Open1
KGF
初心者がKGFのコントラクトを読んだメモ
NFT Contract
mintNFTs
- SaleContract以外からの呼び出しを拒否
- MAX_SUPPLY以下だったらmintするよ
- MAX_SUPPLYはimmutable変数で、コンストラクタで値を設定
function mintNFTs(address to, uint256[] memory tokenIds) public virtual {
require(msg.sender == saleContract, "Nice try lol");
uint256 length = tokenIds.length;
for (uint256 i; i < length; ++i) {
require(tokenIds[i] != 0 && tokenIds[i] <= MAX_SUPPLY, "ID > MAX_SUPPLY");
}
_mintNFTs(to, tokenIds);
}
prepareSale
- コントラクトオーナーが一回だけSaleContractを変更できる
function prepareSale(address _saleContract) public onlyOwner {
require(saleContract == address(0), "Immutable");
saleContract = _saleContract;
}
SaleContract
変数
- アドレスごとの購入数を管理する変数
- 現状分かっている管理情報は、WaveごとtokenIdと購入数
- 正確には違うかもだけど、イメージ的には最初の32ビット→Wave1の購入情報、次の32ビット→Wave2の購入情報みたいな感じ。32ビット×6Wave = 192ビット
- 最後から64ビット目からはmintedBalance。最後の32ビットはbalance
// Purchases are compressed into a single uint256, after 6 rounds the limit is simply removed anyways.
// The last uint32 slot is reserved for their balance. (left-most bytes first)
mapping(address => uint256) purchases;
buyKGF
- ちゃんと在庫があるか確認してる
- amoutForSaleは7717が指定されてる。amoutSoldについては後述。
function buyKGF(uint256 count) external payable nonReentrant {
uint256 _amountSold = amountSold;
uint256 _amountForSale = amountForSale; // 7717
uint256 remaining = _amountForSale - _amountSold;
require(remaining != 0, "Sold out! Sorry!");
- 販売開始時間がどうかチェック
- EOAから直接このコントラクトをコールしているかチェック。めちゃ参考になる記事
- mintする個数が0より大きいかチェック
require(block.timestamp >= startTime, "Sale has not started");
require(tx.origin == msg.sender, "Only direct calls pls");
require(count > 0, "Cannot mint 0");
- KGFのpublic saleでは時間経過ごとによってWaveが進み、それによってmint数が変更される仕組みが存在した。それ関連の実装。
- ちゃんとETHを送ってるかチェック
uint256 wave = currentWave();
require(count <= maxPerTX(wave), "Max for TX in this wave");
require(wave < MAX_WAVES, "Not in main sale");
require(msg.value == count * buyPrice, "Not enough ETH");
- 3個mintしようとしたけど在庫が2個しかなかった場合、2個の購入に切り替える処理。
// Adjust for the last mint being incomplete.
uint256 ethAmountOwed;
if (count > remaining) {
ethAmountOwed = buyPrice * (count - remaining);
count = remaining;
}
- Wave内では、1アドレスにつき1回しか購入できないよう制限
uint256 purchaseInfo = purchases[msg.sender];
require(!hasDoneWave(purchaseInfo, wave), "Already purchased this wave");
- startSupply: amountSold + devSupply + 1
- amountSold: 売れた数(正確にはトランザクションはまだ)
- _createNewPurchaseInfo()については後述
uint256 startSupply = currentMintIndex();
amountSold = _amountSold + count;
purchases[msg.sender] = _createNewPurchaseInfo(purchaseInfo, wave, startSupply, count);
- クライアント側へevent通知
- 上の"3個mintしようとしたけど在庫が2個しかなかった場合"に返金する処理
emit Reserved(msg.sender, count);
if (ethAmountOwed > 0) {
sendValue(payable(msg.sender), ethAmountOwed);
}
}
- 変数purchaseで購入するtokenIdと購入数を管理する
- 具体的には111101 00000001のような感じで、前半ビットがtokenId, 残りが購入数
- 上の算出方法は、startingSupplyを二進数に変換して、8ビット分左シフト
- 購入数の部分は|=で増やしてる
function _createNewPurchaseInfo(uint256 purchaseInfo, uint256 wave, uint256 _startingSupply, uint256 count) internal pure returns (uint256) {
require(wave < MAX_WAVES, "Not a wave index");
uint256 purchase = _startingSupply<<8;
purchase |= count;
uint256 newWaveSlot = _writeWaveSlot(purchaseInfo, wave, purchase);
uint256 newBalance = _getBalance(purchaseInfo) + count;
return _writeDataSlot(newWaveSlot, BALANCE_SLOT_INDEX, newBalance);
}
- Waveごとの購入情報を書き込む
function _writeWaveSlot(uint256 purchase, uint256 index, uint256 data) internal pure returns (uint256) {
require(index < MAX_WAVES, "not valid index");
uint256 writeIndex = 256 - ((index+1) * 32);
require(uint32(purchase<<writeIndex) == 0, "Cannot write in wave slot twice");
uint256 newSlot = data<<writeIndex;
uint256 newPurchase = purchase | newSlot;
return newPurchase;
}