// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "profile-nft-gamification/contracts/PancakeProfile.sol";
import "./interfaces/IIFOV2.sol";
import "pancake-cake-vault/contracts/IFOPool.sol";
import "pancake-cake-vault/contracts/test/CakeToken.sol";
import "pancake-cake-vault/contracts/test/SyrupBar.sol";
import "pancake-cake-vault/contracts/test/MasterChef.sol";
/**
* @title my cake ifo v3 implement
* @author lrqstudy
* @notice
*/
contract MyIFOInitializableV3 is IIFOV2,ReentrancyGuard,Ownerable {
? ? /**
? ? * 使用了openzeppelin中的safemath痴颊,將其方法用于uint256中轿钠,這個(gè)是之前的版本液肌,現(xiàn)在的新的solidity版本不需要
? ? */
? ? using SafeMath for uint256;
? ? using SafeERC20 for IERC20;
? ? // 定義了一個(gè)池子數(shù)的常量络断,公開的和私人的
? ? uint8 public constant NUMBER_POOLS = 2;
? ? // ifo工廠的地址
? ? address public immutable IFO_FACTORY;
? ? uint256 public MAX_BUFFER_BLOCKS;
? ? //用于參與本次ifo 的資金
? ? IERC20 public lpToken;
? ? //用于公開售賣的token地址
? ? IERC20 public offeringToken;
? ? //pancake平臺信息,只有注冊信息才可以參與ifo
? ? PancakeProfile public pancakeProfile;
? ? IFOPool public ifoPool;
? ? bool public isInitialized;
? ? //ifo 開始區(qū)塊號
? ? uint256 public startBlock;
? ? //ifo 結(jié)束區(qū)塊號
? ? uint256 public endBlock;
? ? //本次ifo的活動(dòng)id
? ? uint256 public campaignId;
? ? //參與達(dá)到要求后有多少積分
? ? uint256 public numberPoints;
? ? //最低積分要求
? ? uint256 public thresholdPoints;
? ? //總共要售賣的token數(shù)量
? ? uint256 public totalTokensOffered;
? ? PoolCharacteristics[NUMBER_POOLS] private _poolInformation;
? ? //定義了一個(gè)mapping結(jié)構(gòu)及老,key是地址豹悬,value是狀態(tài),描述用戶是否已經(jīng)領(lǐng)取積分
? ? mapping(address => bool) private _hasClaimedPoints;
? ? /**
? ? * @dev 定了一個(gè)潛逃的mapping結(jié)構(gòu)分瘦, 第一層key是用戶地址蘸泻,value是一個(gè) key為poolid, value為userinfo的結(jié)構(gòu)mapping結(jié)構(gòu)嘲玫,
? ? 用于記錄當(dāng)前用戶參與ifo的信息悦施,參與了幾個(gè)池子。
? ? */
? ? mapping(address => mapping(uint8 => UserInfo)) private _userInfo;
? ? //定義了一個(gè)mapping去团,key是用戶參與地址抡诞, value是用戶累計(jì)參與的金額。
? ? mapping(address => uint256) public userAccumulateDeposits;
? ? /**
? ? * 定義了一個(gè)池子的結(jié)構(gòu)
? ? * 包括了六個(gè)對象土陪, 募集目標(biāo)金額昼汗,共計(jì)提供多少token,每個(gè)用戶多少險(xiǎn)惡鬼雀,是否有參與費(fèi)用顷窒,總共的募集資金,總共產(chǎn)生多少費(fèi)用
? ? * @param amountLP
? ? * @param amountOfferingToken
? ? */
? ? struct PoolCharacteristics {
? ? ? ? uint256 raisingAmountPool;
? ? ? ? uint256 offeringAmountPool;
? ? ? ? uint256 limitPerUserInLP;
? ? ? ? bool hasTax;
? ? ? ? uint256 totalAmountPool;
? ? ? ? uint256 sumTaxesOverflow;
? ? }
? ? /**
? ? * 定義了一個(gè)userinfo的結(jié)構(gòu)取刃,存儲用戶參與了多少資金蹋肮,是否領(lǐng)取的狀態(tài)
? ? * @param amountLP
? ? * @param amountOfferingToken
? ? */
? ? struct UserInfo {
? ? ? ? uint256 amountPool;
? ? ? ? bool claimedPool;
? ? }
? ? event AdminWithdraw(uint256 amountLP, uint256 amountOfferingToken);
? ? event AdminTokenRecovery(address tokenAddress, uint256 amountTokens);
? ? event Deposit(address indexed user, uint256 amount, uint8 indexed pid);
? ? event Harvest(address indexed user, uint256 offeringAmount,uint256 excessAmount, uint8 indexed pid);
? ? event NewStartAndEndBlocks(uint256 startBlock,uint256 endBlock);
? ? event PointParametersSet(uint256 campaignId, uint256 numberPoints, uint256 thresholdPoints);
? ? event PoolParametersSet(uint256 offeringAmountPool, uint256 raisingAmountPool, uint8 pid);
? ? /**
? ? * 定義了一個(gè)修飾符,合約以及代理合約對象不能參與璧疗,只有普通的eta地址可以參與
? ? */
? ? modifier notContract() {
? ? ? ? require(!_isContract(msg.sender)," contract not allowed");
? ? ? ? require(msg.sender == tx.origin," proxy contract not allowed");
? ? ? ? _;
? ? }
? ? /**
? ? * 定義了一個(gè)構(gòu)造函數(shù)坯辩,指定IFO_Factory對象地址
? ? */
? ? constructor() public {
? ? ? ? IFO_FACTORY = msg.sender;
? ? }
? ? /**
? ? * 定義了一個(gè)初始化函數(shù),用于初始化設(shè)置當(dāng)前ifo事件
? ? * @param _lpToken? 用于參與的資金token地址
? ? * @param _offeringToken? 用于售賣的token地址
? ? * @param _pancakeProfileAddress pancakefile的合約地址
? ? * @param _startBlock 售賣開始的區(qū)塊編號
? ? * @param _endBlock? 售賣結(jié)束的區(qū)塊編號
? ? * @param _maxBufferBlocks 最大緩沖區(qū)塊數(shù)崩侠,用于控制ifo的冗余時(shí)間
? ? * @param _adminAddress? 管理員地址漆魔,用于提取當(dāng)前資金
? ? * @param _ifoPoolAddress? ifo池子地址,用于接收
? ? */
? ? function initialize(
? ? ? ? address _lpToken,
? ? ? ? address _offeringToken,
? ? ? ? address _pancakeProfileAddress,
? ? ? ? uint256 _startBlock,
? ? ? ? uint256 _endBlock,
? ? ? ? uint256 _maxBufferBlocks,
? ? ? ? address _adminAddress,
? ? ? ? address _ifoPoolAddress
? ? )? public? {
? ? ? ? require(!isInitialized,"operations: Already initialized");
? ? ? ? require(msg.sender == IFO_FACTORY, "Operation: only for Factory");
? ? ? ? //make this contract initialized
? ? ? ? isInitialized = true;
? ? ? ? lpToken = IERC20(_lpToken);
? ? ? ? offeringToken = IERC20(_offeringToken);
? ? ? ? /**
? ? ? ? * 這里無法理解却音,為什么 PancakeProfile創(chuàng)建了對象改抡,怎么就一個(gè)參數(shù)就可以呢?
? ? ? ? * PancakeProfile的構(gòu)造函數(shù)有四個(gè)參數(shù)系瓢。
? ? ? ? * @param _amount
? ? ? ? * @param _pid
? ? ? ? */
? ? ? ? pancakeProfile = PancakeProfile(_pancakeProfileAddress);
? ? ? ? ifoPool = IFOPool(_ifoPoolAddress);
? ? ? ? startBlock = _startBlock;
? ? ? ? endBlock = _endBlock;
? ? ? ? MAX_BUFFER_BLOCKS = _maxBufferBlocks;
? ? ? ? transferOwnership(_adminAddress);//這個(gè)將當(dāng)前合約轉(zhuǎn)移給管理員
? ? }
? ? /**
? ? * 用于處理用戶參與的請求函數(shù)
? ? * @param _amount 本次參與金額阿纤,
? ? * @param _pid 本次參與的池子編號
? ? */
? ? function depositPool(uint256 _amount,uint8 _pid) external override nonReentrant notContract {
? ? ? ? //用戶必須要注冊并獲得符合規(guī)定的pancake賬戶及頭像,從而參與ifo
? ? ? ? require(pancakeProfile.getUserStatus(msg.sender),"Deposit: Must have an active profile");
? ? ? ? //當(dāng)前只有兩個(gè)池子夷陋,如果池子id不小于2欠拾,說明參數(shù)有問題
? ? ? ? require(_pid < NUMBER_POOLS," Deposit: Non valid pool id");
? ? ? ? //必須是要先設(shè)定好融資額度胰锌,以及提供的token數(shù)量,用戶才能參與進(jìn)來藐窄。
? ? ? ? require(_poolInformation[_pid].offeringAmount > 0 && _poolInformation[_pid].raisingAmountPool>0,"Deposit: Pool not set");
? ? ? ? //只有到了開始的區(qū)塊號才能參與资昧,用戶不能參與
? ? ? ? require(block.number > startBlock,"Deposit: Too early");
? ? ? ? //過了結(jié)束參與的區(qū)塊號,不能參與
? ? ? ? require(block.number< endBlock,"Deposit: Too Late");
? ? ? ? //如果當(dāng)前ifo合約中的對應(yīng)的售賣余額荆忍,大于已經(jīng)提供的格带,那么說明設(shè)置的ifo 數(shù)據(jù)有問題。
? ? ? ? require(offeringToken.balanceOf(address(this))>= totalTokensOffered,"Deposit: Tokens not deposited properly");
? ? ? ? //用戶可以參與的額度刹枉,用于存入的總金額不得大于該額度
? ? ? ? uint256 ifoCredit = ifoPool.getUserCredit(msg.sender);
? ? ? ? require(userAccumulateDeposits[msg.sender].add(_amount)<= ifoCredit,"Not enough IFO credit left");
? ? ? ? //如果這些校驗(yàn)都通過了叽唱,那么將用于提供的cake數(shù)量調(diào)用轉(zhuǎn)賬函數(shù)轉(zhuǎn)到當(dāng)前合約地址中
? ? ? ? lpToken.safeTransferFrom(address(msg.sender),address(this),_amount);
? ? ? ? //將用戶成功參與到金額以及池子信息設(shè)置到mapping中,方便后續(xù)查看
? ? ? ? _userInfo[msg.sender][_pid].amountPool = _userInfo[msg.sender][_pid].amountPool.add(_amount);
? ? ? ? //如果每個(gè)用戶有參與限額嘶卧,那么用戶的參與金額不得大于這個(gè)限額
? ? ? ? if (_poolInformation[_pid].limitPerUserInLP > 0) {
? ? ? ? ? ? require(_userInfo[msg.sender][_pid].amountPool <= _poolInformation[_pid].limitPerUserInLPimi,"Deposit: new amount over user limt");
? ? ? ? }
? ? ? ? //將用戶本次參與的金額更新到總的參與池子信息中尔觉,方便后期查看
? ? ? ? _poolInformation[_pid].totalAmountPool = _poolInformation[_pid].totalAmountPool.add(_amount);
? ? ? ? //記錄用于累計(jì)參與到ifo金額數(shù)量。
? ? ? ? userAccumulateDeposits[msg.sender] = userAccumulateDeposits[msg.sender].add(_amount);
? ? ? ? //發(fā)出用戶參與ifo的事件芥吟。報(bào)告參與人侦铜,參與金額,參與項(xiàng)目id
? ? ? ? emit Deposit(msg.sender,_amount, _pid);
? ? }
? ? /**
? ? * 定義了用戶在參與結(jié)束后領(lǐng)取ifo的處理請求函數(shù)钟鸵。 ifo結(jié)束后钉稍,用戶點(diǎn)擊claim由當(dāng)前函數(shù)進(jìn)行處理。
? ? * @param _pid 項(xiàng)目id
? ? */
? ? function harvestPool(uint8 _pid) external override nonReentrant notContract{
? ? ? ? //必須結(jié)束ifo才能領(lǐng)取
? ? ? ? require(block.number > endBlock,"Harvest: Too early");
? ? ? ? //項(xiàng)目id必須是0 或者1
? ? ? ? require(_pid < NUMBER_POOLS,"Harvest:Non valid pool id");
? ? ? ? //只有參與過的人才可以領(lǐng)取
? ? ? ? require(_userInfo[msg.sender][_pid].amountPool>0,"Harvest: Did not participate ");
? ? ? ? //如果已經(jīng)領(lǐng)取了棺耍,則不能再次領(lǐng)取
? ? ? ? require(!_userInfo[msg.sender][_pid].claimedPool,"Harvest: already done");
? ? ? ? //調(diào)用領(lǐng)取參與積分的函數(shù)
? ? ? ? _claimPoints(msg.sender);
? ? ? ? //將用戶對應(yīng)的項(xiàng)目id的已經(jīng)領(lǐng)取狀態(tài)設(shè)置為true
? ? ? ? _userInfo[msg.sender][_pid].claimedPool = true;
? ? ? ? //計(jì)算用戶已經(jīng)參與的金額贡未,需要退還的金額,以及用戶參與的費(fèi)用
? ? ? ? (
? ? ? ? ? ? uint256 offeringTokenAmount,
? ? ? ? ? ? uint256 refundingTokenAmount,
? ? ? ? ? ? uint256 userTaxOverflow
? ? ? ? ) = _calculateOfferingAndRefundingAmountPool(msg.sender,_pid);
? ? ? ? if(userTaxOverflow > 0){
? ? ? ? ? ? _poolInformation[_pid].sumtaxedOverflow = _poolInformation[_pid].sumTaxesOverflow.add(userTaxOverflow);
? ? ? ? }
? ? ? ? //將用戶本次獲得的token數(shù)轉(zhuǎn)給當(dāng)前用戶
? ? ? ? if(offeringTokenAmount > 0) {
? ? ? ? ? ? //疑問: 這個(gè)是如何設(shè)置用戶的領(lǐng)取周期的 TODO
? ? ? ? ? ? offeringToken.safeTransfer(address(msg.sender),offeringTokenAmount);
? ? ? ? }
? ? ? ? //將用戶超額部分多cake退還給用戶
? ? ? ? if(refundingTokenAmount>0){
? ? ? ? ? ? lpToken.safeTransfer(address(msg.sender),refundingTokenAmount);
? ? ? ? }
? ? ? ? //調(diào)用通知事件蒙袍,告知領(lǐng)取人俊卤,提供的金額,以及退換的金額害幅。
? ? ? ? emit Harvest(msg.sender, offeringTokenAmount,refundingTokenAmount,_pid);
? ? }
? ? /**
? ? * 供ifo項(xiàng)目方領(lǐng)取募集資金以及剩余的出售token消恍,僅僅當(dāng)初設(shè)置為合約所有人地址才能調(diào)用
? ? * @param _lpAmount 領(lǐng)取募集資金額度
? ? * @param _offerAmount 領(lǐng)取剩余出售token數(shù)量
? ? */
? ? function finalWithdraw(uint256 _lpAmount,uint256 _offerAmount) external override onlyOwner {
? ? ? ? //領(lǐng)取數(shù)必須小于等于當(dāng)前合約中剩余的cake數(shù)
? ? ? ? require(_lpAmount <= lpToken.balanceOf(address(this)),"Operations: Not enough LP tokens");
? ? ? ? //領(lǐng)取的token數(shù)必須小于等于當(dāng)前合約剩余的token數(shù)
? ? ? ? require(_offerAmount <= offeringToken.balanceOf(this),"Operations Not enough offering tokens");
? ? ? ? if(_lpAmount > 0){
? ? ? ? ? ? //當(dāng)前合約中的cake轉(zhuǎn)給項(xiàng)目方
? ? ? ? ? ? lpToken.safeTransfer(address(msg.sender),_lpAmount);
? ? ? ? }
? ? ? ? if(_offerAmount >0){
? ? ? ? ? ? //將當(dāng)前合約中剩余的未出售token轉(zhuǎn)給項(xiàng)目方
? ? ? ? ? ? offeringToken.safeTransfer(address(msg.sender),_offerAmount);
? ? ? ? }
? ? ? ? //發(fā)送管理員領(lǐng)取事件
? ? ? ? emit AdminWithdraw(_lpAmount,_offerAmount);
? ? }
? ? /**
? ? * 給合約所有者恢復(fù)錯(cuò)誤轉(zhuǎn)入的token,且必須是除了cake和當(dāng)前待出售的合約以现。
? ? * @param _tokenAddress
? ? * @param _tokenAmount
? ? */
? ? function recoverWrongTokens(address _tokenAddress,uint256 _tokenAmount) external onlyOwner {
? ? ? ? //要轉(zhuǎn)移的token不能是cake
? ? ? ? require(_tokenAddress != address(lpToken), "can not be LP token");
? ? ? ? //要恢復(fù)的token也不能是項(xiàng)目方的出售token
? ? ? ? require(_tokenAddress != address(offeringToken),"recover: cannot be offering token");
? ? ? ? //將當(dāng)前合約對象強(qiáng)制轉(zhuǎn)換為ERC20對象狠怨,從而能調(diào)用erc20對象的safeTransfer方法,將當(dāng)前合約中的當(dāng)前token轉(zhuǎn)給項(xiàng)目方
? ? ? ? IERC20(_tokenAddress).safeTransfer(address(msg.sender),_tokenAmount);
? ? ? ? //發(fā)布管理員恢復(fù)誤操作token
? ? ? ? emit AdminTokenRecovery(_tokenAddress,_tokenAmount);
? ? }
? ? /**
? ? * 定義了一個(gè)設(shè)置啟動(dòng)參數(shù)的功能邑遏,給管理員調(diào)用
? ? * @param _offeringAmountPool 待出售token數(shù)量
? ? * @param _raisingAmountPool? 共計(jì)募集資金
? ? * @param _limitPerUserInLP? 每個(gè)參與人參與限額
? ? * @param _hasTax? 是否有費(fèi)用
? ? * @param _pid? 池子id
? ? */
? ? function setPool(
? ? ? ? uint256 _offeringAmountPool,
? ? ? ? uint256 _raisingAmountPool,
? ? ? ? uint256 _limitPerUserInLP,
? ? ? ? bool _hasTax,
? ? ? ? uint8 _pid
? ? ? ? ) external override onlyOwner {
? ? ? ? ? ? //只有開始的區(qū)塊之前才可以調(diào)用佣赖,在開始后不允許調(diào)用。
? ? ? ? ? ? require(block.number < startBlock," Operations: IFO has started");
? ? ? ? ? ? //池子id必須是指定的幾個(gè)记盒。
? ? ? ? ? ? require(_pid < NUMBER_POOLS," Operations: Pool does not exist");
? ? ? ? ? ? //將傳遞的參數(shù)賦值給poolInformation狀態(tài)變量
? ? ? ? ? ? _poolInformation[_pid].offeringAmountPool = _offeringAmountPool;
? ? ? ? ? ? _poolInformation[_pid].raisingAmountPool = _raisingAmountPool;
? ? ? ? ? ? _poolInformation[_pid].limitPerUserInLP = _limitPerUserInLP;
? ? ? ? ? ? _poolInformation[_pid].hasTax = _hasTax;
? ? ? ? ? ? uint256 tokensDistributedAcrossPools;
? ? ? ? ? ? for(uint8 i =0; i< NUMBER_POOLS;i++){
? ? ? ? ? ? ? ? // 統(tǒng)計(jì)兩個(gè)池子共提供的token數(shù)
? ? ? ? ? ? ? ? tokensDistributedAcrossPools = tokensDistributedAcrossPools.add(_poolInformation[i].offeringAmountPool);
? ? ? ? ? ? }
? ? ? ? ? ? totalTokensOffered = tokensDistributedAcrossPools;
? ? ? ? ? ? //發(fā)送池子參數(shù)設(shè)定的事件
? ? ? ? ? ? emit PoolParametersSet(_offeringAmountPool,_raisingAmountPool,_pid);
? ? }
? ? /**
? ? * 更新積分參數(shù)憎蛤,只能在區(qū)塊結(jié)束之前調(diào)用
? ? * @param _campaignId ifo活動(dòng)id
? ? * @param _numberPoints 積分?jǐn)?shù)
? ? * @param _thresholdPoints 積分門檻
? ? */
? ? function updatePointParameters(
? ? ? ? uint256 _campaignId,
? ? ? ? uint256 _numberPoints,
? ? ? ? uint256 _thresholdPoints
? ? ? ? ) external override onlyOwner {
? ? ? ? ? ? require(block.number < endBlock,"operation IFO has ended");
? ? ? ? ? ? numberPoints = _numberPoints;
? ? ? ? ? ? campaignId = _campaignId;
? ? ? ? ? ? thresholdPoints = _thresholdPoints;
? ? ? ? ? ? // 發(fā)布積分更新時(shí)間
? ? ? ? ? ? emit PointParametersSet(campaignId,numberPoints,thresholdPoints);
? ? }
? ? /**
? ? * 更新開始結(jié)束區(qū)塊號,從而重新設(shè)定開始結(jié)束時(shí)間纪吮。
? ? * @param _startBlock 新的開始區(qū)塊
? ? * @param _endBlock? 新的結(jié)束區(qū)塊
? ? */
? ? function updateStartAndEndBlocks(uint256 _startBlock,uint256 _endBlock) external onlyOwner {
? ? ? ? //新的結(jié)束區(qū)塊只能在當(dāng)前區(qū)塊延后 的MAX_BUFFER_BLOCKS 中
? ? ? ? require(_endBlock < (block.number + MAX_BUFFER_BLOCKS), "Operations: EndBlock too far");
? ? ? ? //當(dāng)前操作必須要在開始之前操作
? ? ? ? require(block.number < startBlock,"Operations: IFO has started");
? ? ? ? //新設(shè)置的結(jié)束區(qū)塊必須要大于開始區(qū)塊
? ? ? ? require(_startBlock < _endBlock, "Operations: New startBlock must be lower than new endBlock");
? ? ? ? //新設(shè)置的區(qū)塊必須要在當(dāng)前系統(tǒng)區(qū)塊之后俩檬。
? ? ? ? require(block.number < _startBlock,"Operations: New startblock must be higher than current block");
? ? ? ? startBlock = _startBlock;
? ? ? ? endBlock = _endBlock;
? ? ? ? //發(fā)布新開始結(jié)束時(shí)間區(qū)塊事件
? ? ? ? emit NewStartAndEndBlocks(_startBlock,_endBlock);
? ? }
? ? /**
? ? * 定義一個(gè)訪問ifo池子的方法栏豺。
? ? * @param _pid
? ? * @return
? ? * @return
? ? * @return
? ? * @return
? ? * @return
? ? * @return
? ? */
? ? function viewPoolInformation(uint256 _pid) external view override returns (
? ? ? ? uint256,
? ? ? ? uint256,
? ? ? ? uint256,
? ? ? ? bool,
? ? ? ? uint256,
? ? ? ? uint256
? ? ? ? ){
? ? ? ? ? ? return (
? ? ? ? ? ? ? ? _poolInformation[_pid].raisingAmountPool,
? ? ? ? ? ? ? ? _poolInformation[_pid].offeringAmountPool,
? ? ? ? ? ? ? ? _poolInformation[_pid].limitPerUserInLP,
? ? ? ? ? ? ? ? _poolInformation[_pid].hasTax,
? ? ? ? ? ? ? ? _poolInformation[_pid].totalAmountPool,
? ? ? ? ? ? ? ? _poolInformation[_pid].sumTaxesOverflow
? ? ? ? ? ? );
? ? }
? ? /**
? ? * 定義一個(gè)通過池子id獲得當(dāng)前池子溢出稅,如果沒有稅豆胸,則返回0,如果有稅巷疼,則返回計(jì)算稅的結(jié)果
? ? * @param _pid
? ? */
? ? function viewPoolTaxRateOverflow(uint256 _pid) external view override returns (uint256){
? ? ? ? if(!_poolInformation[_pid].hasTax){
? ? ? ? ? ? return 0;
? ? ? ? }
? ? ? ? return _calculateTaxOverflow(_poolInformation[_pid].totalAmountPool,_poolInformation[_pid].raisingAmountPool);
? ? }
? ? /**
? ? *
? ? * @param _user
? ? * @param _pids
? ? */
? ? function viewUserAllocationPools(address _user,uint8[] calldata _pids) external view override returns (uint256[] memory){
? ? ? ? uint256[] memory allocationPools = new uint256[](_pids.length);
? ? ? ? for(uint8 i=0; i< _pids.length;i++){
? ? ? ? ? ? allocationPools[i] = _getUserAllocationPool(_user,_pids[i]);
? ? ? ? }
? ? ? ? return allocationPools;
? ? }
? ? /**
? ? * 定義了一個(gè)訪問用戶數(shù)據(jù)的方法晚胡。 返回值有兩個(gè)數(shù)組。
? ? * 第一個(gè)是用戶參與每個(gè)池子對應(yīng)的金額數(shù)嚼沿,第二個(gè)數(shù)組是每個(gè)用戶是否領(lǐng)取了對應(yīng)的token
? ? * @param _user
? ? * @param _pids
? ? * @return
? ? * @return
? ? */
? ? function viewUserInfo(address _user, uint8[] calldata _pids) external view override returns(uint256[] memory, bool[] memory){
? ? ? ? uint256[] memory amountPools = new uint256[](_pids.length);
? ? ? ? bool[] memory statusPools = new bool[](_pids.length);
? ? ? ? for(uint8 i = 0; i< NUMBER_POOLS; i++){
? ? ? ? ? ? amountPools[i] = _userInfo[_user][i].amountPool;
? ? ? ? ? ? statusPools[i] = _userInfo[_user][i].claimedPool;
? ? ? ? }
? ? ? ? return (amountPools,statusPools);
? ? }
? ? /**
? ? * 獲取并返回用戶在每個(gè)池子中參與金額以及退款的金額數(shù)
? ? */
? ? function viewUserOfferingAndRefundingAmountsForPools(address _user,uint8[] calldata _pids) external
? ? view override returns (uint256[3][]memory){
? ? ? ? uint256[3][] memory amountPools = new uint256[3][](_pids.length);
? ? ? ? for(uint8 i =0; i<_pids.length;i++){
? ? ? ? ? ? uint256 userOfferingAmountPool;
? ? ? ? ? ? uint256 userRefundingAmountPool;
? ? ? ? ? ? uint256 userTaxAmountPool;
? ? ? ? ? ? if(_poolInformation[_pids[i]].raisingAmountPool > 0){
? ? ? ? ? ? ? ? (
? ? ? ? ? ? ? ? ? ? userOfferingAmount,
? ? ? ? ? ? ? ? ? ? userRefundingAmountPool,
? ? ? ? ? ? ? ? ? ? userTaxAmountPool
? ? ? ? ? ? ? ? ) = _calculateOfferingAndRefundingAmountsPool(_user,_pids[i]);
? ? ? ? ? ? }
? ? ? ? ? ? amountPools[i] = [userOfferingAmountPool,userRefundingAmountPool,userTaxAmountPool];
? ? ? ? }
? ? ? ? return amountPools;
? ? }
? ? /**
? ? * @notice It calculates the tax overflow given the raisingAmountPool and the totalAmountPool.
? ? * @dev 100,000,000,000 means 0.1 (10%) / 1 means 0.0000000000001 (0.0000001%) / 1,000,000,000,000 means 1 (100%)
? ? * @return It returns the tax percentage
? ? */
? ? function _calculateTaxOverflow(uint256 _totalAmountPool, uint256 _raisingAmountPool)
? ? ? ? internal
? ? ? ? pure
? ? ? ? returns (uint256)
? ? {
? ? ? ? uint256 ratioOverflow = _totalAmountPool.div(_raisingAmountPool);
? ? ? ? if (ratioOverflow >= 1500) {
? ? ? ? ? ? return 500000000; // 0.05%
? ? ? ? } else if (ratioOverflow >= 1000) {
? ? ? ? ? ? return 1000000000; // 0.1%
? ? ? ? } else if (ratioOverflow >= 500) {
? ? ? ? ? ? return 2000000000; // 0.2%
? ? ? ? } else if (ratioOverflow >= 250) {
? ? ? ? ? ? return 2500000000; // 0.25%
? ? ? ? } else if (ratioOverflow >= 100) {
? ? ? ? ? ? return 3000000000; // 0.3%
? ? ? ? } else if (ratioOverflow >= 50) {
? ? ? ? ? ? return 5000000000; // 0.5%
? ? ? ? } else {
? ? ? ? ? ? return 10000000000; // 1%
? ? ? ? }
? ? }
? ? /**
? ? * @notice It calculates the offering amount for a user and the number of LP tokens to transfer back.
? ? * @param _user: user address
? ? * @param _pid: pool id
? ? * @return {uint256, uint256, uint256} It returns the offering amount, the refunding amount (in LP tokens),
? ? * and the tax (if any, else 0)
? ? */
? ? function _calculateOfferingAndRefundingAmountsPool(address _user, uint8 _pid)
? ? ? ? internal
? ? ? ? view
? ? ? ? returns (
? ? ? ? ? ? uint256,
? ? ? ? ? ? uint256,
? ? ? ? ? ? uint256
? ? ? ? )
? ? {
? ? ? ? uint256 userOfferingAmount;
? ? ? ? uint256 userRefundingAmount;
? ? ? ? uint256 taxAmount;
? ? ? ? if (_poolInformation[_pid].totalAmountPool > _poolInformation[_pid].raisingAmountPool) {
? ? ? ? ? ? // Calculate allocation for the user
? ? ? ? ? ? uint256 allocation = _getUserAllocationPool(_user, _pid);
? ? ? ? ? ? // Calculate the offering amount for the user based on the offeringAmount for the pool
? ? ? ? ? ? userOfferingAmount = _poolInformation[_pid].offeringAmountPool.mul(allocation).div(1e12);
? ? ? ? ? ? // Calculate the payAmount
? ? ? ? ? ? uint256 payAmount = _poolInformation[_pid].raisingAmountPool.mul(allocation).div(1e12);
? ? ? ? ? ? // Calculate the pre-tax refunding amount
? ? ? ? ? ? userRefundingAmount = _userInfo[_user][_pid].amountPool.sub(payAmount);
? ? ? ? ? ? // Retrieve the tax rate
? ? ? ? ? ? if (_poolInformation[_pid].hasTax) {
? ? ? ? ? ? ? ? uint256 taxOverflow = _calculateTaxOverflow(
? ? ? ? ? ? ? ? ? ? _poolInformation[_pid].totalAmountPool,
? ? ? ? ? ? ? ? ? ? _poolInformation[_pid].raisingAmountPool
? ? ? ? ? ? ? ? );
? ? ? ? ? ? ? ? // Calculate the final taxAmount
? ? ? ? ? ? ? ? taxAmount = userRefundingAmount.mul(taxOverflow).div(1e12);
? ? ? ? ? ? ? ? // Adjust the refunding amount
? ? ? ? ? ? ? ? userRefundingAmount = userRefundingAmount.sub(taxAmount);
? ? ? ? ? ? }
? ? ? ? } else {
? ? ? ? ? ? userRefundingAmount = 0;
? ? ? ? ? ? taxAmount = 0;
? ? ? ? ? ? // _userInfo[_user] / (raisingAmount / offeringAmount)
? ? ? ? ? ? userOfferingAmount = _userInfo[_user][_pid].amountPool.mul(_poolInformation[_pid].offeringAmountPool).div(
? ? ? ? ? ? ? ? _poolInformation[_pid].raisingAmountPool
? ? ? ? ? ? );
? ? ? ? }
? ? ? ? return (userOfferingAmount, userRefundingAmount, taxAmount);
? ? }
? ? /**
? ? * 定義了一個(gè)供內(nèi)部調(diào)用的領(lǐng)取積分函數(shù)
? ? * @param _user
? ? */
? ? function _claimPoints(address _user) internal? {
? ? ? ? if(_hasClaimedPoints[_user]){
? ? ? ? ? ? return;//如果當(dāng)前用戶已經(jīng)領(lǐng)取積分估盘,那么不執(zhí)行后續(xù)命令? 疑問骡尽,為什么這里不報(bào)錯(cuò)呢遣妥,而僅僅是返回。
? ? ? ? }
? ? ? ? uint256 sumPools;
? ? ? ? for(uint8 i=0; i<NUMBER_POOLS;i++){
? ? ? ? ? ? sumPools = sumPools.add(_userInfo[msg.sender][i].amountPool);//統(tǒng)計(jì)用戶參與的總的金額數(shù)
? ? ? ? }
? ? ? ? //如果用戶參與的總金額數(shù)攀细,超過了限額箫踩,則滿足領(lǐng)取積分條件
? ? ? ? if(sumPools > thresholdPoints) {
? ? ? ? ? ? _hasClaimedPoints = true;//將已經(jīng)領(lǐng)取積分的狀態(tài)設(shè)置為true
? ? ? ? ? ? pancakeProfile.increaseUserPoints(msg.sender, numberPoints,campaignId);//調(diào)用pancakeProfile合約執(zhí)行給用戶增加積分的函數(shù)功能。
? ? ? ? }
? ? }
? ? /**
? ? * 獲取用戶參與量占當(dāng)前總量的比例
? ? * @param _user
? ? * @param _pid
? ? */
? ? function _getUserAllocationPool(address _user,uint8 _pid) internal view returns(uint256) {
? ? ? ? if(_poolInformation[_pid].totalAmountPool > 0) {
? ? ? ? ? ? return _userInfo[_user][_pid].amountPool.mul(1e18).div(_poolInformation[_pid]).totalAmountPool.mul(1e6);
? ? ? ? }
? ? ? ? return 0;
? ? }
? ? /**
? ? * 定義一個(gè)判斷是否是合約地址的內(nèi)部方法
? ? * @param _addr
? ? */
? ? function _isContract(address _addr) internal? view returns (bool){
? ? ? ? uint256 size;
? ? ? ? assembly {
? ? ? ? ? ? size := extcodesize(_addr)
? ? ? ? }
? ? ? ? return size >0;
? ? }
}