diff --git a/community/lootbox_hackathon/mul_lottery/ChainLinkIntegration/LinkTokenInterface.sol b/community/lootbox_hackathon/mul_lottery/ChainLinkIntegration/LinkTokenInterface.sol new file mode 100644 index 0000000000..9c0c576 --- /dev/null +++ b/community/lootbox_hackathon/mul_lottery/ChainLinkIntegration/LinkTokenInterface.sol @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface LinkTokenInterface { + + function allowance( + address owner, + address spender + ) + external + view + returns ( + uint256 remaining + ); + + function approve( + address spender, + uint256 value + ) + external + returns ( + bool success + ); + + function balanceOf( + address owner + ) + external + view + returns ( + uint256 balance + ); + + function decimals() + external + view + returns ( + uint8 decimalPlaces + ); + + function decreaseApproval( + address spender, + uint256 addedValue + ) + external + returns ( + bool success + ); + + function increaseApproval( + address spender, + uint256 subtractedValue + ) external; + + function name() + external + view + returns ( + string memory tokenName + ); + + function symbol() + external + view + returns ( + string memory tokenSymbol + ); + + function totalSupply() + external + view + returns ( + uint256 totalTokensIssued + ); + + function transfer( + address to, + uint256 value + ) + external + returns ( + bool success + ); + + function transferAndCall( + address to, + uint256 value, + bytes calldata data + ) + external + returns ( + bool success + ); + + function transferFrom( + address from, + address to, + uint256 value + ) + external + returns ( + bool success + ); + +} \ No newline at end of file diff --git a/community/lootbox_hackathon/mul_lottery/ChainLinkIntegration/VRFConsumerBase.sol b/community/lootbox_hackathon/mul_lottery/ChainLinkIntegration/VRFConsumerBase.sol new file mode 100644 index 0000000000..e25fe04 --- /dev/null +++ b/community/lootbox_hackathon/mul_lottery/ChainLinkIntegration/VRFConsumerBase.sol @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "./LinkTokenInterface.sol"; + +import "./VRFRequestIDBase.sol"; + +/** **************************************************************************** + * @notice Interface for contracts using VRF randomness + * ***************************************************************************** + * @dev PURPOSE + * + * @dev Reggie the Random Oracle (not his real job) wants to provide randomness + * @dev to Vera the verifier in such a way that Vera can be sure he's not + * @dev making his output up to suit himself. Reggie provides Vera a public key + * @dev to which he knows the secret key. Each time Vera provides a seed to + * @dev Reggie, he gives back a value which is computed completely + * @dev deterministically from the seed and the secret key. + * + * @dev Reggie provides a proof by which Vera can verify that the output was + * @dev correctly computed once Reggie tells it to her, but without that proof, + * @dev the output is indistinguishable to her from a uniform random sample + * @dev from the output space. + * + * @dev The purpose of this contract is to make it easy for unrelated contracts + * @dev to talk to Vera the verifier about the work Reggie is doing, to provide + * @dev simple access to a verifiable source of randomness. + * ***************************************************************************** + * @dev USAGE + * + * @dev Calling contracts must inherit from VRFConsumerBase, and can + * @dev initialize VRFConsumerBase's attributes in their constructor as + * @dev shown: + * + * @dev contract VRFConsumer { + * @dev constuctor(, address _vrfCoordinator, address _link) + * @dev VRFConsumerBase(_vrfCoordinator, _link) public { + * @dev + * @dev } + * @dev } + * + * @dev The oracle will have given you an ID for the VRF keypair they have + * @dev committed to (let's call it keyHash), and have told you the minimum LINK + * @dev price for VRF service. Make sure your contract has sufficient LINK, and + * @dev call requestRandomness(keyHash, fee, seed), where seed is the input you + * @dev want to generate randomness from. + * + * @dev Once the VRFCoordinator has received and validated the oracle's response + * @dev to your request, it will call your contract's fulfillRandomness method. + * + * @dev The randomness argument to fulfillRandomness is the actual random value + * @dev generated from your seed. + * + * @dev The requestId argument is generated from the keyHash and the seed by + * @dev makeRequestId(keyHash, seed). If your contract could have concurrent + * @dev requests open, you can use the requestId to track which seed is + * @dev associated with which randomness. See VRFRequestIDBase.sol for more + * @dev details. (See "SECURITY CONSIDERATIONS" for principles to keep in mind, + * @dev if your contract could have multiple requests in flight simultaneously.) + * + * @dev Colliding `requestId`s are cryptographically impossible as long as seeds + * @dev differ. (Which is critical to making unpredictable randomness! See the + * @dev next section.) + * + * ***************************************************************************** + * @dev SECURITY CONSIDERATIONS + * + * @dev A method with the ability to call your fulfillRandomness method directly + * @dev could spoof a VRF response with any random value, so it's critical that + * @dev it cannot be directly called by anything other than this base contract + * @dev (specifically, by the VRFConsumerBase.rawFulfillRandomness method). + * + * @dev For your users to trust that your contract's random behavior is free + * @dev from malicious interference, it's best if you can write it so that all + * @dev behaviors implied by a VRF response are executed *during* your + * @dev fulfillRandomness method. If your contract must store the response (or + * @dev anything derived from it) and use it later, you must ensure that any + * @dev user-significant behavior which depends on that stored value cannot be + * @dev manipulated by a subsequent VRF request. + * + * @dev Similarly, both miners and the VRF oracle itself have some influence + * @dev over the order in which VRF responses appear on the blockchain, so if + * @dev your contract could have multiple VRF requests in flight simultaneously, + * @dev you must ensure that the order in which the VRF responses arrive cannot + * @dev be used to manipulate your contract's user-significant behavior. + * + * @dev Since the ultimate input to the VRF is mixed with the block hash of the + * @dev block in which the request is made, user-provided seeds have no impact + * @dev on its economic security properties. They are only included for API + * @dev compatability with previous versions of this contract. + * + * @dev Since the block hash of the block which contains the requestRandomness + * @dev call is mixed into the input to the VRF *last*, a sufficiently powerful + * @dev miner could, in principle, fork the blockchain to evict the block + * @dev containing the request, forcing the request to be included in a + * @dev different block with a different hash, and therefore a different input + * @dev to the VRF. However, such an attack would incur a substantial economic + * @dev cost. This cost scales with the number of blocks the VRF oracle waits + * @dev until it calls responds to a request. + */ +abstract contract VRFConsumerBase is VRFRequestIDBase { + + /** + * @notice fulfillRandomness handles the VRF response. Your contract must + * @notice implement it. See "SECURITY CONSIDERATIONS" above for important + * @notice principles to keep in mind when implementing your fulfillRandomness + * @notice method. + * + * @dev VRFConsumerBase expects its subcontracts to have a method with this + * @dev signature, and will call it once it has verified the proof + * @dev associated with the randomness. (It is triggered via a call to + * @dev rawFulfillRandomness, below.) + * + * @param requestId The Id initially returned by requestRandomness + * @param randomness the VRF output + */ + function fulfillRandomness( + bytes32 requestId, + uint256 randomness + ) + internal + virtual; + + /** + * @dev In order to keep backwards compatibility we have kept the user + * seed field around. We remove the use of it because given that the blockhash + * enters later, it overrides whatever randomness the used seed provides. + * Given that it adds no security, and can easily lead to misunderstandings, + * we have removed it from usage and can now provide a simpler API. + */ + uint256 constant private USER_SEED_PLACEHOLDER = 0; + + /** + * @notice requestRandomness initiates a request for VRF output given _seed + * + * @dev The fulfillRandomness method receives the output, once it's provided + * @dev by the Oracle, and verified by the vrfCoordinator. + * + * @dev The _keyHash must already be registered with the VRFCoordinator, and + * @dev the _fee must exceed the fee specified during registration of the + * @dev _keyHash. + * + * @dev The _seed parameter is vestigial, and is kept only for API + * @dev compatibility with older versions. It can't *hurt* to mix in some of + * @dev your own randomness, here, but it's not necessary because the VRF + * @dev oracle will mix the hash of the block containing your request into the + * @dev VRF seed it ultimately uses. + * + * @param _keyHash ID of public key against which randomness is generated + * @param _fee The amount of LINK to send with the request + * + * @return requestId unique ID for this request + * + * @dev The returned requestId can be used to distinguish responses to + * @dev concurrent requests. It is passed as the first argument to + * @dev fulfillRandomness. + */ + function requestRandomness( + bytes32 _keyHash, + uint256 _fee + ) + internal + returns ( + bytes32 requestId + ) + { + LINK.transferAndCall(vrfCoordinator, _fee, abi.encode(_keyHash, USER_SEED_PLACEHOLDER)); + // This is the seed passed to VRFCoordinator. The oracle will mix this with + // the hash of the block containing this request to obtain the seed/input + // which is finally passed to the VRF cryptographic machinery. + uint256 vRFSeed = makeVRFInputSeed(_keyHash, USER_SEED_PLACEHOLDER, address(this), nonces[_keyHash]); + // nonces[_keyHash] must stay in sync with + // VRFCoordinator.nonces[_keyHash][this], which was incremented by the above + // successful LINK.transferAndCall (in VRFCoordinator.randomnessRequest). + // This provides protection against the user repeating their input seed, + // which would result in a predictable/duplicate output, if multiple such + // requests appeared in the same block. + nonces[_keyHash] = nonces[_keyHash] + 1; + return makeRequestId(_keyHash, vRFSeed); + } + + LinkTokenInterface immutable internal LINK; + address immutable private vrfCoordinator; + + // Nonces for each VRF key from which randomness has been requested. + // + // Must stay in sync with VRFCoordinator[_keyHash][this] + mapping(bytes32 /* keyHash */ => uint256 /* nonce */) private nonces; + + /** + * @param _vrfCoordinator address of VRFCoordinator contract + * @param _link address of LINK token contract + * + * @dev https://docs.chain.link/docs/link-token-contracts + */ + constructor( + address _vrfCoordinator, + address _link + ) { + vrfCoordinator = _vrfCoordinator; + LINK = LinkTokenInterface(_link); + } + + // rawFulfillRandomness is called by VRFCoordinator when it receives a valid VRF + // proof. rawFulfillRandomness then calls fulfillRandomness, after validating + // the origin of the call + function rawFulfillRandomness( + bytes32 requestId, + uint256 randomness + ) + external + { + require(msg.sender == vrfCoordinator, "Only VRFCoordinator can fulfill"); + fulfillRandomness(requestId, randomness); + } +} \ No newline at end of file diff --git a/community/lootbox_hackathon/mul_lottery/ChainLinkIntegration/VRFRequestIDBase.sol b/community/lootbox_hackathon/mul_lottery/ChainLinkIntegration/VRFRequestIDBase.sol new file mode 100644 index 0000000000..2bb73e5 --- /dev/null +++ b/community/lootbox_hackathon/mul_lottery/ChainLinkIntegration/VRFRequestIDBase.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract VRFRequestIDBase { + + /** + * @notice returns the seed which is actually input to the VRF coordinator + * + * @dev To prevent repetition of VRF output due to repetition of the + * @dev user-supplied seed, that seed is combined in a hash with the + * @dev user-specific nonce, and the address of the consuming contract. The + * @dev risk of repetition is mostly mitigated by inclusion of a blockhash in + * @dev the final seed, but the nonce does protect against repetition in + * @dev requests which are included in a single block. + * + * @param _userSeed VRF seed input provided by user + * @param _requester Address of the requesting contract + * @param _nonce User-specific nonce at the time of the request + */ + function makeVRFInputSeed( + bytes32 _keyHash, + uint256 _userSeed, + address _requester, + uint256 _nonce + ) + internal + pure + returns ( + uint256 + ) + { + return uint256(keccak256(abi.encode(_keyHash, _userSeed, _requester, _nonce))); + } + + /** + * @notice Returns the id for this request + * @param _keyHash The serviceAgreement ID to be used for this request + * @param _vRFInputSeed The seed to be passed directly to the VRF + * @return The id for this request + * + * @dev Note that _vRFInputSeed is not the seed passed by the consuming + * @dev contract, but the one generated by makeVRFInputSeed + */ + function makeRequestId( + bytes32 _keyHash, + uint256 _vRFInputSeed + ) + internal + pure + returns ( + bytes32 + ) + { + return keccak256(abi.encodePacked(_keyHash, _vRFInputSeed)); + } +} \ No newline at end of file diff --git a/community/lootbox_hackathon/mul_lottery/lootbox_lottery.sol b/community/lootbox_hackathon/mul_lottery/lootbox_lottery.sol new file mode 100644 index 0000000000..42a5380 --- /dev/null +++ b/community/lootbox_hackathon/mul_lottery/lootbox_lottery.sol @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.6; +import "./ChainLinkIntegration/VRFConsumerBase.sol"; + +interface ILootBox { + function afterHarbergerBuy(uint256 _tokenId, address _newOwner) external; +} + +interface ERC20 { + function balanceOf(address who) external view returns (uint256); + function transfer(address to, uint256 value) external returns (bool); + function transferFrom(address from, address to, uint256 value) external returns (bool); +} + +contract LotteryLootBox is ILootBox, VRFConsumerBase { + address[] public players; + address public rewardToken; + address public owner; + address public buidlNFT; + uint256 public mintTokenId; + address public lastWinner; + uint256 public random; + bytes32 internal keyHash; + bytes32 public requestID; + uint256 internal fee; + uint256 public prizePool; + + + enum LOTTERYSTATE { + OPEN, + CLOSED, + CALCULATING + } + + LOTTERYSTATE public lotterystate; + event ReqRandomness(bytes32 indexed requestId); + event RequestRandomnessFulfilled(bytes32 indexed requestId, uint256 randomness); + + //CHAINLINK randomaizer integration + //settings for ethereum mainnet network https://docs.chain.link/docs/vrf-contracts/ + address public _VRFCoordinator = 0xf0d54349aDdcf704F77AE15b96510dEA15cb7952; + address public _LinkToken = 0x514910771AF9Ca656af840dff83E8264EcF986CA; + bytes32 internal _keyHash = 0xAA77729D3466CA35AE8D28B3BBAC7CC36A5031EFDC430821C02BC31A238AF445; + uint256 internal _fee = 2 * 10**18; // 2 LINK !! Don't forget send LINK and ETH for fees on this contract !! + + //DORA token integration and BUILD NFT data + address public _rewardToken = 0xbc4171f45EF0EF66E76F979dF021a34B46DCc81d; + address public _buidlNFT = 0x7D5256D3e0c340FaBa6d6Ecd27E7B9e07f76aa94; + uint256 public _mintTokenId = 1474; + + constructor() VRFConsumerBase(_VRFCoordinator, _LinkToken) { + owner = msg.sender; + rewardToken = _rewardToken; + buidlNFT = _buidlNFT; + mintTokenId = _mintTokenId; + lotterystate = LOTTERYSTATE.CLOSED; + prizePool = 0; + //LINK settings + keyHash = _keyHash; + fee = _fee; + + } + + function getLastWinner() public view returns (address) { + return lastWinner; + } + + function getPrizePool() public view returns (uint256) { + return prizePool; + } + + function PlayersCount() public view returns (uint256) { + return players.length; + } + + function getPlayer(uint256 index) public view returns (address) { + return players[index]; + } + + function launchLottery(uint256 _prizePool) public { + require(msg.sender == owner); + require(lotterystate == LOTTERYSTATE.CLOSED); + require(ERC20(rewardToken).transferFrom(owner, address(this), _prizePool)); + prizePool = _prizePool; + lotterystate = LOTTERYSTATE.OPEN; + } + + // Emergency withdrawals --> + function emergencyStop(uint256 _prizePool) public { + require(msg.sender == owner); + require(ERC20(rewardToken).balanceOf(address(this)) >= _prizePool); + require(ERC20(rewardToken).transfer(msg.sender, _prizePool)); + lotterystate = LOTTERYSTATE.CLOSED; + players = new address[](0); + } + + function emergencyWithdrawETH(uint256 _amount) public { + require(msg.sender == owner); + payable(msg.sender).transfer(_amount); + } + + function emergencyWithdrawLINK(uint256 _amount) public { + require(msg.sender == owner); + require(LINK.transfer(msg.sender, _amount)); + } + // <-- + + function stopLottery() public returns (bytes32){ + require(msg.sender == owner); + require(lotterystate == LOTTERYSTATE.OPEN); + require(LINK.balanceOf(address(this)) >= fee, "Not enough LINK"); + lotterystate = LOTTERYSTATE.CALCULATING; + bytes32 requestId = requestRandomness(keyHash, fee); + emit ReqRandomness(requestId); + return requestId; + } + + function fulfillRandomness(bytes32 _requestId, uint256 _randomness) internal override { + require(lotterystate == LOTTERYSTATE.CALCULATING); + require(_randomness > 0); + uint256 Win_idx = _randomness % players.length; + lastWinner = players[Win_idx]; + require(ERC20(rewardToken).transfer(lastWinner, prizePool)); + players = new address[](0); + lotterystate = LOTTERYSTATE.CLOSED; + random = _randomness; + emit RequestRandomnessFulfilled(_requestId, _randomness); + } + + function afterHarbergerBuy(uint256 _tokenId, address _newOwner) override external { + require(msg.sender == buidlNFT); + require(_tokenId == mintTokenId); + if (lotterystate == LOTTERYSTATE.OPEN) { + players.push(_newOwner); + } + } + +} \ No newline at end of file