Documentation |
diff --git a/packages/hardhat/contracts/BuyMeACeptor.sol b/packages/hardhat/contracts/BuyMeACeptor.sol
new file mode 100644
index 0000000..fcbb20f
--- /dev/null
+++ b/packages/hardhat/contracts/BuyMeACeptor.sol
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: MIT
+pragma solidity >=0.8.0 <0.9.0;
+
+/**
+ * @title World
+ * @dev World struct
+ */
+struct World {
+ string vibe;
+ string gameMasterName;
+ string gameMasterTwitterHandle;
+ string description;
+ uint256 time;
+ address gameMasterAddress;
+}
+
+/**
+ * @title BuyMeACeptorWorld
+ * @dev BuyMeACeptorWorld contract to accept donations and for our users to create a world for us
+ */
+contract BuyMeACeptor{
+ address payable public owner;
+ uint256 public price;
+ World[] public worlds;
+
+ error InsufficientFunds();
+ error InvalidArguments(string message);
+ error OnlyOwner();
+
+ event BuyMeACeptorWorldEvent(address indexed buyer, uint256 price);
+ event NewWorld(address indexed gameMasterAddress, uint256 time, string vibe, string gameMasterName, string gameMasterTwitterHandle, string description);
+
+ constructor() {
+ owner = payable(msg.sender);
+ price = 0.0001 ether;
+ }
+
+ /**
+ * WRITE FUNCTIONS *************
+ */
+
+ /**
+ * @dev Function to buy a world
+ * @param gameMasterName The name of the game master
+ * @param gameMasterTwitterHandle The Twitter handle of the game master
+ * @param description The description of the world
+ * (Note: Using calldata for gas efficiency)
+ */
+ function buyWorld(string calldata vibe, string calldata gameMasterName, string calldata gameMasterTwitterHandle, string calldata description) public payable {
+ if (msg.value < price) {
+ revert InsufficientFunds();
+ }
+
+ emit BuyMeACeptorWorldEvent(msg.sender, msg.value);
+
+ if (bytes(gameMasterName).length == 0 && bytes(description).length == 0) {
+ revert InvalidArguments("Invalid gameMasterName or description");
+ }
+
+ worlds.push(World(vibe, gameMasterName, gameMasterTwitterHandle, description, block.timestamp, msg.sender));
+
+ emit NewWorld(msg.sender, block.timestamp, vibe, gameMasterName, gameMasterTwitterHandle, description);
+ }
+
+ /**
+ * @dev Function to remove a world
+ * @param index The index of the world
+ */
+ function removeWorld(uint256 index) public {
+ if (index >= worlds.length) {
+ revert InvalidArguments("Invalid index");
+ }
+
+ World memory world = worlds[index];
+
+ // if operation isnt sent from the same game master or the owner, then not allowed
+ if (world.gameMasterAddress != msg.sender && msg.sender != owner) {
+ revert InvalidArguments("Operation not allowed");
+ }
+
+ World memory indexWorld = worlds[index];
+ worlds[index] = worlds[worlds.length - 1];
+ worlds[worlds.length - 1] = indexWorld;
+ worlds.pop();
+ }
+
+ /**
+ * @dev Function to modify a world description
+ * @param index The index of the world
+ * @param description The description of the world
+ */
+ function modifyWorldDescription(uint256 index, string memory description) public {
+ if (index >= worlds.length) {
+ revert InvalidArguments("Invalid index");
+ }
+
+ World memory world = worlds[index];
+
+ if (world.gameMasterAddress != msg.sender || msg.sender != owner) {
+ revert InvalidArguments("Operation not allowed");
+ }
+
+ worlds[index].description = description;
+ }
+
+ /**
+ * @dev Function to withdraw the balance
+ */
+ function withdrawTips() public {
+ if (msg.sender != owner) {
+ revert OnlyOwner();
+ }
+
+ if (address(this).balance == 0) {
+ revert InsufficientFunds();
+ }
+
+ (bool sent,) = owner.call{value: address(this).balance}("");
+ require(sent, "Failed to send Ether");
+ }
+
+ /**
+ * READ FUNCTIONS *************
+ */
+
+ /**
+ * @dev Function to get the worlds
+ */
+ function getWorlds() public view returns (World[] memory) {
+ return worlds;
+ }
+
+ /**
+ * @dev Recieve function to accept ether
+ */
+ receive() external payable {}
+}
diff --git a/packages/hardhat/contracts/BuyMeACoffee.sol b/packages/hardhat/contracts/BuyMeACoffee.sol
deleted file mode 100644
index 2e09998..0000000
--- a/packages/hardhat/contracts/BuyMeACoffee.sol
+++ /dev/null
@@ -1,156 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.0 <0.9.0;
-/**
- * ----------------------------------------------------------------------------------------------------------------
- * ---------██████╗ ██╗ ██╗██╗██╗ ██████╗ ██████╗ ███╗ ██╗ ██████╗██╗ ██╗ █████╗ ██╗███╗ ██╗-----
- * ---------██╔══██╗██║ ██║██║██║ ██╔══██╗ ██╔═══██╗████╗ ██║██╔════╝██║ ██║██╔══██╗██║████╗ ██║-----
- * ---------██████╔╝██║ ██║██║██║ ██║ ██║█████╗██║ ██║██╔██╗ ██║██║ ███████║███████║██║██╔██╗ ██║-----
- * ---------██╔══██╗██║ ██║██║██║ ██║ ██║╚════╝██║ ██║██║╚██╗██║██║ ██╔══██║██╔══██║██║██║╚██╗██║-----
- * ---------██████╔╝╚██████╔╝██║███████╗██████╔╝ ╚██████╔╝██║ ╚████║╚██████╗██║ ██║██║ ██║██║██║ ╚████║-----
- * ---------╚═════╝ ╚═════╝ ╚═╝╚══════╝╚═════╝ ╚═════╝ ╚═╝ ╚═══╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝-----
- * ----------------------------------------------------------------------------------------------------------------
- * https://github.com/coinbase/build-onchain-apps
- *
- * Disclaimer: The provided Solidity contracts are intended solely for educational purposes and are
- * not warranted for any specific use. They have not been audited and may contain vulnerabilities, hence should
- * not be deployed in production environments. Users are advised to seek professional review and conduct a
- * comprehensive security audit before any real-world application to mitigate risks of financial loss or other
- * consequences. The author(s) disclaim all liability for any damages arising from the use of these contracts.
- * Use at your own risk, acknowledging the inherent risks of smart contract technology on the blockchain.
- *
- */
-
-/**
- * @title Memos
- * @dev Memo struct
- */
-struct Memo {
- uint numCoffees;
- string userName;
- string twitterHandle;
- string message;
- uint256 time;
- address userAddress;
-}
-
-/**
- * @title BuyMeACoffee
- * @dev BuyMeACoffee contract to accept donations and for our users to leave a memo for us
- */
-contract BuyMeACoffee {
- address payable public owner;
- uint256 public price;
- Memo[] public memos;
-
- error InsufficientFunds();
- error InvalidArguments(string message);
- error OnlyOwner();
-
- event BuyMeACoffeeEvent(address indexed buyer, uint256 price);
- event NewMemo(address indexed userAddress, uint256 time, uint numCoffees, string userName, string twitterHandle, string message);
-
- constructor() {
- owner = payable(msg.sender);
- price = 0.0001 ether;
- }
-
- /**
- * WRITE FUNCTIONS *************
- */
-
- /**
- * @dev Function to buy a coffee
- * @param userName The name of the user
- * @param twitterHandle The Twitter handle of the user
- * @param message The message of the user
- * (Note: Using calldata for gas efficiency)
- */
- function buyCoffee(uint numCoffees, string calldata userName, string calldata twitterHandle, string calldata message) public payable {
- if (msg.value < price*numCoffees) {
- revert InsufficientFunds();
- }
-
- emit BuyMeACoffeeEvent(msg.sender, msg.value);
-
- if (bytes(userName).length == 0 && bytes(message).length == 0) {
- revert InvalidArguments("Invalid userName or message");
- }
-
- memos.push(Memo(numCoffees, userName, twitterHandle, message, block.timestamp, msg.sender));
-
- emit NewMemo(msg.sender, block.timestamp, numCoffees, userName, twitterHandle, message);
- }
-
- /**
- * @dev Function to remove a memo
- * @param index The index of the memo
- */
- function removeMemo(uint256 index) public {
- if (index >= memos.length) {
- revert InvalidArguments("Invalid index");
- }
-
- Memo memory memo = memos[index];
-
- // if operation isnt sent from the same user or the owner, then not allowed
- if (memo.userAddress != msg.sender && msg.sender != owner) {
- revert InvalidArguments("Operation not allowed");
- }
-
- Memo memory indexMemo = memos[index];
- memos[index] = memos[memos.length - 1];
- memos[memos.length - 1] = indexMemo;
- memos.pop();
- }
-
- /**
- * @dev Function to modify a memo
- * @param index The index of the memo
- * @param message The message of the memo
- */
- function modifyMemoMessage(uint256 index, string memory message) public {
- if (index >= memos.length) {
- revert InvalidArguments("Invalid index");
- }
-
- Memo memory memo = memos[index];
-
- if (memo.userAddress != msg.sender || msg.sender != owner) {
- revert InvalidArguments("Operation not allowed");
- }
-
- memos[index].message = message;
- }
-
- /**
- * @dev Function to withdraw the balance
- */
- function withdrawTips() public {
- if (msg.sender != owner) {
- revert OnlyOwner();
- }
-
- if (address(this).balance == 0) {
- revert InsufficientFunds();
- }
-
- (bool sent,) = owner.call{value: address(this).balance}("");
- require(sent, "Failed to send Ether");
- }
-
- /**
- * READ FUNCTIONS *************
- */
-
- /**
- * @dev Function to get the memos
- */
- function getMemos() public view returns (Memo[] memory) {
- return memos;
- }
-
- /**
- * @dev Recieve function to accept ether
- */
- receive() external payable {}
-}
diff --git a/packages/hardhat/contracts/CeptorCS.txt b/packages/hardhat/contracts/CeptorCS.txt
new file mode 100644
index 0000000..01ea2ed
--- /dev/null
+++ b/packages/hardhat/contracts/CeptorCS.txt
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.19;
+
+import "@openzeppelin/contracts/access/Ownable.sol";
+import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
+import "@chainlink/contracts/src/v0.8/vrf/VRFConsumerBaseV2.sol";
+
+contract CeptorCharacterSheets is ERC721URIStorage, VRFConsumerBaseV2, Ownable {
+ struct Stats {
+ uint8 strength;
+ uint8 dexterity;
+ uint8 constitution;
+ uint8 intelligence;
+ uint8 wisdom;
+ uint8 charisma;
+ uint8 luck;
+ }
+
+ struct Character {
+ Stats stats;
+ string name;
+ uint swapsLeft;
+ }
+
+ mapping(address => uint256) public ownerToTokenId;
+ mapping(uint256 => Character) public tokenIdToCharacter;
+
+ uint256 public tokenIdCounter;
+ bytes32 public keyHash;
+ uint256 public fee;
+ uint64 public subscriptionId;
+
+ event CharacterCreated(uint256 indexed tokenId, address owner);
+ event StatsSwapped(uint256 indexed tokenId, address owner);
+
+ constructor(
+ address vrfCoordinator,
+ address linkToken,
+ bytes32 _keyHash,
+ uint64 _subscriptionId
+ )
+ VRFConsumerBaseV2(vrfCoordinator)
+ ERC721("CeptorCharacterSheets", "CCS")
+ {
+ keyHash = _keyHash;
+ fee = 0.1 * 10**18; // Chainlink VRF fee
+ subscriptionId = _subscriptionId;
+ tokenIdCounter = 1;
+ }
+
+ function createCharacter(string memory name) external {
+ require(ownerToTokenId[msg.sender] == 0, "Owner already has a character");
+ uint256 tokenId = tokenIdCounter++;
+ ownerToTokenId[msg.sender] = tokenId;
+ tokenIdToCharacter[tokenId] = Character({
+ name: name,
+ stats: Stats(0, 0, 0, 0, 0, 0, 0),
+ swapsLeft: 3
+ });
+ _safeMint(msg.sender, tokenId);
+ emit CharacterCreated(tokenId, msg.sender);
+ requestStats(tokenId);
+ }
+
+ function requestStats(uint256 tokenId) internal {
+ require(LINK.balanceOf(address(this)) >= fee, "Not enough LINK");
+ requestRandomWords(keyHash, subscriptionId, 3, fee, 1);
+ }
+
+ function fulfillRandomWords(uint256, uint256[] memory randomWords) internal override {
+ uint256 tokenId = ownerToTokenId[msg.sender];
+ Character storage character = tokenIdToCharacter[tokenId];
+ character.stats.strength = uint8(randomWords[0] % 16 + 3);
+ character.stats.dexterity = uint8(randomWords[1] % 16 + 3);
+ character.stats.constitution = uint8(randomWords[2] % 16 + 3);
+ character.stats.intelligence = uint8(randomWords[3] % 16 + 3);
+ character.stats.wisdom = uint8(randomWords[4] % 16 + 3);
+ character.stats.charisma = uint8(randomWords[5] % 16 + 3);
+ character.stats.luck = uint8(randomWords[6] % 16 + 3);
+ }
+
+ function swapStats(uint256 tokenId) external {
+ require(ownerOf(tokenId) == msg.sender, "Not the owner");
+ Character storage character = tokenIdToCharacter[tokenId];
+ require(character.swapsLeft > 0, "No swaps left");
+ character.swapsLeft--;
+ requestStats(tokenId);
+ emit StatsSwapped(tokenId, msg.sender);
+ }
+}
diff --git a/packages/hardhat/contracts/CeptorCharacterGenerator.sol b/packages/hardhat/contracts/CeptorCharacterGenerator.sol
new file mode 100644
index 0000000..95fadec
--- /dev/null
+++ b/packages/hardhat/contracts/CeptorCharacterGenerator.sol
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.7;
+
+import {VRFCoordinatorV2Interface} from "@chainlink/contracts/src/v0.8/vrf/interfaces/VRFCoordinatorV2Interface.sol";
+import {VRFConsumerBaseV2} from "@chainlink/contracts/src/v0.8/vrf/VRFConsumerBaseV2.sol";
+import {ConfirmedOwner} from "@chainlink/contracts/src/v0.8/shared/access/ConfirmedOwner.sol";
+
+contract DnDCharacterGenerator is VRFConsumerBaseV2, ConfirmedOwner {
+ VRFCoordinatorV2Interface COORDINATOR;
+ uint64 s_subscriptionId;
+ bytes32 keyHash = 0x474e34a077df58807dbe9c96d3c009b23b3c6d0cce433e59bbf5b34f823bc56c;
+ // forge-test gas report & gas limit plugin on hardhat
+ uint32 callbackGasLimit = 100000;
+ uint16 requestConfirmations = 3;
+ uint32 numWords = 7; // 6 ability scores + 1 for class
+
+ struct Character {
+ uint256[6] abilities;
+ string class;
+ string name;
+ string alignment;
+ string background;
+ uint8 swaps;
+ }
+
+ mapping(uint256 => address) requestToSender;
+ mapping(address => Character) public characters;
+
+ event CharacterCreated(address owner, uint256 requestId);
+ event CharacterUpdated(address owner, string name, string alignment, string background);
+ event ScoresSwapped(address owner);
+ event RequestFulfilled(uint256 requestId, uint256[] randomWords);
+
+ constructor(uint64 subscriptionId) VRFConsumerBaseV2(0x8103B0A8A00be2DDC778e6e7eaa21791Cd364625)
+ ConfirmedOwner(msg.sender) {
+ COORDINATOR = VRFCoordinatorV2Interface(0x8103B0A8A00be2DDC778e6e7eaa21791Cd364625);
+ s_subscriptionId = subscriptionId;
+ }
+
+ function createCharacter() external onlyOwner {
+ require(characters[msg.sender].abilities[0] == 0, "Character already created");
+ uint256 requestId = COORDINATOR.requestRandomWords(
+ keyHash,
+ s_subscriptionId,
+ requestConfirmations,
+ callbackGasLimit,
+ numWords
+ );
+ requestToSender[requestId] = msg.sender;
+ emit CharacterCreated(msg.sender, requestId);
+ }
+
+ function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override {
+ address owner = requestToSender[requestId];
+ uint256[6] memory abilities;
+ for (uint i = 0; i < 6; ++i) { // ++i saves 2 gas
+ abilities[i] = (randomWords[i] % 16) + 3; // Score range: 3-18
+ }
+ characters[owner] = Character({
+ abilities: abilities,
+ class: getClass(randomWords[6]),
+ name: "",
+ alignment: "",
+ background: "",
+ swaps: 0
+ });
+ emit RequestFulfilled(requestId, randomWords);
+ }
+
+ function updateCharacterDetails(string calldata name, string calldata alignment, string calldata background) external {
+ require(characters[msg.sender].abilities[0] != 0, "Character not created");
+ characters[msg.sender].name = name;
+ characters[msg.sender].alignment = alignment;
+ characters[msg.sender].background = background;
+ emit CharacterUpdated(msg.sender, name, alignment, background);
+ }
+
+ function swapScores(uint8 index1, uint8 index2) external {
+ require(characters[msg.sender].swaps < 3, "Max swaps reached");
+ require(index1 < 6 && index2 < 6, "Invalid index");
+
+ (characters[msg.sender].abilities[index1], characters[msg.sender].abilities[index2]) =
+ (characters[msg.sender].abilities[index2], characters[msg.sender].abilities[index1]);
+ characters[msg.sender].swaps++;
+ emit ScoresSwapped(msg.sender);
+ }
+
+ function getClass(uint256 randomNumber) private pure returns (string memory) {
+ string[12] memory classes = ["Barbarian", "Bard", "Cleric", "Druid", "Fighter", "Monk", "Paladin", "Ranger", "Rogue", "Sorcerer", "Warlock", "Wizard"];
+ return classes[randomNumber % classes.length];
+ }
+}
diff --git a/packages/hardhat/contracts/CharacterSheets.txt b/packages/hardhat/contracts/CharacterSheets.txt
new file mode 100644
index 0000000..0eb0de6
--- /dev/null
+++ b/packages/hardhat/contracts/CharacterSheets.txt
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: MIT
+// An example of a consumer contract that relies on a subscription for funding.
+pragma solidity ^0.8.7;
+
+// Useful for debugging. Remove when deploying to a live network.
+import "hardhat/console.sol";
+
+// Use openzeppelin to inherit battle-tested implementations (ERC20, ERC721, etc)
+import "@openzeppelin/contracts/access/Ownable.sol";
+import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
+import {VRFCoordinatorV2Interface} from "@chainlink/contracts/src/v0.8/vrf/interfaces/VRFCoordinatorV2Interface.sol";
+import {VRFConsumerBaseV2} from "@chainlink/contracts/src/v0.8/vrf/VRFConsumerBaseV2.sol";
+import {ConfirmedOwner} from "@chainlink/contracts/src/v0.8/shared/access/ConfirmedOwner.sol";
+
+/**
+ * Request testnet LINK and ETH here: https://faucets.chain.link/
+ * Find information on LINK Token Contracts and get the latest ETH and LINK faucets here: https://docs.chain.link/docs/link-token-contracts/
+ */
+
+/**
+ * THIS IS AN EXAMPLE CONTRACT THAT USES HARDCODED VALUES FOR CLARITY.
+ * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE.
+ * DO NOT USE THIS CODE IN PRODUCTION.
+ */
+
+contract VRFv2Consumer is VRFConsumerBaseV2, ConfirmedOwner {
+ event RequestSent(uint256 requestId, uint32 numWords);
+ event RequestFulfilled(uint256 requestId, uint256[] randomWords);
+
+ struct RequestStatus {
+ bool fulfilled; // whether the request has been successfully fulfilled
+ bool exists; // whether a requestId exists
+ uint256[] randomWords;
+ }
+ mapping(uint256 => RequestStatus)
+ public s_requests; /* requestId --> requestStatus */
+ VRFCoordinatorV2Interface COORDINATOR;
+
+ // Your subscription ID.
+ uint64 s_subscriptionId;
+
+ // past requests Id.
+ uint256[] public requestIds;
+ uint256 public lastRequestId;
+
+ // The gas lane to use, which specifies the maximum gas price to bump to.
+ // For a list of available gas lanes on each network,
+ // see https://docs.chain.link/docs/vrf/v2/subscription/supported-networks/#configurations
+ bytes32 keyHash =
+ 0x474e34a077df58807dbe9c96d3c009b23b3c6d0cce433e59bbf5b34f823bc56c;
+
+ // Depends on the number of requested values that you want sent to the
+ // fulfillRandomWords() function. Storing each word costs about 20,000 gas,
+ // so 100,000 is a safe default for this example contract. Test and adjust
+ // this limit based on the network that you select, the size of the request,
+ // and the processing of the callback request in the fulfillRandomWords()
+ // function.
+ uint32 callbackGasLimit = 100000;
+
+ // The default is 3, but you can set this higher.
+ uint16 requestConfirmations = 3;
+
+ // For this example, retrieve 2 random values in one request.
+ // Cannot exceed VRFCoordinatorV2.MAX_NUM_WORDS.
+ uint32 numWords = 2;
+
+ /**
+ * HARDCODED FOR SEPOLIA
+ * COORDINATOR: 0x8103B0A8A00be2DDC778e6e7eaa21791Cd364625
+ */
+ constructor(
+ uint64 subscriptionId
+ )
+ VRFConsumerBaseV2(0x8103B0A8A00be2DDC778e6e7eaa21791Cd364625)
+ ConfirmedOwner(msg.sender)
+ {
+ COORDINATOR = VRFCoordinatorV2Interface(
+ 0x8103B0A8A00be2DDC778e6e7eaa21791Cd364625
+ );
+ s_subscriptionId = subscriptionId;
+ }
+
+ // Assumes the subscription is funded sufficiently.
+ function requestRandomWords()
+ external
+ onlyOwner
+ returns (uint256 requestId)
+ {
+ // Will revert if subscription is not set and funded.
+ requestId = COORDINATOR.requestRandomWords(
+ keyHash,
+ s_subscriptionId,
+ requestConfirmations,
+ callbackGasLimit,
+ numWords
+ );
+ s_requests[requestId] = RequestStatus({
+ randomWords: new uint256[](0),
+ exists: true,
+ fulfilled: false
+ });
+ requestIds.push(requestId);
+ lastRequestId = requestId;
+ emit RequestSent(requestId, numWords);
+ return requestId;
+ }
+
+ function fulfillRandomWords(
+ uint256 _requestId,
+ uint256[] memory _randomWords
+ ) internal override {
+ require(s_requests[_requestId].exists, "request not found");
+ s_requests[_requestId].fulfilled = true;
+ s_requests[_requestId].randomWords = _randomWords;
+ emit RequestFulfilled(_requestId, _randomWords);
+ }
+
+ function getRequestStatus(
+ uint256 _requestId
+ ) external view returns (bool fulfilled, uint256[] memory randomWords) {
+ require(s_requests[_requestId].exists, "request not found");
+ RequestStatus memory request = s_requests[_requestId];
+ return (request.fulfilled, request.randomWords);
+ }
+}
diff --git a/packages/hardhat/contracts/DynamicHooty.sol b/packages/hardhat/contracts/DynamicHooty.sol
new file mode 100644
index 0000000..39bbe4c
--- /dev/null
+++ b/packages/hardhat/contracts/DynamicHooty.sol
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: MIT
+// An example of a consumer contract that relies on a subscription for funding.
+pragma solidity ^0.8.7;
+
+import {VRFCoordinatorV2Interface} from "@chainlink/contracts/src/v0.8/vrf/interfaces/VRFCoordinatorV2Interface.sol";
+import {VRFConsumerBaseV2} from "@chainlink/contracts/src/v0.8/vrf/VRFConsumerBaseV2.sol";
+import {ConfirmedOwner} from "@chainlink/contracts/src/v0.8/shared/access/ConfirmedOwner.sol";
+
+/**
+ * Request testnet LINK and ETH here: https://faucets.chain.link/
+ * Find information on LINK Token Contracts and get the latest ETH and LINK faucets here: https://docs.chain.link/docs/link-token-contracts/
+ */
+
+/**
+ * THIS IS AN EXAMPLE CONTRACT THAT USES HARDCODED VALUES FOR CLARITY.
+ * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE.
+ * DO NOT USE THIS CODE IN PRODUCTION.
+ */
+
+contract VRFv2Consumer is VRFConsumerBaseV2, ConfirmedOwner {
+ event RequestSent(uint256 requestId, uint32 numWords);
+ event RequestFulfilled(uint256 requestId, uint256[] randomWords);
+
+ struct RequestStatus {
+ bool fulfilled; // whether the request has been successfully fulfilled
+ bool exists; // whether a requestId exists
+ uint256[] randomWords;
+ }
+ mapping(uint256 => RequestStatus)
+ public s_requests; /* requestId --> requestStatus */
+ VRFCoordinatorV2Interface COORDINATOR;
+
+ // Your subscription ID.
+ uint64 s_subscriptionId;
+
+ // past requests Id.
+ uint256[] public requestIds;
+ uint256 public lastRequestId;
+
+ // The gas lane to use, which specifies the maximum gas price to bump to.
+ // For a list of available gas lanes on each network,
+ // see https://docs.chain.link/docs/vrf/v2/subscription/supported-networks/#configurations
+ bytes32 keyHash =
+ 0x474e34a077df58807dbe9c96d3c009b23b3c6d0cce433e59bbf5b34f823bc56c;
+
+ // Depends on the number of requested values that you want sent to the
+ // fulfillRandomWords() function. Storing each word costs about 20,000 gas,
+ // so 100,000 is a safe default for this example contract. Test and adjust
+ // this limit based on the network that you select, the size of the request,
+ // and the processing of the callback request in the fulfillRandomWords()
+ // function.
+ uint32 callbackGasLimit = 100000;
+
+ // The default is 3, but you can set this higher.
+ uint16 requestConfirmations = 3;
+
+ // For this example, retrieve 2 random values in one request.
+ // Cannot exceed VRFCoordinatorV2.MAX_NUM_WORDS.
+ uint32 numWords = 2;
+
+ /**
+ * HARDCODED FOR SEPOLIA
+ * COORDINATOR: 0x8103B0A8A00be2DDC778e6e7eaa21791Cd364625
+ */
+ constructor(
+ uint64 subscriptionId
+ )
+ VRFConsumerBaseV2(0x8103B0A8A00be2DDC778e6e7eaa21791Cd364625)
+ ConfirmedOwner(msg.sender)
+ {
+ COORDINATOR = VRFCoordinatorV2Interface(
+ 0x8103B0A8A00be2DDC778e6e7eaa21791Cd364625
+ );
+ s_subscriptionId = subscriptionId;
+ }
+
+ // Assumes the subscription is funded sufficiently.
+ function requestRandomWords()
+ external
+ onlyOwner
+ returns (uint256 requestId)
+ {
+ // Will revert if subscription is not set and funded.
+ requestId = COORDINATOR.requestRandomWords(
+ keyHash,
+ s_subscriptionId,
+ requestConfirmations,
+ callbackGasLimit,
+ numWords
+ );
+ s_requests[requestId] = RequestStatus({
+ randomWords: new uint256[](0),
+ exists: true,
+ fulfilled: false
+ });
+ requestIds.push(requestId);
+ lastRequestId = requestId;
+ emit RequestSent(requestId, numWords);
+ return requestId;
+ }
+
+ function fulfillRandomWords(
+ uint256 _requestId,
+ uint256[] memory _randomWords
+ ) internal override {
+ require(s_requests[_requestId].exists, "request not found");
+ s_requests[_requestId].fulfilled = true;
+ s_requests[_requestId].randomWords = _randomWords;
+ emit RequestFulfilled(_requestId, _randomWords);
+ }
+
+ function getRequestStatus(
+ uint256 _requestId
+ ) external view returns (bool fulfilled, uint256[] memory randomWords) {
+ require(s_requests[_requestId].exists, "request not found");
+ RequestStatus memory request = s_requests[_requestId];
+ return (request.fulfilled, request.randomWords);
+ }
+}
diff --git a/packages/hardhat/contracts/YourContract.sol b/packages/hardhat/contracts/YourContract.sol
index 3d364a0..ccd9a12 100644
--- a/packages/hardhat/contracts/YourContract.sol
+++ b/packages/hardhat/contracts/YourContract.sol
@@ -70,7 +70,15 @@ contract YourContract {
// emit: keyword used to trigger an event
emit GreetingChange(msg.sender, _newGreeting, msg.value > 0, msg.value);
}
+// Mapping from address to number
+mapping(address => uint) public userNumbers;
+event NumberUpdated(address indexed user, uint number);
+// Function to store a number
+function storeNumber(uint _number) public {
+ userNumbers[msg.sender] = _number;
+ emit NumberUpdated(msg.sender, _number);
+ }
/**
* Function that allows the owner to withdraw all the Ether in the contract
* The function can only be called by the owner of the contract as defined by the isOwner modifier
diff --git a/packages/hardhat/contracts/readme.md b/packages/hardhat/contracts/readme.md
new file mode 100644
index 0000000..93eb659
--- /dev/null
+++ b/packages/hardhat/contracts/readme.md
@@ -0,0 +1,14 @@
+# Games Contracts
+
+1. [Game World Generator](BuyMeACeptor.sol) - A contract that generates a game world based on a user's vibe and number of players. The world is generated with a visual of the planet, scenarios, locations, descriptions, maps, denizens, secrets, goals, and players. Each world has its own blockchain. Creating a World costs 10 gameTokens. when creating a game, i want to have my own world or play with others. each world should be locked to a blockchain. 10 gT to make a world. 5 gT to join one as a GM, 2 gT to join as player.
+
+Inside worlds, there are games
+inside games there are schedules
+inside schedules there are sessions
+(and we verify who shows up)
+
+1. [Character Generator](CeptorCharacterGenerator.sol) - A contract that generates a character for a user in the game world. The character is generated with abilities, class, name, alignment, and background. Each character has its own unique attributes. Creating a Character is only allowed by the owner of the contract. This is a mistake, and will be replaced in upgrade to VRF2.5
+
+2. Is the World Generator deploying a World contract? Yes. Is the World contract tracking all its games, or deploying each game as its own contract which tracks the sessions. Verifiable Truth.
+
+3. NPC Generator - Unlike the PCG which is usable by any Verified Credential having hooty in their hey hey. The NPCG is a contract that generates a non-player character for a user in the game world. The character is generated with abilities, class, name, alignment, hometown, and background. Each character has its own unique attributes. Creating a Character is only allowed by the owner of the contract. VRF2.5 because reusable code choices.
\ No newline at end of file
diff --git a/packages/hardhat/deploy/01_deploy_buy_me_a_coffee.ts b/packages/hardhat/deploy/01_deploy_buy_me_a_ceptor.ts
similarity index 75%
rename from packages/hardhat/deploy/01_deploy_buy_me_a_coffee.ts
rename to packages/hardhat/deploy/01_deploy_buy_me_a_ceptor.ts
index 474d826..fd61c54 100644
--- a/packages/hardhat/deploy/01_deploy_buy_me_a_coffee.ts
+++ b/packages/hardhat/deploy/01_deploy_buy_me_a_ceptor.ts
@@ -3,12 +3,12 @@ import { DeployFunction } from "hardhat-deploy/types";
import { Contract } from "ethers";
/**
- * Deploys a contract named "BuyMeACoffee" using the deployer account and
+ * Deploys a contract named "BuyMeACeptor" using the deployer account and
* constructor arguments set to the deployer address
*
* @param hre HardhatRuntimeEnvironment object.
*/
-const deployBuyMeACoffee: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
+const deployBuyMeACeptor: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
/*
On localhost, the deployer account is the one that comes with Hardhat, which is already funded.
@@ -22,7 +22,7 @@ const deployBuyMeACoffee: DeployFunction = async function (hre: HardhatRuntimeEn
const { deployer } = await hre.getNamedAccounts();
const { deploy } = hre.deployments;
- await deploy("BuyMeACoffee", {
+ await deploy("BuyMeACeptor", {
from: deployer,
// Contract constructor arguments
log: true,
@@ -32,12 +32,12 @@ const deployBuyMeACoffee: DeployFunction = async function (hre: HardhatRuntimeEn
});
// Get the deployed contract to interact with it after deploying.
- const buyMeACoffeeContract = await hre.ethers.getContract("BuyMeACoffee", deployer);
- console.log("👋 Buy this person a coffee!", await buyMeACoffeeContract.owner());
+ const buyMeACeptorContract = await hre.ethers.getContract("BuyMeACeptor", deployer);
+ console.log("👋 Buy this person a Ceptor!", await buyMeACeptorContract.owner());
};
-export default deployBuyMeACoffee;
+export default deployBuyMeACeptor;
// Tags are useful if you have multiple deploy files and only want to run one of them.
-// e.g. yarn deploy --tags BuyMeACoffee
-deployBuyMeACoffee.tags = ["BuyMeACoffee"];
+// e.g. yarn deploy --tags BuyMeACeptor
+deployBuyMeACeptor.tags = ["BuyMeACeptor"];
diff --git a/packages/hardhat/package.json b/packages/hardhat/package.json
index 918b5d0..c5a9a2a 100644
--- a/packages/hardhat/package.json
+++ b/packages/hardhat/package.json
@@ -50,6 +50,7 @@
"typescript": "^5.1.6"
},
"dependencies": {
+ "@chainlink/contracts": "^1.1.0",
"@openzeppelin/contracts": "^4.8.1",
"@typechain/ethers-v6": "^0.5.1",
"dotenv": "^16.0.3",
diff --git a/packages/nextjs/app/games/page.tsx b/packages/nextjs/app/games/page.tsx
new file mode 100644
index 0000000..42ab391
--- /dev/null
+++ b/packages/nextjs/app/games/page.tsx
@@ -0,0 +1,35 @@
+"use client";
+
+import type { NextPage } from "next";
+import { useAccount } from "wagmi";
+import CharacterCard from "~~/components/ceptor/CharacterCard";
+import characters from "~~/components/ceptor/CharacterData";
+import { Address } from "~~/components/scaffold-eth";
+
+const Games: NextPage = () => {
+ const { address: connectedAddress } = useAccount();
+
+ return (
+ <>
+