간단 순서
1. 트레이더 컨트랙트를 통해서 LP에 유동성을 공급하여 LP토큰을 받는다.
2. LP토큰을 마스터쉐프 컨트랙트에 스테이킹을 시킨다.
3. 마스터쉐프에서는 분배의 규칙에 따라 거버넌스 토큰을 보상(reward)으로 지급한다.
4. 일정 시간이 지난후 트레이더 컨트랙트는 마스터쉐프에서 리워트 토큰을 수확(claim)이 가능하다.
대표적 함수 3가지
add
거버넌스 토큰을 분배 해줄 풀(LP)등록 풀마다 고유의 id(pid) 생성 allocPoint: 해당 풀에 대한 토큰 분배 비율
deposit/ withdraw
LP토큰을 예치/ 인출 거버넌스 토큰도 함께 수확
deposit(pid, 0)
거버넌스 토큰만 수확하고 싶은 경우
interface IMigratorChef {
function migrate(IERC20 token) external returns (IERC20);
}
contract MasterChef is Ownable {
using SafeMath for uint256;
// Info of each user.
struct UserInfo {
uint256 amount; // 어떤 유저가 LP토큰을 얼마나 예치했는가.
uint256 rewardDebt; // 얼만큼 보상을 가져갔는지.
}
// Info of each pool.
struct PoolInfo {
IERC20 lpToken; // 어떤 LP 토큰인지
uint256 allocPoint; // 지분률, 얼마나 분배를 할지 지분률로 나눔
uint256 lastRewardBlock; // Last block number that whales distribution occurs.
uint256 accwhalePerShare; // Accumulated whales per share, times 1e12. See below.
}
// The whale TOKEN!
WhaleToken public whale;
// Dev address.
address public devaddr; // 개발자 주소 보통 리워트 토큰을 분배할때 일정량을 개발자 주소로 추가 민트를 해서 발행을 하기도 한다.
// whale tokens created per block. 리워드 토큰이 얼마나 분배되는지, 발행 속도를 나타냄.
// 블럭당 거버넌스 토큰이 얼마나 분배되는지.
// ex) 1e18(10**18) 1블럭 생성당 1토큰
uint256 public whalePerBlock;
// Bonus muliplier for early whale makers.
uint256 public BONUS_MULTIPLIER = 1;
// The migrator contract. It has a lot of power. Can only be set through governance (owner).
IMigratorChef public migrator;
// Info of each pool.
PoolInfo[] public poolInfo;
// Info of each user that stakes LP tokens.
mapping(uint256 => mapping(address => UserInfo)) public userInfo;
// Total allocation points. Must be the sum of all allocation points in all pools.
uint256 public totalAllocPoint = 0;
// The blcokNumber when whale mining starts.
uint256 public startBlock;
address public factory;
event Deposit(address indexed user, uint256 indexed pid, uint256 amount);
event Withdraw(address indexed user, uint256 indexed pid, uint256 amount);
event EmergencyWithdraw(address indexed user, uint256 indexed pid, uint256 amount);
modifier onlyRole () {
require(msg.sender == owner() || msg.sender == factory, "only Role");
_;
}
constructor(
WhaleToken _whale,
address _factory
) public {
whale = _whale;
devaddr = msg.sender;
whalePerBlock = 1e18;
startBlock = block.number;
factory = _factory;
// staking pool
poolInfo.push(PoolInfo({lpToken: _whale, allocPoint: 1000, lastRewardBlock: startBlock, accwhalePerShare: 0}));
totalAllocPoint = 1000;
}
function updateMultiplier(uint256 multiplierNumber) public onlyOwner {
BONUS_MULTIPLIER = multiplierNumber;
}
function poolLength() public view returns (uint256) {
return poolInfo.length;
}
function getPid(address token) external view returns (uint) {
for (uint i = 0; i<poolLength(); i++) {
PoolInfo memory info = poolInfo[i];
if (address(info.lpToken) == token) return i;
}
}
// Add a new lp to the pool. Can only be called by the owner.
// XXX DO NOT add the same LP token more than once. Rewards will be messed up if you do.
function add( // 마스터쉐프에게 어떤 LP의 리워드 토큰을 얼마나 분배할지의 정보를 추가 가능.
uint256 _allocPoint,
IERC20 _lpToken,
bool _withUpdate
) public onlyRole {
if (_withUpdate) { // 기존에 있는 풀에 정보를 함께 업데이트를 시킬지.
massUpdatePools();
}
uint256 lastRewardBlock = block.number > startBlock ? block.number : startBlock;
totalAllocPoint = totalAllocPoint.add(_allocPoint);
poolInfo.push(
PoolInfo({lpToken: _lpToken, allocPoint: _allocPoint, lastRewardBlock: lastRewardBlock, accwhalePerShare: 0})
);
updateStakingPool();
}
// Deposit LP tokens to MasterChef for whale allocation.
function deposit(uint256 _pid, uint256 _amount) public {
require(_pid != 0, "deposit whale by staking");
PoolInfo storage pool = poolInfo[_pid];
UserInfo storage user = userInfo[_pid][msg.sender];
updatePool(_pid);
if (user.amount > 0) {
uint256 pending = user.amount.mul(pool.accwhalePerShare).div(1e12).sub(user.rewardDebt);
if (pending > 0) {
safewhaleTransfer(msg.sender, pending);
}
}
if (_amount > 0) {
pool.lpToken.transferFrom(address(msg.sender), address(this), _amount);
user.amount = user.amount.add(_amount);
}
user.rewardDebt = user.amount.mul(pool.accwhalePerShare).div(1e12);
emit Deposit(msg.sender, _pid, _amount);
}
}