ETH源码学习(2)GetBalance
从这开始读,因为最简单。还能对重要的东西有所理解。如果是单节点debug,重启链之后余额会归零,所以最少要两个节点,同步区块就没这个问题了,具体原因还未知。 声明:eth版本:Geth/v1.10.7-unstable/darwin-arm64/go1.17.5,我对比了网上的一些文章,这块代码有改动,主要以下改变从BlockChain的snap中读account几乎放弃从trie中获取account,判断极为严苛,我想不到有什么情况能进入,应该是放弃了这层分析入口//命令 eth.getBalance('0x0d7dd6dbabee2ec9b325aa7aa8b42d75068e8597') //入口 func (s *PublicBlockChainAPI) GetBalance(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Big, error) { //获取statedb state, _, err := s.b.StateAndHea...
Ether.js+Web3modal基础使用
1.说明现在网站会提供很多种钱包,web3modal可以提供统一的provider,不需要你操心太多东西用ether.js而不是web3.js的原因是简单,爽2.安装npm i web3modal npm i ethers //另外还需要安装对应wallet的包,自行搜索就行 3.连接钱包// MM默认就有,无需显式加入 const providerOptions = { walletconnect: { package: walletconnectProvider, options: { infuraId: "", }, }, }; //构建Web3Modal对象 const web3Modal = new Web3Modal({ //缓存provider cacheProvider: true, providerOptions, }); //连接wallet async function connect() { try { const web3ModalProvider = await web3Modal.connect(); provider = new ethers.pr...
ETH源码学习(1)创建私有链
下载geth,创建genesis.json{ "config": { "chainId": 8888, //自行修改 "homesteadBlock": 0, "daoForkBlock": 0, "daoForkSupport": true, "eip150Block": 0, "eip155Block": 0, "eip158Block": 0, "byzantiumBlock": 0, "constantinopleBlock": 0, "petersburgBlock": 0, "ethash": {} }, "nonce": "0x42", "timestamp": "0x0", "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", "gasLimit": "0xffffffff", "difficulty": "0x700000",//难度,越大挖矿越慢 "alloc": { "093f59f1d91017d30d8c2caa78feb5beb0d2cfaf...
ETH源码学习(2)GetBalance
从这开始读,因为最简单。还能对重要的东西有所理解。如果是单节点debug,重启链之后余额会归零,所以最少要两个节点,同步区块就没这个问题了,具体原因还未知。 声明:eth版本:Geth/v1.10.7-unstable/darwin-arm64/go1.17.5,我对比了网上的一些文章,这块代码有改动,主要以下改变从BlockChain的snap中读account几乎放弃从trie中获取account,判断极为严苛,我想不到有什么情况能进入,应该是放弃了这层分析入口//命令 eth.getBalance('0x0d7dd6dbabee2ec9b325aa7aa8b42d75068e8597') //入口 func (s *PublicBlockChainAPI) GetBalance(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Big, error) { //获取statedb state, _, err := s.b.StateAndHea...
Ether.js+Web3modal基础使用
1.说明现在网站会提供很多种钱包,web3modal可以提供统一的provider,不需要你操心太多东西用ether.js而不是web3.js的原因是简单,爽2.安装npm i web3modal npm i ethers //另外还需要安装对应wallet的包,自行搜索就行 3.连接钱包// MM默认就有,无需显式加入 const providerOptions = { walletconnect: { package: walletconnectProvider, options: { infuraId: "", }, }, }; //构建Web3Modal对象 const web3Modal = new Web3Modal({ //缓存provider cacheProvider: true, providerOptions, }); //连接wallet async function connect() { try { const web3ModalProvider = await web3Modal.connect(); provider = new ethers.pr...
ETH源码学习(1)创建私有链
下载geth,创建genesis.json{ "config": { "chainId": 8888, //自行修改 "homesteadBlock": 0, "daoForkBlock": 0, "daoForkSupport": true, "eip150Block": 0, "eip155Block": 0, "eip158Block": 0, "byzantiumBlock": 0, "constantinopleBlock": 0, "petersburgBlock": 0, "ethash": {} }, "nonce": "0x42", "timestamp": "0x0", "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", "gasLimit": "0xffffffff", "difficulty": "0x700000",//难度,越大挖矿越慢 "alloc": { "093f59f1d91017d30d8c2caa78feb5beb0d2cfaf...
Subscribe to point
Subscribe to point
Share Dialog
Share Dialog
<100 subscribers
<100 subscribers
纯属个人收集整理资料+揣测理解,我也不是NFT玩家,也没这个资金实力玩NFT。所以,理解上没有专业人士准确。
固定305APE价格售卖
售卖分为kyc pre-mint(Contributor才行,应该就是白名单吧)、公售(大家抢)、持有无聊猿和变异猿的人可以再mint
5.1买的是盲盒,5.2拆盒

operator也是操作了这些事情,业务理解应该没太大问题
删掉了一些业务无关、重复、无需理解的代码
代码还算简单,只有通过随机数将土地和nft721的metadata关联起来有点疑问
通过链上操作可以知道tokenurl是在土拍前就设置了,并且再也没有改过,所以不是其他项目那样卖完后通过改url拆盒
项目方也上传了metadatahashes,所以所有nft metadata是事先就准备好了的
链上的随机数并没有参与链上业务的处理,比如通过随机数+tokenid获取metadata url
既然链上的方法都没有使用,那么这个关联操作应该是放在了链下,
api.otherside.xyz/lands/27893 访问这个地址后,通过他们未开源的算法,将27893+随机数指向了metaurl。但是这样是不是就能够通过修改算法,改变用户的nft了。。。。。。。这点没有想明白,如果有好心人愿意指导下我,可以zn978740431@gmail.com给我发邮件。
网上找到两个还算靠谱的实现文章,看起来都是在链上完成这个操作的,搞不明白ape是怎么想的。因为很多nft项目方在这上面做手脚,还是很想理解透这个事情。
https://vocus.cc/article/6212f73ffd89780001687585
https://zombit.info/write-to-the-nft-project/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
contract Land {
using SafeERC20 for IERC20;
// attributes
string private baseURI;

address public operator;
//公售开关
bool public publicSaleActive;
//公售时间
uint256 public publicSaleStartTime;
//荷兰拍用的
uint256 public publicSalePriceLoweringDuration;
uint256 public publicSaleStartPrice;
uint256 public publicSaleEndingPrice;
//荷兰拍用的
//记录当前的土地数量
uint256 public currentNumLandsMintedPublicSale;
//当前mint tokenId
uint256 public mintIndexPublicSaleAndContributors;
//ape地址,用于交钱
address public tokenContract;
//是否需要验证kyc
bool private isKycCheckRequired;
//验证kyc的hash root, Merkle tree验证
bytes32 public kycMerkleRoot;

//每次最大可以mint的上限
uint256 public maxMintPerTx;
//每个地址总共mint的上限
uint256 public maxMintPerAddress;
//每个地址mint的数量
mapping(address => uint256) public mintedPerAddress;
//无聊猿和变异猿是否可以额外mint
bool public claimableActive;
bool public adminClaimStarted;
//无聊猿mint的信息
address public alphaContract;
mapping(uint256 => bool) public alphaClaimed;
uint256 public alphaClaimedAmount;
//变异mint的信息
address public betaContract;
mapping(uint256 => bool) public betaClaimed;
uint256 public betaClaimedAmount;
uint256 public betaNftIdCurrent;
//贡献者mint的信息
bool public contributorsClaimActive;
mapping(address => uint256) public contributors;
uint256 public futureLandsNftIdCurrent;
//未来还可以再mint
address public futureMinter;
//保证官方没有作弊,用于所有人拿这里面的数据进行验证
Metadata[] public metadataHashes;
//随机数seed, 线上结果 0xaa77729d3466ca35ae8d28b3bbac7cc36a5031efdc430821c02bc31a238af445
bytes32 public keyHash;
//请求随机数的费用
uint256 public fee;
//下面三个获得的随机数然后计算出来的偏移量 线上结果 35117
uint256 public publicSaleAndContributorsOffset;
//线上结果 5491
uint256 public alphaOffset;
//线上结果 15491
uint256 public betaOffset;
//是否请求过随机数
mapping(bytes32 => bool) public isRandomRequestForPublicSaleAndContributors;
bool public publicSaleAndContributorsRandomnessRequested;
bool public ownerClaimRandomnessRequested;
// constants
uint256 immutable public MAX_LANDS;
uint256 immutable public MAX_LANDS_WITH_FUTURE;
uint256 immutable public MAX_ALPHA_NFT_AMOUNT;
uint256 immutable public MAX_BETA_NFT_AMOUNT;
uint256 immutable public MAX_PUBLIC_SALE_AMOUNT;
uint256 immutable public RESERVED_CONTRIBUTORS_AMOUNT;
uint256 immutable public MAX_FUTURE_LANDS;
uint256 constant public MAX_MINT_PER_BLOCK = 150;
// structs
struct LandAmount {
uint256 alpha;
uint256 beta;
uint256 publicSale;
uint256 future;
}
struct ContributorAmount {
address contributor;
uint256 amount;
}
struct Metadata {
bytes32 metadataHash;
bytes32 shuffledArrayHash;
uint256 startIndex;
uint256 endIndex;
}

struct ContractAddresses {
address alphaContract;
address betaContract;
address tokenContract;
}
// modifiers
。。。。。。。。。。
// events
。。。。。。。。。。
constructor(string memory name, string memory symbol,
ContractAddresses memory addresses,
LandAmount memory amount,
ContributorAmount[] memory _contributors,
address _vrfCoordinator, address _linkTokenAddress,
bytes32 _vrfKeyHash, uint256 _vrfFee,
address _operator
) ERC721(name, symbol) VRFConsumerBase(_vrfCoordinator, _linkTokenAddress) {
//无聊猿和变异猿的地址,持有这个的能够mint(具体的业务忘记了)
alphaContract = addresses.alphaContract;
betaContract = addresses.betaContract;
tokenContract = addresses.tokenContract;
MAX_ALPHA_NFT_AMOUNT = amount.alpha;
MAX_BETA_NFT_AMOUNT = amount.beta;
MAX_PUBLIC_SALE_AMOUNT = amount.publicSale;
MAX_FUTURE_LANDS = amount.future;
betaNftIdCurrent = amount.alpha; //beta starts after alpha
mintIndexPublicSaleAndContributors = amount.alpha + amount.beta; //public sale starts after beta
//贡献者mint数量
uint256 tempSum;
for(uint256 i; i<_contributors.length; ++i){
contributors[_contributors[i].contributor] = _contributors[i].amount;
tempSum += _contributors[i].amount;
}
RESERVED_CONTRIBUTORS_AMOUNT = tempSum;
//总数量
MAX_LANDS = amount.alpha + amount.beta + amount.publicSale + RESERVED_CONTRIBUTORS_AMOUNT;
//以后还可以mint的数量
MAX_LANDS_WITH_FUTURE = MAX_LANDS + amount.future;
futureLandsNftIdCurrent = MAX_LANDS; //future starts after public sale
//用于随机数请求
keyHash = _vrfKeyHash;
fee = _vrfFee;
//操作者地址
operator = _operator;
}
//一些set函数
。。。。。。。。。。
// Public Sale Methods
//公售
function startPublicSale(
uint256 _publicSalePriceLoweringDuration,
uint256 _publicSaleStartPrice,
uint256 _publicSaleEndingPrice,
uint256 _maxMintPerTx,
uint256 _maxMintPerAddress,
bool _isKycCheckRequired
) external onlyOperator {
require(!publicSaleActive, "Public sale has already begun");
//不用管,荷兰拍取消了
publicSalePriceLoweringDuration = _publicSalePriceLoweringDuration;
publicSaleStartPrice = _publicSaleStartPrice;
publicSaleEndingPrice = _publicSaleEndingPrice;
publicSaleStartTime = block.timestamp;
publicSaleActive = true;
//每次,每个地址的mint限制
maxMintPerTx = _maxMintPerTx;
maxMintPerAddress = _maxMintPerAddress;
isKycCheckRequired = _isKycCheckRequired;
emit LandPublicSaleStart(publicSalePriceLoweringDuration, publicSaleStartTime);
}

纯属个人收集整理资料+揣测理解,我也不是NFT玩家,也没这个资金实力玩NFT。所以,理解上没有专业人士准确。
固定305APE价格售卖
售卖分为kyc pre-mint(Contributor才行,应该就是白名单吧)、公售(大家抢)、持有无聊猿和变异猿的人可以再mint
5.1买的是盲盒,5.2拆盒

operator也是操作了这些事情,业务理解应该没太大问题
删掉了一些业务无关、重复、无需理解的代码
代码还算简单,只有通过随机数将土地和nft721的metadata关联起来有点疑问
通过链上操作可以知道tokenurl是在土拍前就设置了,并且再也没有改过,所以不是其他项目那样卖完后通过改url拆盒
项目方也上传了metadatahashes,所以所有nft metadata是事先就准备好了的
链上的随机数并没有参与链上业务的处理,比如通过随机数+tokenid获取metadata url
既然链上的方法都没有使用,那么这个关联操作应该是放在了链下,
api.otherside.xyz/lands/27893 访问这个地址后,通过他们未开源的算法,将27893+随机数指向了metaurl。但是这样是不是就能够通过修改算法,改变用户的nft了。。。。。。。这点没有想明白,如果有好心人愿意指导下我,可以zn978740431@gmail.com给我发邮件。
网上找到两个还算靠谱的实现文章,看起来都是在链上完成这个操作的,搞不明白ape是怎么想的。因为很多nft项目方在这上面做手脚,还是很想理解透这个事情。
https://vocus.cc/article/6212f73ffd89780001687585
https://zombit.info/write-to-the-nft-project/
// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
contract Land {
using SafeERC20 for IERC20;
// attributes
string private baseURI;

address public operator;
//公售开关
bool public publicSaleActive;
//公售时间
uint256 public publicSaleStartTime;
//荷兰拍用的
uint256 public publicSalePriceLoweringDuration;
uint256 public publicSaleStartPrice;
uint256 public publicSaleEndingPrice;
//荷兰拍用的
//记录当前的土地数量
uint256 public currentNumLandsMintedPublicSale;
//当前mint tokenId
uint256 public mintIndexPublicSaleAndContributors;
//ape地址,用于交钱
address public tokenContract;
//是否需要验证kyc
bool private isKycCheckRequired;
//验证kyc的hash root, Merkle tree验证
bytes32 public kycMerkleRoot;

//每次最大可以mint的上限
uint256 public maxMintPerTx;
//每个地址总共mint的上限
uint256 public maxMintPerAddress;
//每个地址mint的数量
mapping(address => uint256) public mintedPerAddress;
//无聊猿和变异猿是否可以额外mint
bool public claimableActive;
bool public adminClaimStarted;
//无聊猿mint的信息
address public alphaContract;
mapping(uint256 => bool) public alphaClaimed;
uint256 public alphaClaimedAmount;
//变异mint的信息
address public betaContract;
mapping(uint256 => bool) public betaClaimed;
uint256 public betaClaimedAmount;
uint256 public betaNftIdCurrent;
//贡献者mint的信息
bool public contributorsClaimActive;
mapping(address => uint256) public contributors;
uint256 public futureLandsNftIdCurrent;
//未来还可以再mint
address public futureMinter;
//保证官方没有作弊,用于所有人拿这里面的数据进行验证
Metadata[] public metadataHashes;
//随机数seed, 线上结果 0xaa77729d3466ca35ae8d28b3bbac7cc36a5031efdc430821c02bc31a238af445
bytes32 public keyHash;
//请求随机数的费用
uint256 public fee;
//下面三个获得的随机数然后计算出来的偏移量 线上结果 35117
uint256 public publicSaleAndContributorsOffset;
//线上结果 5491
uint256 public alphaOffset;
//线上结果 15491
uint256 public betaOffset;
//是否请求过随机数
mapping(bytes32 => bool) public isRandomRequestForPublicSaleAndContributors;
bool public publicSaleAndContributorsRandomnessRequested;
bool public ownerClaimRandomnessRequested;
// constants
uint256 immutable public MAX_LANDS;
uint256 immutable public MAX_LANDS_WITH_FUTURE;
uint256 immutable public MAX_ALPHA_NFT_AMOUNT;
uint256 immutable public MAX_BETA_NFT_AMOUNT;
uint256 immutable public MAX_PUBLIC_SALE_AMOUNT;
uint256 immutable public RESERVED_CONTRIBUTORS_AMOUNT;
uint256 immutable public MAX_FUTURE_LANDS;
uint256 constant public MAX_MINT_PER_BLOCK = 150;
// structs
struct LandAmount {
uint256 alpha;
uint256 beta;
uint256 publicSale;
uint256 future;
}
struct ContributorAmount {
address contributor;
uint256 amount;
}
struct Metadata {
bytes32 metadataHash;
bytes32 shuffledArrayHash;
uint256 startIndex;
uint256 endIndex;
}

struct ContractAddresses {
address alphaContract;
address betaContract;
address tokenContract;
}
// modifiers
。。。。。。。。。。
// events
。。。。。。。。。。
constructor(string memory name, string memory symbol,
ContractAddresses memory addresses,
LandAmount memory amount,
ContributorAmount[] memory _contributors,
address _vrfCoordinator, address _linkTokenAddress,
bytes32 _vrfKeyHash, uint256 _vrfFee,
address _operator
) ERC721(name, symbol) VRFConsumerBase(_vrfCoordinator, _linkTokenAddress) {
//无聊猿和变异猿的地址,持有这个的能够mint(具体的业务忘记了)
alphaContract = addresses.alphaContract;
betaContract = addresses.betaContract;
tokenContract = addresses.tokenContract;
MAX_ALPHA_NFT_AMOUNT = amount.alpha;
MAX_BETA_NFT_AMOUNT = amount.beta;
MAX_PUBLIC_SALE_AMOUNT = amount.publicSale;
MAX_FUTURE_LANDS = amount.future;
betaNftIdCurrent = amount.alpha; //beta starts after alpha
mintIndexPublicSaleAndContributors = amount.alpha + amount.beta; //public sale starts after beta
//贡献者mint数量
uint256 tempSum;
for(uint256 i; i<_contributors.length; ++i){
contributors[_contributors[i].contributor] = _contributors[i].amount;
tempSum += _contributors[i].amount;
}
RESERVED_CONTRIBUTORS_AMOUNT = tempSum;
//总数量
MAX_LANDS = amount.alpha + amount.beta + amount.publicSale + RESERVED_CONTRIBUTORS_AMOUNT;
//以后还可以mint的数量
MAX_LANDS_WITH_FUTURE = MAX_LANDS + amount.future;
futureLandsNftIdCurrent = MAX_LANDS; //future starts after public sale
//用于随机数请求
keyHash = _vrfKeyHash;
fee = _vrfFee;
//操作者地址
operator = _operator;
}
//一些set函数
。。。。。。。。。。
// Public Sale Methods
//公售
function startPublicSale(
uint256 _publicSalePriceLoweringDuration,
uint256 _publicSaleStartPrice,
uint256 _publicSaleEndingPrice,
uint256 _maxMintPerTx,
uint256 _maxMintPerAddress,
bool _isKycCheckRequired
) external onlyOperator {
require(!publicSaleActive, "Public sale has already begun");
//不用管,荷兰拍取消了
publicSalePriceLoweringDuration = _publicSalePriceLoweringDuration;
publicSaleStartPrice = _publicSaleStartPrice;
publicSaleEndingPrice = _publicSaleEndingPrice;
publicSaleStartTime = block.timestamp;
publicSaleActive = true;
//每次,每个地址的mint限制
maxMintPerTx = _maxMintPerTx;
maxMintPerAddress = _maxMintPerAddress;
isKycCheckRequired = _isKycCheckRequired;
emit LandPublicSaleStart(publicSalePriceLoweringDuration, publicSaleStartTime);
}

//停止公售
function stopPublicSale() external onlyOperator whenPublicSaleActive {
emit LandPublicSaleStop(getMintPrice(), getElapsedSaleTime());
publicSaleActive = false;
}
//本来是荷兰拍的,取消了,改成固定价格
function getElapsedSaleTime() private view returns (uint256) {
return publicSaleStartTime > 0 ? block.timestamp - publicSaleStartTime : 0;
}
function getMintPrice() public view whenPublicSaleActive returns (uint256) {
uint256 elapsed = getElapsedSaleTime();
uint256 price;
if(elapsed < publicSalePriceLoweringDuration) {
// Linear decreasing function
price =
publicSaleStartPrice -
( ( publicSaleStartPrice - publicSaleEndingPrice ) * elapsed ) / publicSalePriceLoweringDuration ;
} else {
price = publicSaleEndingPrice;
}
return price;
}
//mint land
function mintLands(uint256 numLands, bytes32[] calldata merkleProof) external whenPublicSaleActive nonReentrant {
//最少一块土地
require(numLands > 0, "Must mint at least one beta");
//mint的土地有没有超过最大限制
require(currentNumLandsMintedPublicSale + numLands <= MAX_PUBLIC_SALE_AMOUNT, "Minting would exceed max supply");
//每次mint限制
require(numLands <= maxMintPerTx, "numLands should not exceed maxMintPerTx");
//每个人的mint限制
require(numLands + mintedPerAddress[msg.sender] <= maxMintPerAddress, "sender address cannot mint more than maxMintPerAddress lands");
//是否需要kyc验证,贡献者需要,公售的不要
if(isKycCheckRequired) {
require(MerkleProof.verify(merkleProof, kycMerkleRoot, keccak256(abi.encodePacked(msg.sender))), "Sender address is not in KYC allowlist");
} else {
//不允许合约等间接调用,老老实实正常操作
require(msg.sender == tx.origin, "Minting from smart contracts is disallowed");
}
//获得土地价格
uint256 mintPrice = getMintPrice();
//交钱
IERC20(tokenContract).safeTransferFrom(msg.sender, address(this), mintPrice * numLands);
//记录当前的土地数量
currentNumLandsMintedPublicSale += numLands;
//记录每个用户的的土地数量
mintedPerAddress[msg.sender] += numLands;
emit PublicSaleMint(msg.sender, numLands, mintPrice);
//mint land
mintLandsCommon(numLands, msg.sender);
}
//调用erc721 mint
function mintLandsCommon(uint256 numLands, address recipient) private {
for (uint256 i; i < numLands; ++i) {
_safeMint(recipient, mintIndexPublicSaleAndContributors++);
}
}
//赚钱喽,取钱
function withdraw() external onlyOwner {
uint256 balance = address(this).balance;
if(balance > 0){
Address.sendValue(payable(owner()), balance);
}
balance = IERC20(tokenContract).balanceOf(address(this));
if(balance > 0){
IERC20(tokenContract).safeTransfer(owner(), balance);
}
}
// Alpha/Beta Claim Methods
//无聊猿和变异猿持有者是否可以再mint
function flipClaimableState() external onlyOperator {
claimableActive = !claimableActive;
emit ClaimableStateChanged(claimableActive);
}
//无聊猿和变异猿持有者再mint
function nftOwnerClaimLand(uint256[] calldata alphaTokenIds, uint256[] calldata betaTokenIds) external whenClaimableActive {
require(alphaTokenIds.length > 0 || betaTokenIds.length > 0, "Should claim at least one land");
require(alphaTokenIds.length + betaTokenIds.length <= MAX_MINT_PER_BLOCK, "Input length should be <= MAX_MINT_PER_BLOCK");
//无聊猿持有者mint
alphaClaimLand(alphaTokenIds);
//变异猿持有者mint
betaClaimLand(betaTokenIds);
}
function alphaClaimLand(uint256[] calldata alphaTokenIds) private {
for(uint256 i; i < alphaTokenIds.length; ++i){
uint256 alphaTokenId = alphaTokenIds[i];
require(!alphaClaimed[alphaTokenId], "ALPHA NFT already claimed");
require(ERC721(alphaContract).ownerOf(alphaTokenId) == msg.sender, "Must own all of the alpha defined by alphaTokenIds");
alphaClaimLandByTokenId(alphaTokenId);
}
}
function alphaClaimLandByTokenId(uint256 alphaTokenId) private {
alphaClaimed[alphaTokenId] = true;
++alphaClaimedAmount;
_safeMint(msg.sender, alphaTokenId);
}
// Contributors Claim Methods
//开始贡献者mint
function startContributorsClaimPeriod() onlyOperator external {
require(!contributorsClaimActive, "Contributors claim is already active");
contributorsClaimActive = true;
emit ContributorsClaimStart(block.timestamp);
}
//结束贡献者mint
function stopContributorsClaimPeriod() onlyOperator external whenContributorsClaimActive {
contributorsClaimActive = false;
emit ContributorsClaimStop(block.timestamp);
}
function contributorsClaimLand(uint256 amount, address recipient) external onlyContributors(msg.sender) whenContributorsClaimActive {
require(amount > 0, "Must mint at least one land");
require(amount <= MAX_MINT_PER_BLOCK, "amount should not exceed MAX_MINT_PER_BLOCK");
require(amount <= contributors[msg.sender], "Contributor cannot claim other lands");
contributors[msg.sender] -= amount;
mintLandsCommon(amount, recipient);
}
//应该是把没卖完的土地mint掉,但是不可能出现这种事情的吧。。。。留个后手总归好的
function claimUnclaimedAndUnsoldLands(address recipient) external onlyOwner {
claimUnclaimedAndUnsoldLandsWithAmount(recipient, MAX_MINT_PER_BLOCK);
}
。。。。。。。。。
// metadata
//用于给别人验证,保证我的土拍是没有作弊的
function loadLandMetadata(Metadata memory _landMetadata)
external onlyOperator checkMetadataRange(_landMetadata)
checkFirstMetadataRange(metadataHashes.length, _landMetadata.startIndex, _landMetadata.endIndex)
{
metadataHashes.push(_landMetadata);
}
function putLandMetadataAtIndex(uint256 index, Metadata memory _landMetadata)
external onlyOperator checkMetadataRange(_landMetadata)
checkFirstMetadataRange(index, _landMetadata.startIndex, _landMetadata.endIndex)
{
metadataHashes[index] = _landMetadata;
}
// randomness
//用link vrf生成随机数,这个随机数给公售和贡献者用
function requestRandomnessForPublicSaleAndContributors() external onlyOperator returns (bytes32 requestId) {
require(!publicSaleAndContributorsRandomnessRequested, "Public Sale And Contributors Offset already requested");
publicSaleAndContributorsRandomnessRequested = true;
requestId = requestRandomnessPrivate();
isRandomRequestForPublicSaleAndContributors[requestId] = true;
}
//用link vrf生成随机数,这个随机数给猿持有者用
function requestRandomnessForOwnerClaim() external onlyOperator returns (bytes32 requestId) {
require(!ownerClaimRandomnessRequested, "Owner Claim Offset already requested");
ownerClaimRandomnessRequested = true;
requestId = requestRandomnessPrivate();
isRandomRequestForPublicSaleAndContributors[requestId] = false;
}
function requestRandomnessPrivate() private returns (bytes32 requestId) {
require(
LINK.balanceOf(address(this)) >= fee,
"Not enough LINK"
);
return requestRandomness(keyHash, fee);
}
//link会把产生的随机数回调这个函数,然后项目方进行业务处理,这里猜测应该是概率不同,我持有nft,那拿到好土地的概率会更大
function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
//区分是公售、贡献者还是猿持有者随机数
if(isRandomRequestForPublicSaleAndContributors[requestId]){
publicSaleAndContributorsOffset = (randomness % (MAX_PUBLIC_SALE_AMOUNT + RESERVED_CONTRIBUTORS_AMOUNT));
emit StartingIndexSetPublicSale(publicSaleAndContributorsOffset);
} else {
alphaOffset = (randomness % MAX_ALPHA_NFT_AMOUNT);
betaOffset = (randomness % MAX_BETA_NFT_AMOUNT);
emit StartingIndexSetAlphaBeta(alphaOffset, betaOffset);
}
}
//停止公售
function stopPublicSale() external onlyOperator whenPublicSaleActive {
emit LandPublicSaleStop(getMintPrice(), getElapsedSaleTime());
publicSaleActive = false;
}
//本来是荷兰拍的,取消了,改成固定价格
function getElapsedSaleTime() private view returns (uint256) {
return publicSaleStartTime > 0 ? block.timestamp - publicSaleStartTime : 0;
}
function getMintPrice() public view whenPublicSaleActive returns (uint256) {
uint256 elapsed = getElapsedSaleTime();
uint256 price;
if(elapsed < publicSalePriceLoweringDuration) {
// Linear decreasing function
price =
publicSaleStartPrice -
( ( publicSaleStartPrice - publicSaleEndingPrice ) * elapsed ) / publicSalePriceLoweringDuration ;
} else {
price = publicSaleEndingPrice;
}
return price;
}
//mint land
function mintLands(uint256 numLands, bytes32[] calldata merkleProof) external whenPublicSaleActive nonReentrant {
//最少一块土地
require(numLands > 0, "Must mint at least one beta");
//mint的土地有没有超过最大限制
require(currentNumLandsMintedPublicSale + numLands <= MAX_PUBLIC_SALE_AMOUNT, "Minting would exceed max supply");
//每次mint限制
require(numLands <= maxMintPerTx, "numLands should not exceed maxMintPerTx");
//每个人的mint限制
require(numLands + mintedPerAddress[msg.sender] <= maxMintPerAddress, "sender address cannot mint more than maxMintPerAddress lands");
//是否需要kyc验证,贡献者需要,公售的不要
if(isKycCheckRequired) {
require(MerkleProof.verify(merkleProof, kycMerkleRoot, keccak256(abi.encodePacked(msg.sender))), "Sender address is not in KYC allowlist");
} else {
//不允许合约等间接调用,老老实实正常操作
require(msg.sender == tx.origin, "Minting from smart contracts is disallowed");
}
//获得土地价格
uint256 mintPrice = getMintPrice();
//交钱
IERC20(tokenContract).safeTransferFrom(msg.sender, address(this), mintPrice * numLands);
//记录当前的土地数量
currentNumLandsMintedPublicSale += numLands;
//记录每个用户的的土地数量
mintedPerAddress[msg.sender] += numLands;
emit PublicSaleMint(msg.sender, numLands, mintPrice);
//mint land
mintLandsCommon(numLands, msg.sender);
}
//调用erc721 mint
function mintLandsCommon(uint256 numLands, address recipient) private {
for (uint256 i; i < numLands; ++i) {
_safeMint(recipient, mintIndexPublicSaleAndContributors++);
}
}
//赚钱喽,取钱
function withdraw() external onlyOwner {
uint256 balance = address(this).balance;
if(balance > 0){
Address.sendValue(payable(owner()), balance);
}
balance = IERC20(tokenContract).balanceOf(address(this));
if(balance > 0){
IERC20(tokenContract).safeTransfer(owner(), balance);
}
}
// Alpha/Beta Claim Methods
//无聊猿和变异猿持有者是否可以再mint
function flipClaimableState() external onlyOperator {
claimableActive = !claimableActive;
emit ClaimableStateChanged(claimableActive);
}
//无聊猿和变异猿持有者再mint
function nftOwnerClaimLand(uint256[] calldata alphaTokenIds, uint256[] calldata betaTokenIds) external whenClaimableActive {
require(alphaTokenIds.length > 0 || betaTokenIds.length > 0, "Should claim at least one land");
require(alphaTokenIds.length + betaTokenIds.length <= MAX_MINT_PER_BLOCK, "Input length should be <= MAX_MINT_PER_BLOCK");
//无聊猿持有者mint
alphaClaimLand(alphaTokenIds);
//变异猿持有者mint
betaClaimLand(betaTokenIds);
}
function alphaClaimLand(uint256[] calldata alphaTokenIds) private {
for(uint256 i; i < alphaTokenIds.length; ++i){
uint256 alphaTokenId = alphaTokenIds[i];
require(!alphaClaimed[alphaTokenId], "ALPHA NFT already claimed");
require(ERC721(alphaContract).ownerOf(alphaTokenId) == msg.sender, "Must own all of the alpha defined by alphaTokenIds");
alphaClaimLandByTokenId(alphaTokenId);
}
}
function alphaClaimLandByTokenId(uint256 alphaTokenId) private {
alphaClaimed[alphaTokenId] = true;
++alphaClaimedAmount;
_safeMint(msg.sender, alphaTokenId);
}
// Contributors Claim Methods
//开始贡献者mint
function startContributorsClaimPeriod() onlyOperator external {
require(!contributorsClaimActive, "Contributors claim is already active");
contributorsClaimActive = true;
emit ContributorsClaimStart(block.timestamp);
}
//结束贡献者mint
function stopContributorsClaimPeriod() onlyOperator external whenContributorsClaimActive {
contributorsClaimActive = false;
emit ContributorsClaimStop(block.timestamp);
}
function contributorsClaimLand(uint256 amount, address recipient) external onlyContributors(msg.sender) whenContributorsClaimActive {
require(amount > 0, "Must mint at least one land");
require(amount <= MAX_MINT_PER_BLOCK, "amount should not exceed MAX_MINT_PER_BLOCK");
require(amount <= contributors[msg.sender], "Contributor cannot claim other lands");
contributors[msg.sender] -= amount;
mintLandsCommon(amount, recipient);
}
//应该是把没卖完的土地mint掉,但是不可能出现这种事情的吧。。。。留个后手总归好的
function claimUnclaimedAndUnsoldLands(address recipient) external onlyOwner {
claimUnclaimedAndUnsoldLandsWithAmount(recipient, MAX_MINT_PER_BLOCK);
}
。。。。。。。。。
// metadata
//用于给别人验证,保证我的土拍是没有作弊的
function loadLandMetadata(Metadata memory _landMetadata)
external onlyOperator checkMetadataRange(_landMetadata)
checkFirstMetadataRange(metadataHashes.length, _landMetadata.startIndex, _landMetadata.endIndex)
{
metadataHashes.push(_landMetadata);
}
function putLandMetadataAtIndex(uint256 index, Metadata memory _landMetadata)
external onlyOperator checkMetadataRange(_landMetadata)
checkFirstMetadataRange(index, _landMetadata.startIndex, _landMetadata.endIndex)
{
metadataHashes[index] = _landMetadata;
}
// randomness
//用link vrf生成随机数,这个随机数给公售和贡献者用
function requestRandomnessForPublicSaleAndContributors() external onlyOperator returns (bytes32 requestId) {
require(!publicSaleAndContributorsRandomnessRequested, "Public Sale And Contributors Offset already requested");
publicSaleAndContributorsRandomnessRequested = true;
requestId = requestRandomnessPrivate();
isRandomRequestForPublicSaleAndContributors[requestId] = true;
}
//用link vrf生成随机数,这个随机数给猿持有者用
function requestRandomnessForOwnerClaim() external onlyOperator returns (bytes32 requestId) {
require(!ownerClaimRandomnessRequested, "Owner Claim Offset already requested");
ownerClaimRandomnessRequested = true;
requestId = requestRandomnessPrivate();
isRandomRequestForPublicSaleAndContributors[requestId] = false;
}
function requestRandomnessPrivate() private returns (bytes32 requestId) {
require(
LINK.balanceOf(address(this)) >= fee,
"Not enough LINK"
);
return requestRandomness(keyHash, fee);
}
//link会把产生的随机数回调这个函数,然后项目方进行业务处理,这里猜测应该是概率不同,我持有nft,那拿到好土地的概率会更大
function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
//区分是公售、贡献者还是猿持有者随机数
if(isRandomRequestForPublicSaleAndContributors[requestId]){
publicSaleAndContributorsOffset = (randomness % (MAX_PUBLIC_SALE_AMOUNT + RESERVED_CONTRIBUTORS_AMOUNT));
emit StartingIndexSetPublicSale(publicSaleAndContributorsOffset);
} else {
alphaOffset = (randomness % MAX_ALPHA_NFT_AMOUNT);
betaOffset = (randomness % MAX_BETA_NFT_AMOUNT);
emit StartingIndexSetAlphaBeta(alphaOffset, betaOffset);
}
}
No activity yet