Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: lottery tests and align design to factory #41

Merged
merged 4 commits into from
Dec 26, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions contracts/apps/factory/lotteryFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ abstract contract NFTLotteryFactory is INFTLotteryFactory {
address nftContract,
address decrypter,
uint256 fee,
uint8 threshold,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mmmm...
Is there any reason to remove the threshold param from the function signature?? Cause it is required at the time of deployment of lottery

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We change the requirement specification requested by Olli. Its probably that after this PR some more changes will be require. Hope doesnt change to much on the re-design part.

address fairyring
) public returns (address) {
bool isERC721 = _supportsInterface(nftContract, type(IERC721).interfaceId);
Expand Down Expand Up @@ -54,7 +53,7 @@ abstract contract NFTLotteryFactory is INFTLotteryFactory {
revert NFTLotteryFactory__UnsupportedNFTStandards();
}

address lotteryAddress = _deployLottery(nftHandler, fee, threshold, fairyring, decrypter);
address lotteryAddress = _deployLottery(nftHandler, fee, fairyring, decrypter);

lotteryTypes[lotteryAddress] = standard;
emit LotteryCreated(lotteryAddress, standard);
Expand All @@ -73,11 +72,10 @@ abstract contract NFTLotteryFactory is INFTLotteryFactory {
function _deployLottery(
address nftHandler,
uint256 fee,
uint8 threshold,
address fairyringContract,
address decrypter
) internal returns (address) {
NFTLotteryProxy lottery = new NFTLotteryProxy(nftHandler, fee, threshold, fairyringContract, decrypter);
NFTLotteryProxy lottery = new NFTLotteryProxy(nftHandler, fee, fairyringContract, decrypter);
return address(lottery);
}
}
Expand All @@ -86,11 +84,11 @@ contract NFTLotteryProxy {
address private immutable nftHandler;
address private immutable implementation;

constructor(address _nftHandler, uint256 _fee, uint8 _threshold, address _fairyringContract, address _decrypter) {
constructor(address _nftHandler, uint256 _fee, address _fairyringContract, address _decrypter) {
nftHandler = _nftHandler;

// Deploy implementation contract
implementation = address(new NFTLottery(_nftHandler, _fee, _threshold, _fairyringContract, _decrypter));
implementation = address(new NFTLottery(_nftHandler, _fee, _fairyringContract, _decrypter));
}

fallback() external payable {
Expand Down
1 change: 0 additions & 1 deletion contracts/apps/factory/lotteryinterface.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ interface INFTLotteryFactory {
function createLottery(
address nftContract,
uint256 fee,
uint8 threshold,
address fairyringContract,
address decrypter
) external returns (address);
Expand Down
87 changes: 30 additions & 57 deletions contracts/apps/lottery.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { IFairyringContract, IDecrypter } from "./Ifairyring.sol";
// import { IERC721A } from "erc721a/contracts/IERC721A.sol";
import { Lazy1155 } from "./lazy1155.sol";
// import "hardhat/console.sol";
import "hardhat/console.sol";
// import { console } from "forge-std/console.sol";
import { ERC1155Holder } from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";

Expand Down Expand Up @@ -65,15 +65,10 @@ contract NFTLottery is Ownable, ERC1155Holder {
/// @notice This will help with generating random numbers
IFairyringContract public fairyringContract;

/// @notice Contains all the requerid nft data
struct Collection {
Lazy1155 nft; // This will maintain the NFTs
uint256 tokenIndex; // This will maintain the internal TokenId index of NFTs
uint256 maxTokens; // This will maintain the internal Max tokens of NFTs as cached value
}

/// @notice Collection
Collection[] private _collections;
Lazy1155 private _nft;

uint256 private _tokensIdCap;

/// @notice Indicates if the campaign is live or not.
bool public isCampaignOpen = false;
Expand All @@ -86,34 +81,20 @@ contract NFTLottery is Ownable, ERC1155Holder {
* @param _decrypter Address of the decryption contract
* @param _fee The fee required to submit a draw
* @param _fairyringContract Address of the fairy ring contract
* @param _addressList A list of NFTs Addresses
* @param _erc1155 A list of NFTs Addresses
*/
constructor(
address _decrypter,
uint256 _fee,
address _fairyringContract,
address[] memory _addressList
) Ownable(msg.sender) {
// We expect here a _nftContracts.length == 4 for probability 7% (1%+2%+2%+2% distance algorithm)
if (_addressList.length > 4) revert NFTLottery__InternalError("_addressList should be max length 4 elements");

uint256 expectedMaxTokens = 1;
for (uint256 i = 0; i < _addressList.length; ++i) {
Lazy1155 nft = Lazy1155(_addressList[i]);
uint256 maxTokens = _getMaxNFTs(nft);
// console.log("A[%s] E[%s] A[%s]", address(nft), expectedMaxTokens, maxTokens);
if (maxTokens != expectedMaxTokens)
revert NFTLottery__TooFewNFTs("At least 1 nft element should exist on every nft contract");

_collections.push(Collection({ nft: nft, tokenIndex: 0, maxTokens: maxTokens }));
totalCollectionItems = totalCollectionItems + maxTokens;
expectedMaxTokens = expectedMaxTokens * 2;
constructor(address _erc1155, uint256 _fee, address _fairyringContract, address _decrypter) Ownable(msg.sender) {
_nft = Lazy1155(_erc1155);
_tokensIdCap = 0;
for (uint256 i = 0; i < 255; i++) {
// Scan from zero all tokensId assigned to owner
if (_nft.tokenExists(i)) {
_tokensIdCap++;
}
}

decrypterContract = IDecrypter(_decrypter);
fee = _fee;
fairyringContract = IFairyringContract(_fairyringContract);

emit LotteryInitialized(_decrypter, _fee);
}

Expand Down Expand Up @@ -180,27 +161,23 @@ contract NFTLottery is Ownable, ERC1155Holder {

//console.log("guess[%s] random[%s] distance [%s]", normalizedGuess, normalizedRandom, distance);
// If distance is less than 4, we got a winner
if (distance < 4) {
if (distance < _tokensIdCap) {
// Winner case
isWinner = true;
// We start from the Top winning according to distance and
// if there are no more nft tokens, we increase distance by one
// to check if there are tokens in other levels
// Here, could be the case that the winner win an nft, but there are no more
// on low levels prices. In this case, is a lose.
for (uint256 i = distance; i < _collections.length; i++) {
Collection storage collection = _collections[i];

// Check if there are NFTs remaining
if (collection.tokenIndex < collection.maxTokens) {
collection.nft.safeTransferFrom(address(this), msg.sender, collection.tokenIndex, 1, "0x0");
nftId = collection.tokenIndex;
emit LotteryDrawn(msg.sender, isWinner, collection.tokenIndex, totalDraws);
emit MintedNft(msg.sender, collection.tokenIndex);
++collection.tokenIndex;
for (uint256 tokenId = distance; tokenId < _tokensIdCap; tokenId++) {
// Check if there are quantity of NFT id TokenId remaining
if (_nft.balanceOf(address(this), tokenId) > 0) {
_nft.safeTransferFrom(address(this), msg.sender, tokenId, 1, "0x0");
emit LotteryDrawn(msg.sender, isWinner, tokenId, totalDraws);
emit MintedNft(msg.sender, tokenId);
++user.winCount;
userDetails[msg.sender] = user;
return nftId;
return tokenId;
}
}
revert NFTLottery__TooFewNFTs("No more NFTs");
Expand All @@ -217,28 +194,24 @@ contract NFTLottery is Ownable, ERC1155Holder {
revert NFTLottery__TooFewPooPoints();
}

console.log(" Points[%s]", user.pooPoints);
// Check if there are NFTs remaining starting from low nfts types
for (uint256 i = _collections.length - 1; i > 0; i--) {
for (uint256 tokenId = _tokensIdCap - 1; tokenId > 0; tokenId--) {
// i = 3,2,1,0
Collection storage collection = _collections[i];
// console.log("TokenId[%s] max[%s]", collection.tokenIndex, collection.maxTokens);
// console.log(" Points[%s]", user.pooPoints);
console.log("TokenId[%s]", tokenId);
uint256 balance = _nft.balanceOf(address(this), tokenId);
console.log("TokenId[%s] max[%s]", tokenId, balance);

// Check if there are NFTs remaining
if (collection.tokenIndex < collection.maxTokens) {
if (balance > 0) {
// Deduct 100 poo points
user.pooPoints -= 100;
userDetails[msg.sender] = user;

// Transfer NFT to the user
nftId = collection.tokenIndex;
collection.nft.safeTransferFrom(address(this), msg.sender, nftId, 1, "0x0");
emit MintedNft(msg.sender, collection.tokenIndex);

// Update index of NFTs
++collection.tokenIndex;

return nftId;
_nft.safeTransferFrom(address(this), msg.sender, tokenId, 1, "0x0");
emit MintedNft(msg.sender, tokenId);
return tokenId;
}
}

Expand Down
2 changes: 1 addition & 1 deletion deploy/Deploy_NFTLottery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {

console.log(JSON.stringify(ERC721Contract.address, null, 2))

// address _decrypter, uint256 _fee, uint8 _threshold, address _fairyringContract, address _nftContract
// address _decrypter, uint256 _fee, address _fairyringContract, address _nftContract
const lotteryContract: DeployResult = await hre.deployments.deploy(lottery, {
from: deployer,
args: [fairyRingContract.address, 0.1, 20, fairyRingContract.address, ERC721Contract.address],
Expand Down
72 changes: 34 additions & 38 deletions test/apps/lottery.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@ contract LotteryTest is Test, ERC1155Holder {
// address[] private _nftList721;

// test for 1155
Lazy1155[] private _collections1155;
address[] private _nftList1155;
Lazy1155 private nft1155;

IFairyringContract private _fairyringContract;
Handler private _handler;
Expand All @@ -59,7 +58,7 @@ contract LotteryTest is Test, ERC1155Holder {
uint256 private _fee = 1 ether;

function setUp() public {
uint8 tokenCap = 1;
uint8 tokenCap = 4;

// Setup
_fundedUser = makeAddr("funded_user");
Expand Down Expand Up @@ -99,51 +98,48 @@ contract LotteryTest is Test, ERC1155Holder {
// }

// NFT Contructors and Minting 1155
for (uint256 i = 0; i < 4; ++i) {
// Construct NFT contract
Lazy1155 nft = new Lazy1155(tokenCap, "ipfs://hash/{id}.json", 0); // we want to test mint, so =0

// Mint Tokens
uint256[] memory ids = new uint256[](tokenCap);
uint256[] memory amounts = new uint256[](tokenCap);

for (uint256 n = 0; n < tokenCap; ++n) {
ids[n] = n;
amounts[n] = 1;
}
nft.mintBatch(address(this), ids, amounts, "");
assertEq(tokenCap, nft.totalSupply());

// Add to list
_collections1155.push(nft);
_nftList1155.push(address(nft));
tokenCap *= 2;

// Construct NFT contract
Lazy1155 nft1155 = new Lazy1155(tokenCap, "ipfs://hash/{id}.json", 0); // we want to test mint, so =0

// Mint Tokens
uint256[] memory ids = new uint256[](tokenCap);
uint256[] memory amounts = new uint256[](tokenCap);

uint256 quantity = 1;
for (uint256 tokenId = 0; tokenId < tokenCap; ++tokenId) {
ids[tokenId] = tokenId;
amounts[tokenId] = quantity;
quantity *= 2;
}
nft1155.mintBatch(address(this), ids, amounts, "");
assertEq(15, nft1155.totalSupply());

// Random mock
_fairyringContract = IFairyringContract(address(0));

// the owner is LotteryTest
// Construct Lottery
_lottery = new NFTLottery(address(_fairyringContract), _fee, address(_fairyringContract), _nftList1155);
_lottery = new NFTLottery(address(nft1155), _fee, address(_fairyringContract), address(_fairyringContract));

// Set approval for all NFTs to Loterry as `Operator`
for (uint256 i = 0; i < 4; ++i) {
_collections1155[i].setApprovalForAll(address(_lottery), true);
bool isApproved = _collections1155[i].isApprovedForAll(address(this), address(_lottery));
assertEq(true, isApproved);

// transfer ownership of all NFT tokens to lottery contract
uint256 totalSupply = _collections1155[i].totalSupply();
uint256 totalBalance = 0;
for (uint256 tokenId = 0; tokenId < totalSupply; ++tokenId) {
_collections1155[i].safeTransferFrom(address(this), address(_lottery), tokenId, 1, "");
uint256 balance = _collections1155[i].balanceOf(address(_lottery), tokenId);
assertEq(1, balance);
totalBalance += balance;
}
assertEq(totalSupply, totalBalance);
nft1155.setApprovalForAll(address(_lottery), true);
bool isApproved = nft1155.isApprovedForAll(address(this), address(_lottery));
assertEq(true, isApproved);

// transfer ownership of all NFT tokens to lottery contract
uint256 totalSupply = nft1155.totalSupply();
uint256 totalBalance = 0;

quantity = 1;
for (uint256 tokenId = 0; tokenId < tokenCap; ++tokenId) {
nft1155.safeTransferFrom(address(this), address(_lottery), tokenId, quantity, "");
quantity *= 2;
uint256 balance = nft1155.balanceOf(address(_lottery), tokenId);
assertEq(1, balance);
totalBalance += balance;
}
assertEq(totalSupply, totalBalance);

_handler = new Handler(address(_lottery), _fundedUser);
targetContract(address(_handler));
Expand Down
Loading