diff --git a/.gitignore b/.gitignore index 6db6ff8..599cd5e 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,8 @@ node_modules # cli dist + +# subgraph +subgraph/build/* +subgraph/generated/* +subgraph/graph-node/data/* diff --git a/README.md b/README.md index 6899542..c2545f9 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,7 @@ # 🧠 Think2Earn ♩ -

- Documentation | - Website -

+ +![DALLE2024-06-0114 30 55-CreateaseriesofminimalisticlogosforacryptoprojectfeaturinganimageofabrainandincorporatingcryptoconceptsliketheEthereumETHlog-ezgif com-webp-to-jpg-converter](https://github.com/ETHPrague-BRX/think2earn/assets/83903147/d74bf393-d6a0-46de-a283-d91018edd5c8) đŸ§Ș Brain Crypto Interfaces suggests the use of incentives for uploading brain data to improve open source classification models. Commmunication privacy and security between users can be leveraged with Waku communication protocols. @@ -92,22 +90,3 @@ yarn start ``` Visit your app on: `http://localhost:3000`. You can interact with your smart contract using the `Debug Contracts` page. You can tweak the app config in `packages/nextjs/scaffold.config.ts`. - -**What's next**: - -- Edit your smart contract `YourContract.sol` in `packages/hardhat/contracts` -- Edit your frontend homepage at `packages/nextjs/app/page.tsx`. For guidance on [routing](https://nextjs.org/docs/app/building-your-application/routing/defining-routes) and configuring [pages/layouts](https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts) checkout the Next.js documentation. -- Edit your deployment scripts in `packages/hardhat/deploy` -- Edit your smart contract test in: `packages/hardhat/test`. To run test use `yarn hardhat:test` - -## Documentation - -Visit our [docs](https://docs.scaffoldeth.io) to learn how to start building with Scaffold-ETH 2. - -To know more about its features, check out our [website](https://scaffoldeth.io). - -## Contributing to Scaffold-ETH 2 - -We welcome contributions to Scaffold-ETH 2! - -Please see [CONTRIBUTING.MD](https://github.com/scaffold-eth/scaffold-eth-2/blob/main/CONTRIBUTING.md) for more information and guidelines for contributing to Scaffold-ETH 2. diff --git a/package.json b/package.json index ece3a4b..8348809 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,8 @@ "workspaces": { "packages": [ "packages/hardhat", - "packages/nextjs" + "packages/nextjs", + "packages/subgraph" ] }, "scripts": { @@ -33,7 +34,15 @@ "postinstall": "husky install", "precommit": "lint-staged", "vercel": "yarn workspace @se-2/nextjs vercel", - "vercel:yolo": "yarn workspace @se-2/nextjs vercel:yolo" + "vercel:yolo": "yarn workspace @se-2/nextjs vercel:yolo", + "subgraph:test": "yarn workspace @se-2/subgraph test", + "stop-node": "yarn workspace @se-2/subgraph stop-node", + "clean-node": "yarn workspace @se-2/subgraph clean-node", + "run-node": "yarn workspace @se-2/subgraph run-node", + "local-create": "yarn workspace @se-2/subgraph local-create", + "local-ship": "yarn workspace @se-2/subgraph local-ship", + "abi-copy": "yarn workspace @se-2/subgraph abi-copy", + "codegen": "yarn workspace @se-2/subgraph codegen" }, "packageManager": "yarn@3.2.3", "devDependencies": { diff --git a/packages/hardhat/contracts/Think2Earn.sol b/packages/hardhat/contracts/Think2Earn.sol index 1784193..a558ac0 100644 --- a/packages/hardhat/contracts/Think2Earn.sol +++ b/packages/hardhat/contracts/Think2Earn.sol @@ -11,12 +11,9 @@ interface Think2EarnBountyRegistry { interface Think2EarnBountyFactoryV1 { // seller methods - // struct Submission {} function submitEEGData(uint256 _bountyId, bytes32 _eegDataHash) external returns (uint256 submissionId); - // function deleteEEGData(uint256 bountyId, uint256 _submissionId, bytes32 _eegDataHash) external; // buyer methods - // struct Bounty{} function createBounty( string memory _name, string memory _description, @@ -25,10 +22,7 @@ interface Think2EarnBountyFactoryV1 { uint256 _judgeTime, uint256 _maxProgress ) external payable; - // function approveSubmissionAndPay(uint256 bountyId, uint256 submissionId) external; // require(msgSender == creator) - // function rejectSubmission(uint256 bountyId, uint256 submissionId) external; // require(msgSender == creator) - function completeBounty(uint256 _bountyId, uint256[] calldata acceptedSubmissions) external; // require(msgSender == creator) - // function sendMedia(uint256 _submissionId, bytes32 _mediaURIHash) external; // this was for the buyer to upload cat pics?? + function completeBounty(uint256 _bountyId, uint256[] calldata acceptedSubmissions) external; // view function getBountyCount() external view returns (uint256); @@ -43,7 +37,8 @@ interface Think2EarnBountyFactoryV1 { address creator, uint256 creationBlock, bool isActive, - uint256 submissionsLength + uint256 submissionsLength, + uint256 currentProgress ); function getBountySubmissions(uint256 _bountyId, uint256 _submission) external view returns (Submission memory); function getActiveBounties() external view returns (uint[] memory); @@ -51,8 +46,6 @@ interface Think2EarnBountyFactoryV1 { struct Submission { address submitter; bytes32 eegDataHash; // Hash of the EEG data - // bool isPaid; - // bool eegDataSubmitted; // Indicates if EEG data has been submitted } } @@ -67,27 +60,21 @@ contract Think2Earn is Think2EarnBountyFactoryV1, ReentrancyGuard { uint256 judgeTime; // in blocks uint256 maxProgress; // max number of participants uint256 creationBlock; - address creator; // msg sender? + address creator; bool isActive; - Submission[] submissions; // Array of submissions for this bounty } uint256[] public activeBountyIds; mapping(uint256 => Bounty) public bounties; - uint256 public bountyCount = 1; // Start counting bounties from 1 + uint256 public bountyCount = 0; // Start counting bounties from 0 -// event MediaSent(uint256 indexed submissionId, bytes32 mediaURIHash, address indexed recipient); event EEGDataSubmitted(uint256 indexed bountyId, uint256 indexed submissionId, bytes32 eegDataHash); event EtherDeposited(address indexed sender, uint256 amount); event PaymentMade(uint256 indexed bountyId, uint256 indexed submissionId, uint256 amount); event BountyCreated(uint256 indexed bountyId, string name, string description, uint256 reward, uint256 duration, uint256 judgeTime, uint256 maxProgress, address indexed creator); event BountyCompleted(uint256 indexed bountyId, uint256 numAcceptedSubmissions); - // constructor(address initialOwner) { - // transferOwnership(initialOwner); - // } - receive() external payable { emit EtherDeposited(msg.sender, msg.value); } @@ -95,47 +82,17 @@ contract Think2Earn is Think2EarnBountyFactoryV1, ReentrancyGuard { function submitEEGData(uint256 _bountyId, bytes32 _eegDataHash) external returns (uint256 submissionId) { require(_eegDataHash != 0, "Invalid EEG data hash"); - bounties[_bountyId].submissions.push (Submission({ + bounties[_bountyId].submissions.push(Submission({ submitter: msg.sender, eegDataHash: _eegDataHash })); - uint256 submissionId = bounties[_bountyId].submissions.length; + submissionId = bounties[_bountyId].submissions.length; emit EEGDataSubmitted(_bountyId, submissionId, _eegDataHash); return submissionId; } -// function approveAndPay(uint256 _submissionId, address _submitterAddress) external onlyOwner nonReentrant { -// Submission storage submission = submissions[_submissionId]; -// require(submission.submitter == _submitterAddress, "Submission ID and address do not match"); -// require(submission.submitter != address(0), "Submission not found"); -// require(!submission.isPaid, "Already paid"); -// require(submission.eegDataSubmitted, "EEG data not submitted"); - -// submission.isPaid = true; -// (bool sent, ) = submission.submitter.call{value: submission.tokenReward}(""); -// require(sent, "Failed to send Ether"); - -// emit PaymentMade(_submissionId, submission.tokenReward); -// } - -// function setReward(uint256 _submissionId, uint256 _reward) external onlyOwner { -// Submission storage submission = submissions[_submissionId]; -// require(!submission.isPaid, "Already paid"); -// submission.tokenReward = _reward; -// } - -// function withdrawEther(address payable _to, uint256 _amount) external onlyOwner nonReentrant { -// require(_amount <= address(this).balance, "Insufficient balance"); -// (bool sent, ) = _to.call{value: _amount}(""); Submission[] memory submissions; -// require(sent, "Failed to send Ether"); -// } - -// function getBalance() public view returns (uint256) { -// return address(this).balance; -// } - function createBounty( string calldata _name, string calldata _description, @@ -146,7 +103,6 @@ contract Think2Earn is Think2EarnBountyFactoryV1, ReentrancyGuard { ) external payable { require(bytes(_name).length > 0, "Bounty name cannot be empty"); require(bytes(_description).length > 0, "Bounty description cannot be empty"); - // require(_reward > 0, "Bounty reward must be greater than zero"); // TODO - maybe not? some fun/charity bounties? require(_duration > 0, "Bounty duration must be greater than zero"); Bounty storage newBounty = bounties[bountyCount]; @@ -161,34 +117,17 @@ contract Think2Earn is Think2EarnBountyFactoryV1, ReentrancyGuard { newBounty.creator = msg.sender; newBounty.isActive = true; - // bounties[bountyCount] = Bounty({ - // name: _name, - // description: _description, - // mediaURIHash: _mediaURIHash, // Hash of the media URI - // reward: msg.value, - // duration: _duration, - // judgeTime: _judgeTime, - // maxProgress: _maxProgress, - // creationBlock: block.number, - // creator: msg.sender, - // isActive: true - // // submissions: submissions - // }); - emit BountyCreated(bountyCount, _name, _description, msg.value, _duration, _judgeTime, _maxProgress, msg.sender); activeBountyIds.push(bountyCount); bountyCount++; } - // function rejectSubmission(uint256 bountyId, uint256 dasubmissionId) external; // require(msgSender == creator) - // TODO - security consideration, this could run out of gas for some very large submission numbers - function completeBounty(uint256 _bountyId, uint256[] calldata acceptedSubmissions) external nonReentrant { // require(msgSender == creator) + function completeBounty(uint256 _bountyId, uint256[] calldata acceptedSubmissions) external nonReentrant { Bounty storage bounty = bounties[_bountyId]; require(bounty.isActive, "Bounty is not active"); require(msg.sender == bounty.creator); require(block.number >= bounty.creationBlock + bounty.duration, "Bounty duration not yet passed"); - // require(bounty.reward <= address(this).balance, "Insufficient contract balance to reward"); // TODO we can get stuck if not enough eth in the contract (e.g. due to some rounding error) bounty.isActive = false; @@ -200,10 +139,9 @@ contract Think2Earn is Think2EarnBountyFactoryV1, ReentrancyGuard { address payable submitter = payable(bounty.submissions[acceptedSubmissions[i]].submitter); (bool success, ) = submitter.call{value: rewardPerSubmission}(""); emit PaymentMade(_bountyId, acceptedSubmissions[i], rewardPerSubmission); - // require(success, "Failed to send Ether"); TODO security issue - if we can't send bounty to someone, we coulnd't close the bounty } - // return remainging eth (or all e.g. if nothing accepted or closing early) + // return remaining eth (or all e.g. if nothing accepted or closing early) uint256 leftoverReward = startingBalance - address(this).balance - bounty.reward; (bool success, ) = bounty.creator.call{value: leftoverReward}(""); bounty.reward = 0; @@ -212,7 +150,6 @@ contract Think2Earn is Think2EarnBountyFactoryV1, ReentrancyGuard { emit BountyCompleted(_bountyId, numAcceptedSubmissions); } - // Helper function to remove a bounty from the active list function removeBountyFromActiveList(uint bountyId) private { for (uint i = 0; i < activeBountyIds.length; i++) { if (activeBountyIds[i] == bountyId) { @@ -227,8 +164,7 @@ contract Think2Earn is Think2EarnBountyFactoryV1, ReentrancyGuard { return bountyCount; } - function getBountySubmissions(uint256 _bountyId, uint256 _submissionId) external view returns (Submission memory) - { + function getBountySubmissions(uint256 _bountyId, uint256 _submissionId) external view returns (Submission memory) { return bounties[_bountyId].submissions[_submissionId]; } @@ -236,7 +172,6 @@ contract Think2Earn is Think2EarnBountyFactoryV1, ReentrancyGuard { return activeBountyIds; } - // TODO @tom returns lenght of the submission array, submissions function getBountyDetails(uint256 _bountyId) external view returns ( string memory name, string memory description, @@ -248,7 +183,8 @@ contract Think2Earn is Think2EarnBountyFactoryV1, ReentrancyGuard { address creator, uint256 creationBlock, bool isActive, - uint256 submissionsLength + uint256 submissionsLength, + uint256 currentProgress ) { Bounty storage bounty = bounties[_bountyId]; return ( @@ -262,11 +198,21 @@ contract Think2Earn is Think2EarnBountyFactoryV1, ReentrancyGuard { bounty.creator, bounty.creationBlock, bounty.isActive, + bounty.submissions.length, bounty.submissions.length ); } - function getVersion() external view returns (uint256) - { + + function getBounties() external view returns (Bounty[] memory) { + Bounty[] memory allBounties = new Bounty[](activeBountyIds.length); + for (uint256 i = 0; i < bountyCount; i++) { + allBounties[i] = bounties[activeBountyIds[i]]; + } + return allBounties; + } + + function getVersion() external view returns (uint256) { return 1; } } + diff --git a/packages/hardhat/package.json b/packages/hardhat/package.json index 0b619c5..05676a0 100644 --- a/packages/hardhat/package.json +++ b/packages/hardhat/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "scripts": { "account": "hardhat run scripts/listAccount.ts", - "chain": "hardhat node --network hardhat --no-deploy", + "chain": "hardhat node --network hardhat --no-deploy --hostname 0.0.0.0", "compile": "hardhat compile", "deploy": "hardhat deploy", "fork": "MAINNET_FORKING_ENABLED=true hardhat node --network hardhat --no-deploy", diff --git a/packages/nextjs/app/subgraph/_components/GreetingsTable.tsx b/packages/nextjs/app/subgraph/_components/GreetingsTable.tsx new file mode 100644 index 0000000..9a58b84 --- /dev/null +++ b/packages/nextjs/app/subgraph/_components/GreetingsTable.tsx @@ -0,0 +1,61 @@ +"use client"; + +import { gql, useQuery } from "@apollo/client"; +import { Address } from "~~/components/scaffold-eth"; + +const GreetingsTable = () => { + const GREETINGS_GRAPHQL = ` +{ + greetings(first: 25, orderBy: createdAt, orderDirection: desc) { + id + greeting + premium + value + createdAt + sender { + address + greetingCount + } + } +} +`; + + const GREETINGS_GQL = gql(GREETINGS_GRAPHQL); + const { data: greetingsData, error } = useQuery(GREETINGS_GQL, { fetchPolicy: "network-only" }); + + // Subgraph maybe not yet configured + if (error) { + return <>; + } + + return ( +
+
+ + + + + + + + + + {greetingsData?.greetings?.map((greeting: any, index: number) => { + return ( + + + + + + ); + })} + +
SenderGreetings
{index + 1} +
+
{greeting.greeting}
+
+
+ ); +}; + +export default GreetingsTable; diff --git a/packages/nextjs/app/subgraph/page.tsx b/packages/nextjs/app/subgraph/page.tsx new file mode 100644 index 0000000..cdd94c7 --- /dev/null +++ b/packages/nextjs/app/subgraph/page.tsx @@ -0,0 +1,93 @@ +import React from "react"; +import Link from "next/link"; +import GreetingsTable from "./_components/GreetingsTable"; +import type { NextPage } from "next"; +import { MagnifyingGlassIcon, PlusIcon, PowerIcon, RocketLaunchIcon } from "@heroicons/react/24/outline"; + +const Subgraph: NextPage = () => { + return ( + <> +
+
+

+ Welcome to your + Subgraph +

+

+ Look at the subgraph manifest defined in{" "} + + packages/subgraph/subgraph.yaml + +

+

+ Examine the entities in{" "} + + schema.graphql + {" "} + located in{" "} + + packages/subgraph/src + +

+

+ Data is processed using AssemblyScript Mappings in{" "} + + packages/subgraph/src/mapping.ts + +

+
+
+
+
+ +

+ Start your subgraph environment using{" "} + + yarn run-node + +

+
+
+ +

+ Create your subgraph on graph-node with{" "} + + yarn local-create + +

+
+
+ +

+ Deploy your subgraph configuration with{" "} + + yarn local-ship + +

+
+
+ +

Explore data using the

+ + GraphiQL tool. + {" "} + Clean up any stale data using{" "} + + yarn clean-node + +
+
+
+ +
+ + ); +}; + +export default Subgraph; diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index cea6830..6732f3d 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -14,6 +14,7 @@ "vercel:yolo": "vercel --build-env NEXT_PUBLIC_IGNORE_BUILD_ERROR=true" }, "dependencies": { + "@apollo/client": "^3.9.4", "@heroicons/react": "^2.0.11", "@rainbow-me/rainbowkit": "2.1.0", "@tanstack/react-query": "^5.28.6", @@ -22,6 +23,7 @@ "blo": "^1.0.1", "burner-connector": "^0.0.7", "daisyui": "4.5.0", + "graphql": "^16.8.1", "next": "^14.0.4", "next-themes": "^0.2.1", "nprogress": "^0.2.0", diff --git a/packages/subgraph/README.md b/packages/subgraph/README.md new file mode 100644 index 0000000..b8e6d06 --- /dev/null +++ b/packages/subgraph/README.md @@ -0,0 +1,339 @@ +# Scaffold-ETH 2 and The Graph + +Uses a subgraph from The Graph to index and query blockchain data. + +## đŸ§‘đŸŒâ€đŸš€ The Graph + +[The Graph](https://thegraph.com/) is a protocol for building decentralized applications (dApps) quickly on Ethereum and IPFS using GraphQL. + +- đŸ—ƒïž **Decentralized Indexing**: The Graph enables open APIs ("subgraphs") to efficiently index and organize blockchain data. +- 🔎 **Efficient Querying**: The protocol uses GraphQL for streamlined querying blockchain data. +- 🙌 **Community Ecosystem**: The Graph fosters collaboration by empowering developers to build, deploy, and share subgraphs! + +For detailed instructions and more context, check out the [Getting Started Guide](https://thegraph.com/docs/en/cookbook/quick-start). + +  + +## ✅ Requirements + +Before you begin, you need to install the following tools: + +- [Node.js](https://nodejs.org/en/download/) +- Yarn ([v1](https://classic.yarnpkg.com/en/docs/install/) or [v2+](https://yarnpkg.com/getting-started/install)) +- [Git](https://git-scm.com/downloads) +- [Docker](https://docs.docker.com/get-docker/) + +  + +## High level steps to create a subgraph + +1. Create entities (schema.graphql) +2. Create mapping functions (mapping.ts) +3. Configure the subgraph data sources: name, network, source, entities, abis, eventHandlers (subgraph.yaml) +4. Run local graph node (yarn run-node) +5. Create a local subgraph (yarn local-create) +6. Build and deploy the subgraph to the local node (yarn local-ship) + +More information at https://thegraph.com/docs/en/developing/creating-a-subgraph/ + +## Getting Started with subgraph-package of Scaffold-ETH 2 + +Clone the repository. + +``` +git clone -b subgraph-package \ + https://github.com/scaffold-eth/scaffold-eth-2.git \ + scaffold-eth-2-subgraph-package +``` + +Install all the packages required. + +``` +cd scaffold-eth-2-subgraph-package && \ + yarn install +``` + +Next, we will want to start up our local blockchain so that we can eventually deploy and test our smart contracts. Scaffold-ETH 2 comes with Hardhat by default. To spin up the chain just type the following yarn command
 + +``` +yarn chain +``` + +> You will keep this window up and available so that you can see any output from hardhat console. đŸ–„ïž + +Next we are going to spin up our frontend application. Scaffold-ETH 2 comes with NextJS by default and also can be started with a simple yarn command. You will need to open up a new command line and type the following
 + +``` +yarn start +``` + +> You will also want to keep this window up at all times so that you can debug any code changes you make to NextJS, debug performance or just check that the server is running properly. + +Next, you will want to open up a third window where you can deploy your smart contract, along with some other useful commands found in Scaffold-ETH. To do a deploy you can simply run the following
 + +``` +yarn deploy +``` + +> You should get a tx along with an address and amount of gas spent on the deploy. ⛜ + +If you navigate to http://localhost:3000 you should see the NextJS application. Explore the menus and features of Scaffold-ETH 2! Someone call in an emergency, cause hot damn that is fire! đŸ”„ + +  + +## 🚀 Setup The Graph Integration + +Now that we have spun up our blockchain, started our frontend application and deployed our smart contract, we can start setting up our subgraph and utilize The Graph! + +> Before following these steps be sure Docker is running! + +  + +#### ✅ Step 1: Clean up any old data and spin up our docker containers ✅ + +First run the following to clean up any old data. Do this if you need to reset everything. + +``` +yarn clean-node +``` + +> We can now spin up a graph node by running the following command
 🧑‍🚀 + +``` +yarn run-node +``` + +This will spin up all the containers for The Graph using docker-compose. You will want to keep this window open at all times so that you can see log output from Docker. + +> As stated before, be sure to keep this window open so that you can see any log output from Docker. 🔎 + +> NOTE FOR LINUX USERS: If you are running Linux you will need some additional changes to the project. + +##### Linux Only + +Update your package.json in packages/hardhat with the following command line option for the hardhat chain. + +``` +"chain": "hardhat node --network hardhat --no-deploy --hostname 0.0.0.0" +``` + +Save the file and then restart your chain in its original window. + +``` +yarn chain +``` + +You might also need to add a firewall exception for port 8432. As an example for Ubuntu... run the following command. + +``` +sudo ufw allow 8545/tcp +``` + +  + +#### ✅ Side Quest: Run a Matchstick Test ✅ + +Matchstick is a [unit testing framework](https://thegraph.com/docs/en/developing/unit-testing-framework/), developed by [LimeChain](https://limechain.tech/), that enables subgraph developers to test their mapping logic in a sandboxed environment and deploy their subgraphs with confidence! + +The project comes with a pre-written test located in `packages/subgraph/tests/asserts.test.ts` + +To test simply type.... + +``` +yarn subgraph:test +``` + +> This will run `graph test` and automatically download the needed files for testing. + +You should receive the following output. + +``` +Fetching latest version tag... +Downloading release from https://github.com/LimeChain/matchstick/releases/download/0.6.0/binary-macos-11-m1 +binary-macos-11-m1 has been installed! + +___ ___ _ _ _ _ _ +| \/ | | | | | | | (_) | | +| . . | __ _| |_ ___| |__ ___| |_ _ ___| | __ +| |\/| |/ _` | __/ __| '_ \/ __| __| |/ __| |/ / +| | | | (_| | || (__| | | \__ \ |_| | (__| < +\_| |_/\__,_|\__\___|_| |_|___/\__|_|\___|_|\_\ + +Compiling... + +💬 Compiling asserts... + +Igniting tests đŸ”„ + +asserts +-------------------------------------------------- + Asserts: + √ Greeting and Sender entities - 0.102ms + +All 1 tests passed! 😎 + +[Thu, 07 Mar 2024 15:10:26 -0800] Program executed in: 1.838s. +``` + +#### ✅ Step 2: Create and ship our Subgraph ✅ + +Now we can open up a fourth window to finish setting up The Graph. 😅 In this forth window we will create our local subgraph! + +> Note: You will only need to do this once. + +``` +yarn local-create +``` + +> You should see some output stating your Subgraph has been created along with a log output on your graph-node inside docker. + +Next we will ship our subgraph! You will need to give your subgraph a version after executing this command. (e.g. 0.0.1). + +``` +yarn local-ship +``` + +> This command does the following all in one
 🚀🚀🚀 + +- Copies the contracts ABI from the hardhat/deployments folder +- Generates the networks.json file +- Generates AssemblyScript types from the subgraph schema and the contract ABIs. +- Compiles and checks the mapping functions. +- 
 and deploy a local subgraph! + +> If you get an error ts-node you can install it with the following command + +``` +npm install -g ts-node +``` + +You should get a build completed output along with the address of your Subgraph endpoint. + +``` +Build completed: QmYdGWsVSUYTd1dJnqn84kJkDggc2GD9RZWK5xLVEMB9iP + +Deployed to http://localhost:8000/subgraphs/name/scaffold-eth/your-contract/graphql + +Subgraph endpoints: +Queries (HTTP): http://localhost:8000/subgraphs/name/scaffold-eth/your-contract +``` + +  + +#### ✅ Step 3: Test your Subgraph ✅ + +Go ahead and head over to your subgraph endpoint and take a look! + +> Here is an example query
 + +``` + { + greetings(first: 25, orderBy: createdAt, orderDirection: desc) { + id + greeting + premium + value + createdAt + sender { + address + greetingCount + } + } + } +``` + +> If all is well and you’ve sent a transaction to your smart contract then you will see a similar data output! + +Next up we will dive into a bit more detail on how The Graph works so that as you start adding events to your smart contract you can start indexing and parsing the data you need for your front end application. + +  + +## A list of all available commands + +### run-node + +```sh +yarn run-node +``` + +Spin up a local graph node (requires Docker). + +### stop-node + +```sh +yarn stop-node +``` + +Stop the local graph node. + +### clean-node + +```sh +yarn clean-node +``` + +Remove the data from the local graph node. + +### local-create + +```sh +yarn local-create +``` + +Create your local subgraph (only required once). + +### local-remove + +```sh +yarn local-remove +``` + +Delete a local subgprah. + +### abi-copy + +```sh +yarn abi-copy +``` + +Copy the contracts ABI from the hardhat/deployments folder. Generates the networks.json file too. + +### codegen + +```sh +yarn codegen +``` + +Generates AssemblyScript types from the subgraph schema and the contract ABIs. + +### build + +```sh +yarn build +``` + +Compile and check the mapping functions. + +### local-deploy + +```sh +yarn local-deploy +``` + +Deploy a local subgraph. + +### local-ship + +```sh +yarn local-ship +``` + +Run all the required commands to deploy a local subgraph (abi-copy, codegen, build and local-deploy). + +### deploy + +```sh +yarn deploy +``` + +Deploy a subgraph to TheGraph. diff --git a/packages/subgraph/abis/localhost_Think2Earn.json b/packages/subgraph/abis/localhost_Think2Earn.json new file mode 100644 index 0000000..9fe9da7 --- /dev/null +++ b/packages/subgraph/abis/localhost_Think2Earn.json @@ -0,0 +1,469 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "bountyId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "numAcceptedSubmissions", + "type": "uint256" + } + ], + "name": "BountyCompleted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "bountyId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "indexed": false, + "internalType": "string", + "name": "description", + "type": "string" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "reward", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "duration", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "judgeTime", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "maxProgress", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "creator", + "type": "address" + } + ], + "name": "BountyCreated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "bountyId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "submissionId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "eegDataHash", + "type": "bytes32" + } + ], + "name": "EEGDataSubmitted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "EtherDeposited", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "bountyId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "submissionId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "PaymentMade", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "activeBountyIds", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "bounties", + "outputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + }, + { + "internalType": "string", + "name": "mediaURIHash", + "type": "string" + }, + { + "internalType": "uint256", + "name": "reward", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "duration", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "judgeTime", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxProgress", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "creationBlock", + "type": "uint256" + }, + { + "internalType": "address", + "name": "creator", + "type": "address" + }, + { + "internalType": "bool", + "name": "isActive", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "bountyCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_bountyId", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "acceptedSubmissions", + "type": "uint256[]" + } + ], + "name": "completeBounty", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_name", + "type": "string" + }, + { + "internalType": "string", + "name": "_description", + "type": "string" + }, + { + "internalType": "string", + "name": "_mediaURIHash", + "type": "string" + }, + { + "internalType": "uint256", + "name": "_duration", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_judgeTime", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_maxProgress", + "type": "uint256" + } + ], + "name": "createBounty", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "getActiveBounties", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getBountyCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_bountyId", + "type": "uint256" + } + ], + "name": "getBountyDetails", + "outputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + }, + { + "internalType": "string", + "name": "mediaURI", + "type": "string" + }, + { + "internalType": "uint256", + "name": "reward", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "duration", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "judgeTime", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxProgress", + "type": "uint256" + }, + { + "internalType": "address", + "name": "creator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "creationBlock", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isActive", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "submissionsLength", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_bountyId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_submissionId", + "type": "uint256" + } + ], + "name": "getBountySubmissions", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "submitter", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "eegDataHash", + "type": "bytes32" + } + ], + "internalType": "struct Think2EarnBountyFactoryV1.Submission", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getVersion", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_bountyId", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "_eegDataHash", + "type": "bytes32" + } + ], + "name": "submitEEGData", + "outputs": [ + { + "internalType": "uint256", + "name": "submissionId", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] \ No newline at end of file diff --git a/packages/subgraph/abis/localhost_YourContract.json b/packages/subgraph/abis/localhost_YourContract.json new file mode 100644 index 0000000..02fff58 --- /dev/null +++ b/packages/subgraph/abis/localhost_YourContract.json @@ -0,0 +1,139 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "greetingSetter", + "type": "address" + }, + { + "indexed": false, + "internalType": "string", + "name": "newGreeting", + "type": "string" + }, + { + "indexed": false, + "internalType": "bool", + "name": "premium", + "type": "bool" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "GreetingChange", + "type": "event" + }, + { + "inputs": [], + "name": "greeting", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "premium", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_newGreeting", + "type": "string" + } + ], + "name": "setGreeting", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "totalCounter", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "userGreetingCounter", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] \ No newline at end of file diff --git a/packages/subgraph/build/YourContract/YourContract.wasm b/packages/subgraph/build/YourContract/YourContract.wasm new file mode 100644 index 0000000..94a4e7b Binary files /dev/null and b/packages/subgraph/build/YourContract/YourContract.wasm differ diff --git a/packages/subgraph/build/YourContract/abis/localhost_YourContract.json b/packages/subgraph/build/YourContract/abis/localhost_YourContract.json new file mode 100644 index 0000000..02fff58 --- /dev/null +++ b/packages/subgraph/build/YourContract/abis/localhost_YourContract.json @@ -0,0 +1,139 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "greetingSetter", + "type": "address" + }, + { + "indexed": false, + "internalType": "string", + "name": "newGreeting", + "type": "string" + }, + { + "indexed": false, + "internalType": "bool", + "name": "premium", + "type": "bool" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "GreetingChange", + "type": "event" + }, + { + "inputs": [], + "name": "greeting", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "premium", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_newGreeting", + "type": "string" + } + ], + "name": "setGreeting", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "totalCounter", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "userGreetingCounter", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] \ No newline at end of file diff --git a/packages/subgraph/build/schema.graphql b/packages/subgraph/build/schema.graphql new file mode 100644 index 0000000..9ef60f5 --- /dev/null +++ b/packages/subgraph/build/schema.graphql @@ -0,0 +1,45 @@ +type Bounty @entity { + id: ID! + name: String! + description: String! + mediaURIHash: String! + reward: BigInt! + duration: BigInt! + judgeTime: BigInt! + maxProgress: BigInt! + creationBlock: BigInt! + creator: Bytes! + isActive: Boolean! + submissions: [Submission!] @derivedFrom(field: "bounty") + completed: Boolean! + numAcceptedSubmissions: BigInt! +} + +type Submission @entity { + id: ID! + bounty: Bounty! + submitter: Bytes! + eegDataHash: Bytes! +} + +type Deposit @entity { + id: ID! + sender: Bytes! + amount: BigInt! +} + +type Payment @entity { + id: ID! + bounty: Bounty! + submission: Submission! + amount: BigInt! +} + +type User @entity { + id: ID! + address: Bytes! + submissionCount: BigInt! + depositCount: BigInt! + paymentReceivedCount: BigInt! +} + diff --git a/packages/subgraph/build/subgraph.yaml b/packages/subgraph/build/subgraph.yaml new file mode 100644 index 0000000..67687df --- /dev/null +++ b/packages/subgraph/build/subgraph.yaml @@ -0,0 +1,38 @@ +specVersion: 0.0.4 +description: Think2Earn Subgraph +repository: https://github.com/scaffold-eth/se-2/packages/subgraph/ +schema: + file: schema.graphql +dataSources: + - kind: ethereum/contract + name: Think2Earn + network: localhost + source: + abi: Think2Earn + address: "0x5FbDB2315678afecb367f032d93F642f64180aa3" + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - Bounty + - Submission + - Deposit + - Payment + - User + abis: + - name: Think2Earn + file: Think2Earn/abis/localhost_Think2Earn.json + eventHandlers: + - event: BountyCreated(uint256,string,string,string,uint256,uint256,uint256,uint256,address) + handler: handleBountyCreated + - event: EEGDataSubmitted(uint256,uint256,bytes32) + handler: handleEEGDataSubmitted + - event: BountyCompleted(uint256,uint256) + handler: handleBountyCompleted + - event: EtherDeposited(address,uint256) + handler: handleEtherDeposited + - event: PaymentMade(uint256,uint256,uint256) + handler: handlePaymentMade + file: Think2Earn/Think2Earn.wasm + diff --git a/packages/subgraph/generated/YourContract/YourContract.ts b/packages/subgraph/generated/YourContract/YourContract.ts new file mode 100644 index 0000000..8e663eb --- /dev/null +++ b/packages/subgraph/generated/YourContract/YourContract.ts @@ -0,0 +1,216 @@ +// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. + +import { + ethereum, + JSONValue, + TypedMap, + Entity, + Bytes, + Address, + BigInt +} from "@graphprotocol/graph-ts"; + +export class GreetingChange extends ethereum.Event { + get params(): GreetingChange__Params { + return new GreetingChange__Params(this); + } +} + +export class GreetingChange__Params { + _event: GreetingChange; + + constructor(event: GreetingChange) { + this._event = event; + } + + get greetingSetter(): Address { + return this._event.parameters[0].value.toAddress(); + } + + get newGreeting(): string { + return this._event.parameters[1].value.toString(); + } + + get premium(): boolean { + return this._event.parameters[2].value.toBoolean(); + } + + get value(): BigInt { + return this._event.parameters[3].value.toBigInt(); + } +} + +export class YourContract extends ethereum.SmartContract { + static bind(address: Address): YourContract { + return new YourContract("YourContract", address); + } + + greeting(): string { + let result = super.call("greeting", "greeting():(string)", []); + + return result[0].toString(); + } + + try_greeting(): ethereum.CallResult { + let result = super.tryCall("greeting", "greeting():(string)", []); + if (result.reverted) { + return new ethereum.CallResult(); + } + let value = result.value; + return ethereum.CallResult.fromValue(value[0].toString()); + } + + owner(): Address { + let result = super.call("owner", "owner():(address)", []); + + return result[0].toAddress(); + } + + try_owner(): ethereum.CallResult
{ + let result = super.tryCall("owner", "owner():(address)", []); + if (result.reverted) { + return new ethereum.CallResult(); + } + let value = result.value; + return ethereum.CallResult.fromValue(value[0].toAddress()); + } + + premium(): boolean { + let result = super.call("premium", "premium():(bool)", []); + + return result[0].toBoolean(); + } + + try_premium(): ethereum.CallResult { + let result = super.tryCall("premium", "premium():(bool)", []); + if (result.reverted) { + return new ethereum.CallResult(); + } + let value = result.value; + return ethereum.CallResult.fromValue(value[0].toBoolean()); + } + + totalCounter(): BigInt { + let result = super.call("totalCounter", "totalCounter():(uint256)", []); + + return result[0].toBigInt(); + } + + try_totalCounter(): ethereum.CallResult { + let result = super.tryCall("totalCounter", "totalCounter():(uint256)", []); + if (result.reverted) { + return new ethereum.CallResult(); + } + let value = result.value; + return ethereum.CallResult.fromValue(value[0].toBigInt()); + } + + userGreetingCounter(param0: Address): BigInt { + let result = super.call( + "userGreetingCounter", + "userGreetingCounter(address):(uint256)", + [ethereum.Value.fromAddress(param0)] + ); + + return result[0].toBigInt(); + } + + try_userGreetingCounter(param0: Address): ethereum.CallResult { + let result = super.tryCall( + "userGreetingCounter", + "userGreetingCounter(address):(uint256)", + [ethereum.Value.fromAddress(param0)] + ); + if (result.reverted) { + return new ethereum.CallResult(); + } + let value = result.value; + return ethereum.CallResult.fromValue(value[0].toBigInt()); + } +} + +export class ConstructorCall extends ethereum.Call { + get inputs(): ConstructorCall__Inputs { + return new ConstructorCall__Inputs(this); + } + + get outputs(): ConstructorCall__Outputs { + return new ConstructorCall__Outputs(this); + } +} + +export class ConstructorCall__Inputs { + _call: ConstructorCall; + + constructor(call: ConstructorCall) { + this._call = call; + } + + get _owner(): Address { + return this._call.inputValues[0].value.toAddress(); + } +} + +export class ConstructorCall__Outputs { + _call: ConstructorCall; + + constructor(call: ConstructorCall) { + this._call = call; + } +} + +export class SetGreetingCall extends ethereum.Call { + get inputs(): SetGreetingCall__Inputs { + return new SetGreetingCall__Inputs(this); + } + + get outputs(): SetGreetingCall__Outputs { + return new SetGreetingCall__Outputs(this); + } +} + +export class SetGreetingCall__Inputs { + _call: SetGreetingCall; + + constructor(call: SetGreetingCall) { + this._call = call; + } + + get _newGreeting(): string { + return this._call.inputValues[0].value.toString(); + } +} + +export class SetGreetingCall__Outputs { + _call: SetGreetingCall; + + constructor(call: SetGreetingCall) { + this._call = call; + } +} + +export class WithdrawCall extends ethereum.Call { + get inputs(): WithdrawCall__Inputs { + return new WithdrawCall__Inputs(this); + } + + get outputs(): WithdrawCall__Outputs { + return new WithdrawCall__Outputs(this); + } +} + +export class WithdrawCall__Inputs { + _call: WithdrawCall; + + constructor(call: WithdrawCall) { + this._call = call; + } +} + +export class WithdrawCall__Outputs { + _call: WithdrawCall; + + constructor(call: WithdrawCall) { + this._call = call; + } +} diff --git a/packages/subgraph/generated/schema.ts b/packages/subgraph/generated/schema.ts new file mode 100644 index 0000000..82a90f4 --- /dev/null +++ b/packages/subgraph/generated/schema.ts @@ -0,0 +1,239 @@ +// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. + +import { + TypedMap, + Entity, + Value, + ValueKind, + store, + Bytes, + BigInt, + BigDecimal +} from "@graphprotocol/graph-ts"; + +export class Greeting extends Entity { + constructor(id: string) { + super(); + this.set("id", Value.fromString(id)); + } + + save(): void { + let id = this.get("id"); + assert(id != null, "Cannot save Greeting entity without an ID"); + if (id) { + assert( + id.kind == ValueKind.STRING, + `Entities of type Greeting must have an ID of type String but the id '${id.displayData()}' is of type ${id.displayKind()}` + ); + store.set("Greeting", id.toString(), this); + } + } + + static loadInBlock(id: string): Greeting | null { + return changetype(store.get_in_block("Greeting", id)); + } + + static load(id: string): Greeting | null { + return changetype(store.get("Greeting", id)); + } + + get id(): string { + let value = this.get("id"); + if (!value || value.kind == ValueKind.NULL) { + throw new Error("Cannot return null for a required field."); + } else { + return value.toString(); + } + } + + set id(value: string) { + this.set("id", Value.fromString(value)); + } + + get sender(): string { + let value = this.get("sender"); + if (!value || value.kind == ValueKind.NULL) { + throw new Error("Cannot return null for a required field."); + } else { + return value.toString(); + } + } + + set sender(value: string) { + this.set("sender", Value.fromString(value)); + } + + get greeting(): string { + let value = this.get("greeting"); + if (!value || value.kind == ValueKind.NULL) { + throw new Error("Cannot return null for a required field."); + } else { + return value.toString(); + } + } + + set greeting(value: string) { + this.set("greeting", Value.fromString(value)); + } + + get premium(): boolean { + let value = this.get("premium"); + if (!value || value.kind == ValueKind.NULL) { + return false; + } else { + return value.toBoolean(); + } + } + + set premium(value: boolean) { + this.set("premium", Value.fromBoolean(value)); + } + + get value(): BigInt | null { + let value = this.get("value"); + if (!value || value.kind == ValueKind.NULL) { + return null; + } else { + return value.toBigInt(); + } + } + + set value(value: BigInt | null) { + if (!value) { + this.unset("value"); + } else { + this.set("value", Value.fromBigInt(value)); + } + } + + get createdAt(): BigInt { + let value = this.get("createdAt"); + if (!value || value.kind == ValueKind.NULL) { + throw new Error("Cannot return null for a required field."); + } else { + return value.toBigInt(); + } + } + + set createdAt(value: BigInt) { + this.set("createdAt", Value.fromBigInt(value)); + } + + get transactionHash(): string { + let value = this.get("transactionHash"); + if (!value || value.kind == ValueKind.NULL) { + throw new Error("Cannot return null for a required field."); + } else { + return value.toString(); + } + } + + set transactionHash(value: string) { + this.set("transactionHash", Value.fromString(value)); + } +} + +export class Sender extends Entity { + constructor(id: string) { + super(); + this.set("id", Value.fromString(id)); + } + + save(): void { + let id = this.get("id"); + assert(id != null, "Cannot save Sender entity without an ID"); + if (id) { + assert( + id.kind == ValueKind.STRING, + `Entities of type Sender must have an ID of type String but the id '${id.displayData()}' is of type ${id.displayKind()}` + ); + store.set("Sender", id.toString(), this); + } + } + + static loadInBlock(id: string): Sender | null { + return changetype(store.get_in_block("Sender", id)); + } + + static load(id: string): Sender | null { + return changetype(store.get("Sender", id)); + } + + get id(): string { + let value = this.get("id"); + if (!value || value.kind == ValueKind.NULL) { + throw new Error("Cannot return null for a required field."); + } else { + return value.toString(); + } + } + + set id(value: string) { + this.set("id", Value.fromString(value)); + } + + get address(): Bytes { + let value = this.get("address"); + if (!value || value.kind == ValueKind.NULL) { + throw new Error("Cannot return null for a required field."); + } else { + return value.toBytes(); + } + } + + set address(value: Bytes) { + this.set("address", Value.fromBytes(value)); + } + + get greetings(): GreetingLoader { + return new GreetingLoader( + "Sender", + this.get("id")!.toString(), + "greetings" + ); + } + + get createdAt(): BigInt { + let value = this.get("createdAt"); + if (!value || value.kind == ValueKind.NULL) { + throw new Error("Cannot return null for a required field."); + } else { + return value.toBigInt(); + } + } + + set createdAt(value: BigInt) { + this.set("createdAt", Value.fromBigInt(value)); + } + + get greetingCount(): BigInt { + let value = this.get("greetingCount"); + if (!value || value.kind == ValueKind.NULL) { + throw new Error("Cannot return null for a required field."); + } else { + return value.toBigInt(); + } + } + + set greetingCount(value: BigInt) { + this.set("greetingCount", Value.fromBigInt(value)); + } +} + +export class GreetingLoader extends Entity { + _entity: string; + _field: string; + _id: string; + + constructor(entity: string, id: string, field: string) { + super(); + this._entity = entity; + this._id = id; + this._field = field; + } + + load(): Greeting[] { + let value = store.loadRelated(this._entity, this._id, this._field); + return changetype(value); + } +} diff --git a/packages/subgraph/graph-node/README.md b/packages/subgraph/graph-node/README.md new file mode 100644 index 0000000..fdfb0a6 --- /dev/null +++ b/packages/subgraph/graph-node/README.md @@ -0,0 +1,79 @@ +# Graph Node Docker Image + +Preconfigured Docker image for running a Graph Node. + +## Usage + +```sh +docker run -it \ + -e postgres_host=[:] \ + -e postgres_user= \ + -e postgres_pass= \ + -e postgres_db= \ + -e ipfs=: \ + -e ethereum=: \ + graphprotocol/graph-node:latest +``` + +### Example usage + +```sh +docker run -it \ + -e postgres_host=host.docker.internal:5432 + -e postgres_user=graph-node \ + -e postgres_pass=oh-hello \ + -e postgres_db=graph-node \ + -e ipfs=host.docker.internal:5001 \ + -e ethereum=mainnet:http://localhost:8545/ \ + graphprotocol/graph-node:latest +``` + +## Docker Compose + +The Docker Compose setup requires an Ethereum network name and node +to connect to. By default, it will use `mainnet:http://host.docker.internal:8545` +in order to connect to an Ethereum node running on your host machine. +You can replace this with anything else in `docker-compose.yaml`. + +> **Note for Linux users:** On Linux, if you have docker v20.10 and above, you will need to make +> sure you have extra_hosts in the docker-compose.yml file. If you have a docker older than v20.10, +> `host.docker.internal` is not supported. Instead, you will have to replace it with the +> IP address of your Docker host (from the perspective of the Graph +> Node container). +> To do this, run: +> +> ``` +> CONTAINER_ID=$(docker container ls | grep graph-node | cut -d' ' -f1) +> docker exec $CONTAINER_ID /bin/bash -c 'ip route | awk \'/^default via /{print $3}\'' +> ``` +> +> This will print the host's IP address. Then, put it into `docker-compose.yml`: +> +> ``` +> sed -i -e 's/host.docker.internal//g' docker-compose.yml +> ``` + +After you have set up an Ethereum node—e.g. Ganache or Parity—simply +clone this repository and run + +```sh +docker-compose up +``` + +This will start IPFS, Postgres and Graph Node in Docker and create persistent +data directories for IPFS and Postgres in `./data/ipfs` and `./data/postgres`. You +can access these via: + +- Graph Node: + - GraphiQL: `http://localhost:8000/` + - HTTP: `http://localhost:8000/subgraphs/name/` + - WebSockets: `ws://localhost:8001/subgraphs/name/` + - Admin: `http://localhost:8020/` +- IPFS: + - `127.0.0.1:5001` or `/ip4/127.0.0.1/tcp/5001` +- Postgres: + - `postgresql://graph-node:let-me-in@localhost:5432/graph-node` + +Once this is up and running, you can use +[`graph-cli`](https://github.com/graphprotocol/graph-cli) to create and +deploy your subgraph to the running Graph Node. diff --git a/packages/subgraph/graph-node/bin/create b/packages/subgraph/graph-node/bin/create new file mode 100755 index 0000000..9d9a4eb --- /dev/null +++ b/packages/subgraph/graph-node/bin/create @@ -0,0 +1,11 @@ +#! /bin/bash + +if [ $# != 1 ]; then + echo "usage: create " + exit 1 +fi + +api="http://index-node.default/" + +data=$(printf '{"jsonrpc": "2.0", "method": "subgraph_create", "params": {"name":"%s"}, "id":"1"}' "$1") +curl -s -H "content-type: application/json" --data "$data" "$api" diff --git a/packages/subgraph/graph-node/bin/debug b/packages/subgraph/graph-node/bin/debug new file mode 100755 index 0000000..87649f1 --- /dev/null +++ b/packages/subgraph/graph-node/bin/debug @@ -0,0 +1,9 @@ +#! /bin/bash + +if [ -f "$1" ] +then + exec rust-gdb -c "$1" /usr/local/cargo/bin/graph-node +else + echo "usage: debug " + exit 1 +fi diff --git a/packages/subgraph/graph-node/bin/deploy b/packages/subgraph/graph-node/bin/deploy new file mode 100755 index 0000000..f0c9833 --- /dev/null +++ b/packages/subgraph/graph-node/bin/deploy @@ -0,0 +1,12 @@ +#! /bin/bash + +if [ $# != 3 ]; then + echo "usage: deploy " + exit 1 +fi + +api="http://index-node.default/" + +echo "Deploying $1 (deployment $2)" +data=$(printf '{"jsonrpc": "2.0", "method": "subgraph_deploy", "params": {"name":"%s", "ipfs_hash":"%s", "node_id":"%s"}, "id":"1"}' "$1" "$2" "$3") +curl -s -H "content-type: application/json" --data "$data" "$api" diff --git a/packages/subgraph/graph-node/bin/reassign b/packages/subgraph/graph-node/bin/reassign new file mode 100755 index 0000000..a8eb703 --- /dev/null +++ b/packages/subgraph/graph-node/bin/reassign @@ -0,0 +1,12 @@ +#! /bin/bash + +if [ $# -lt 3 ]; then + echo "usage: reassign " + exit 1 +fi + +api="http://index-node.default/" + +echo Assigning to "$3" +data=$(printf '{"jsonrpc": "2.0", "method": "subgraph_reassign", "params": {"name":"%s", "ipfs_hash":"%s", "node_id":"%s"}, "id":"1"}' "$1" "$2" "$3") +curl -s -H "content-type: application/json" --data "$data" "$api" diff --git a/packages/subgraph/graph-node/bin/remove b/packages/subgraph/graph-node/bin/remove new file mode 100755 index 0000000..baaad2b --- /dev/null +++ b/packages/subgraph/graph-node/bin/remove @@ -0,0 +1,11 @@ +#! /bin/bash + +if [ $# != 1 ]; then + echo "usage: create " + exit 1 +fi + +api="http://index-node.default/" + +data=$(printf '{"jsonrpc": "2.0", "method": "subgraph_remove", "params": {"name":"%s"}, "id":"1"}' "$1") +curl -s -H "content-type: application/json" --data "$data" "$api" diff --git a/packages/subgraph/graph-node/data/ipfs/api b/packages/subgraph/graph-node/data/ipfs/api new file mode 100644 index 0000000..71a0e23 --- /dev/null +++ b/packages/subgraph/graph-node/data/ipfs/api @@ -0,0 +1 @@ +/ip4/0.0.0.0/tcp/5001 \ No newline at end of file diff --git a/packages/subgraph/graph-node/data/ipfs/blocks/2A/CIQEGKVPDRYUBVPOKEXB45OFVY6XHIEDOK7L7QCQMX6O64GAEETH2AA.data b/packages/subgraph/graph-node/data/ipfs/blocks/2A/CIQEGKVPDRYUBVPOKEXB45OFVY6XHIEDOK7L7QCQMX6O64GAEETH2AA.data new file mode 100644 index 0000000..d9d6e48 --- /dev/null +++ b/packages/subgraph/graph-node/data/ipfs/blocks/2A/CIQEGKVPDRYUBVPOKEXB45OFVY6XHIEDOK7L7QCQMX6O64GAEETH2AA.data @@ -0,0 +1,3 @@ +- +" =í„ltQ6hŒöÜ9§°/ԀTvșžù"0}čÌsâ±abisš + \ No newline at end of file diff --git a/packages/subgraph/graph-node/data/ipfs/blocks/36/CIQJZE6T3XNTQ7ECAIJSWEGJK23THY5ZP6A3SI6WVTDAW7EPSZED36Y.data b/packages/subgraph/graph-node/data/ipfs/blocks/36/CIQJZE6T3XNTQ7ECAIJSWEGJK23THY5ZP6A3SI6WVTDAW7EPSZED36Y.data new file mode 100644 index 0000000..4fa03f1 Binary files /dev/null and b/packages/subgraph/graph-node/data/ipfs/blocks/36/CIQJZE6T3XNTQ7ECAIJSWEGJK23THY5ZP6A3SI6WVTDAW7EPSZED36Y.data differ diff --git a/packages/subgraph/graph-node/data/ipfs/blocks/5G/CIQE2TKYGXU3KPTCV4FJZREVK2PD3FDZYA336FFFRFUGIRHDNPWM5GI.data b/packages/subgraph/graph-node/data/ipfs/blocks/5G/CIQE2TKYGXU3KPTCV4FJZREVK2PD3FDZYA336FFFRFUGIRHDNPWM5GI.data new file mode 100644 index 0000000..e4fcf47 --- /dev/null +++ b/packages/subgraph/graph-node/data/ipfs/blocks/5G/CIQE2TKYGXU3KPTCV4FJZREVK2PD3FDZYA336FFFRFUGIRHDNPWM5GI.data @@ -0,0 +1,3 @@ +W +" ‡ 3"äd $ʱ*š!|„ȚAžȘÈäD.Dd”żśĐ.QmXS8Vix1NPYsvWpc2ZUb1K8AuSbomqtK6sXwMgeDGund5 + \ No newline at end of file diff --git a/packages/subgraph/graph-node/data/ipfs/blocks/6Y/CIQA4T3TD3BP3C2M3GXCGRCRTCCHV7XSGAZPZJOAOHLPOI6IQR3H6YQ.data b/packages/subgraph/graph-node/data/ipfs/blocks/6Y/CIQA4T3TD3BP3C2M3GXCGRCRTCCHV7XSGAZPZJOAOHLPOI6IQR3H6YQ.data new file mode 100644 index 0000000..a81b134 Binary files /dev/null and b/packages/subgraph/graph-node/data/ipfs/blocks/6Y/CIQA4T3TD3BP3C2M3GXCGRCRTCCHV7XSGAZPZJOAOHLPOI6IQR3H6YQ.data differ diff --git a/packages/subgraph/graph-node/data/ipfs/blocks/75/CIQBEM7N2AM5YRAMJY7WDI6TJ4MGYIWVBA7POWSBPYKENY5IKK2I75Y.data b/packages/subgraph/graph-node/data/ipfs/blocks/75/CIQBEM7N2AM5YRAMJY7WDI6TJ4MGYIWVBA7POWSBPYKENY5IKK2I75Y.data new file mode 100644 index 0000000..749e87b --- /dev/null +++ b/packages/subgraph/graph-node/data/ipfs/blocks/75/CIQBEM7N2AM5YRAMJY7WDI6TJ4MGYIWVBA7POWSBPYKENY5IKK2I75Y.data @@ -0,0 +1,4 @@ +W +" ”b +7M#„ûœż!w{© ’VŻ«bą‡‚Ì%Œbgj.QmQGiYLVAdSHJQKYFRTJZMG4BXBHqKperaZtyKGmCRLmsFœ + \ No newline at end of file diff --git a/packages/subgraph/graph-node/data/ipfs/blocks/BE/CIQCXBHBZAHEHBHU6P7PEA72E7UZQRJALHH7OH2FCWSWMTU7DMWVBEA.data b/packages/subgraph/graph-node/data/ipfs/blocks/BE/CIQCXBHBZAHEHBHU6P7PEA72E7UZQRJALHH7OH2FCWSWMTU7DMWVBEA.data new file mode 100644 index 0000000..7ce1d8a --- /dev/null +++ b/packages/subgraph/graph-node/data/ipfs/blocks/BE/CIQCXBHBZAHEHBHU6P7PEA72E7UZQRJALHH7OH2FCWSWMTU7DMWVBEA.data @@ -0,0 +1,4 @@ +. +" ' śM­ü8gÉԗ€öž%ŸS€Ö.A92Ż––Ì )œaboutœ / +" jöÔnČÒáŚUÒ_0BÂ%äFçĄéÓŰìreadmeÎ + \ No newline at end of file diff --git a/packages/subgraph/graph-node/data/ipfs/blocks/FM/CIQD33NFNR2FCNTIRT3NYONHWAXQ5VEAKR3LVHXZEIYH3OIXZRZ6FMI.data b/packages/subgraph/graph-node/data/ipfs/blocks/FM/CIQD33NFNR2FCNTIRT3NYONHWAXQ5VEAKR3LVHXZEIYH3OIXZRZ6FMI.data new file mode 100644 index 0000000..45e45de --- /dev/null +++ b/packages/subgraph/graph-node/data/ipfs/blocks/FM/CIQD33NFNR2FCNTIRT3NYONHWAXQ5VEAKR3LVHXZEIYH3OIXZRZ6FMI.data @@ -0,0 +1,3 @@ +D +" Àș?nÌȚòr x+ÿÚ±qőg+?‡6YÍh+ąËlocalhost_YourContract.jsonĐ + \ No newline at end of file diff --git a/packages/subgraph/graph-node/data/ipfs/blocks/HB/CIQMDQRK7B5DSZKBYOX4353TGN5J3JXS5VS6YNSAEJBOXBG26R76HBY.data b/packages/subgraph/graph-node/data/ipfs/blocks/HB/CIQMDQRK7B5DSZKBYOX4353TGN5J3JXS5VS6YNSAEJBOXBG26R76HBY.data new file mode 100644 index 0000000..ea70391 --- /dev/null +++ b/packages/subgraph/graph-node/data/ipfs/blocks/HB/CIQMDQRK7B5DSZKBYOX4353TGN5J3JXS5VS6YNSAEJBOXBG26R76HBY.data @@ -0,0 +1,3 @@ +V +" ó°æ‚Ś›‹z,!mbŹâŒWF„H!Œw”Vì“/:dč¶.QmejvEPop4D7YUadeGqYWmZxHhLc4JBUCzJJHWMzdcMe2y + \ No newline at end of file diff --git a/packages/subgraph/graph-node/data/ipfs/blocks/HL/CIQEYG7RVE3IRKQIH535ZAPRBB2TL7CTI4ZMER5LAGCCVRKLG37OHLY.data b/packages/subgraph/graph-node/data/ipfs/blocks/HL/CIQEYG7RVE3IRKQIH535ZAPRBB2TL7CTI4ZMER5LAGCCVRKLG37OHLY.data new file mode 100644 index 0000000..4755dca --- /dev/null +++ b/packages/subgraph/graph-node/data/ipfs/blocks/HL/CIQEYG7RVE3IRKQIH535ZAPRBB2TL7CTI4ZMER5LAGCCVRKLG37OHLY.data @@ -0,0 +1,31 @@ + +áÙdataSources: + - kind: ethereum/contract + mapping: + abis: + - file: + /: /ipfs/QmbJyhuDJ3njx5JzYatbAFCBtSjAqgqJZzA1cp5MmC7jEX + name: YourContract + apiVersion: 0.0.6 + entities: + - Greeting + - Sender + eventHandlers: + - event: 'GreetingChange(indexed address,string,bool,uint256)' + handler: handleGreetingChange + file: + /: /ipfs/Qmf2YCkd9t1rkjLWFa6o2WvR8haEmZYFXcNLU1jGywJfSQ + kind: ethereum/events + language: wasm/assemblyscript + name: YourContract + network: localhost + source: + abi: YourContract + address: '0x5FbDB2315678afecb367f032d93F642f64180aa3' +description: Greetings +repository: 'https://github.com/scaffold-eth/se-2/packages/subgraph/' +schema: + file: + /: /ipfs/QmXS8Vix1NPYsvWpc2ZUb1K8AuSbomqtK6sXwMgeDGund5 +specVersion: 0.0.4 +Ù \ No newline at end of file diff --git a/packages/subgraph/graph-node/data/ipfs/blocks/I2/CIQBZNLCBI3U2I5F7O636DRBO552SCMSK2X2WYVCQ6BMYJN4MJTRI2Q.data b/packages/subgraph/graph-node/data/ipfs/blocks/I2/CIQBZNLCBI3U2I5F7O636DRBO552SCMSK2X2WYVCQ6BMYJN4MJTRI2Q.data new file mode 100644 index 0000000..186648d --- /dev/null +++ b/packages/subgraph/graph-node/data/ipfs/blocks/I2/CIQBZNLCBI3U2I5F7O636DRBO552SCMSK2X2WYVCQ6BMYJN4MJTRI2Q.data @@ -0,0 +1,115 @@ + +™ ‘ # 0.1 - Quick Start + +This is a set of short examples with minimal explanation. It is meant as +a "quick start". + + +Add a file to ipfs: + + echo "hello world" >hello + ipfs add hello + + +View it: + + ipfs cat + + +Try a directory: + + mkdir foo + mkdir foo/bar + echo "baz" > foo/baz + echo "baz" > foo/bar/baz + ipfs add -r foo + + +View things: + + ipfs ls + ipfs ls /bar + ipfs cat /baz + ipfs cat /bar/baz + ipfs cat /bar + ipfs ls /baz + + +References: + + ipfs refs + ipfs refs -r + ipfs refs --help + + +Get: + + ipfs get -o foo2 + diff foo foo2 + + +Objects: + + ipfs object get + ipfs object get /foo2 + ipfs object --help + + +Pin + GC: + + ipfs pin add + ipfs repo gc + ipfs ls + ipfs pin rm + ipfs repo gc + + +Daemon: + + ipfs daemon (in another terminal) + ipfs id + + +Network: + + (must be online) + ipfs swarm peers + ipfs id + ipfs cat + + +Mount: + + (warning: fuse is finicky!) + ipfs mount + cd /ipfs/ + ls + + +Tool: + + ipfs version + ipfs update + ipfs commands + ipfs config --help + open http://localhost:5001/webui + + +Browse: + + WebUI: + + http://localhost:5001/webui + + video: + + http://localhost:8080/ipfs/QmVc6zuAneKJzicnJpfrqCH9gSy6bz54JhcypfJYhGUFQu/play#/ipfs/QmTKZgRNwDNZwHtJSjCp6r5FYefzpULfy37JvMt9DwvXse + + images: + + http://localhost:8080/ipfs/QmZpc3HvfjEXvLWGQPWbHk3AjD5j8NEN4gmFN8Jmrd5g83/cs + + markdown renderer app: + + http://localhost:8080/ipfs/QmX7M9CiYXjVeFnkfVGf3y5ixTZ2ACeSGyL1vBJY1HvQPp/mdown +‘ \ No newline at end of file diff --git a/packages/subgraph/graph-node/data/ipfs/blocks/IL/CIQJFGRQHQ45VCQLM7AJNF2GF5UHUAGGHC6LLAH6VYDEKLQMD4QLILY.data b/packages/subgraph/graph-node/data/ipfs/blocks/IL/CIQJFGRQHQ45VCQLM7AJNF2GF5UHUAGGHC6LLAH6VYDEKLQMD4QLILY.data new file mode 100644 index 0000000..62d1c29 --- /dev/null +++ b/packages/subgraph/graph-node/data/ipfs/blocks/IL/CIQJFGRQHQ45VCQLM7AJNF2GF5UHUAGGHC6LLAH6VYDEKLQMD4QLILY.data @@ -0,0 +1,8 @@ + +ĆœCome hang out in our IRC chat room if you have any questions. + +Contact the ipfs dev team: +- Bugs: https://github.com/ipfs/go-ipfs/issues +- Help: irc.freenode.org/#ipfs +- Email: dev@ipfs.io +œ \ No newline at end of file diff --git a/packages/subgraph/graph-node/data/ipfs/blocks/IY/CIQB4655YD5GLBB7WWEUAHCO6QONU5ICBONAA5JEPBIOEIVZ5RXTIYY.data b/packages/subgraph/graph-node/data/ipfs/blocks/IY/CIQB4655YD5GLBB7WWEUAHCO6QONU5ICBONAA5JEPBIOEIVZ5RXTIYY.data new file mode 100644 index 0000000..4c72ab6 Binary files /dev/null and b/packages/subgraph/graph-node/data/ipfs/blocks/IY/CIQB4655YD5GLBB7WWEUAHCO6QONU5ICBONAA5JEPBIOEIVZ5RXTIYY.data differ diff --git a/packages/subgraph/graph-node/data/ipfs/blocks/JN/CIQPHMHGQLLZXC32FQQW2YVM4KGFORVFJAQYY55VK3WJGLZ2MS4RJNQ.data b/packages/subgraph/graph-node/data/ipfs/blocks/JN/CIQPHMHGQLLZXC32FQQW2YVM4KGFORVFJAQYY55VK3WJGLZ2MS4RJNQ.data new file mode 100644 index 0000000..2708836 --- /dev/null +++ b/packages/subgraph/graph-node/data/ipfs/blocks/JN/CIQPHMHGQLLZXC32FQQW2YVM4KGFORVFJAQYY55VK3WJGLZ2MS4RJNQ.data @@ -0,0 +1,3 @@ + + +ipfs \ No newline at end of file diff --git a/packages/subgraph/graph-node/data/ipfs/blocks/KE/CIQD44K6LTXM6PHWK2RHB3G2VCYFPMVBTALE572GSMETJGBJTELFKEI.data b/packages/subgraph/graph-node/data/ipfs/blocks/KE/CIQD44K6LTXM6PHWK2RHB3G2VCYFPMVBTALE572GSMETJGBJTELFKEI.data new file mode 100644 index 0000000..f741cda --- /dev/null +++ b/packages/subgraph/graph-node/data/ipfs/blocks/KE/CIQD44K6LTXM6PHWK2RHB3G2VCYFPMVBTALE572GSMETJGBJTELFKEI.data @@ -0,0 +1,3 @@ +W +" jöÔnČÒáŚUÒ_0BÂ%äFçĄéÓŰì.QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEBÎ + \ No newline at end of file diff --git a/packages/subgraph/graph-node/data/ipfs/blocks/MJ/CIQHQFRJK4MU2CVNFR3QG6KZB3FZG6OG7EBI4SUNB5K4S4T5UVECMJA.data b/packages/subgraph/graph-node/data/ipfs/blocks/MJ/CIQHQFRJK4MU2CVNFR3QG6KZB3FZG6OG7EBI4SUNB5K4S4T5UVECMJA.data new file mode 100644 index 0000000..7d8ed98 --- /dev/null +++ b/packages/subgraph/graph-node/data/ipfs/blocks/MJ/CIQHQFRJK4MU2CVNFR3QG6KZB3FZG6OG7EBI4SUNB5K4S4T5UVECMJA.data @@ -0,0 +1,3 @@ +W +" ' śM­ü8gÉԗ€öž%ŸS€Ö.A92Ż––Ì )œ.QmQy6xmJhrcC5QLboAcGFcAE1tC8CrwDVkrHdEYJkLscrQœ + \ No newline at end of file diff --git a/packages/subgraph/graph-node/data/ipfs/blocks/N6/CIQGFYPT5OBMRC7ZMUFC2R3ZQPKOGBMHJEDDFEVS5ALYBKIZCXPTN6Y.data b/packages/subgraph/graph-node/data/ipfs/blocks/N6/CIQGFYPT5OBMRC7ZMUFC2R3ZQPKOGBMHJEDDFEVS5ALYBKIZCXPTN6Y.data new file mode 100644 index 0000000..e8711ca Binary files /dev/null and b/packages/subgraph/graph-node/data/ipfs/blocks/N6/CIQGFYPT5OBMRC7ZMUFC2R3ZQPKOGBMHJEDDFEVS5ALYBKIZCXPTN6Y.data differ diff --git a/packages/subgraph/graph-node/data/ipfs/blocks/OO/CIQBT4N7PS5IZ5IG2ZOUGKFK27IE33WKGJNDW2TY3LSBNQ34R6OVOOQ.data b/packages/subgraph/graph-node/data/ipfs/blocks/OO/CIQBT4N7PS5IZ5IG2ZOUGKFK27IE33WKGJNDW2TY3LSBNQ34R6OVOOQ.data new file mode 100644 index 0000000..7703482 --- /dev/null +++ b/packages/subgraph/graph-node/data/ipfs/blocks/OO/CIQBT4N7PS5IZ5IG2ZOUGKFK27IE33WKGJNDW2TY3LSBNQ34R6OVOOQ.data @@ -0,0 +1,27 @@ + +’ Š IPFS Alpha Security Notes + +We try hard to ensure our system is safe and robust, but all software +has bugs, especially new software. This distribution is meant to be an +alpha preview, don't use it for anything mission critical. + +Please note the following: + +- This is alpha software and has not been audited. It is our goal + to conduct a proper security audit once we close in on a 1.0 release. + +- ipfs is a networked program, and may have serious undiscovered + vulnerabilities. It is written in Go, and we do not execute any + user provided data. But please point any problems out to us in a + github issue, or email security@ipfs.io privately. + +- security@ipfs.io GPG key: + - 4B9665FB 92636D17 7C7A86D3 50AAE8A9 59B13AF3 + - https://pgp.mit.edu/pks/lookup?op=get&search=0x50AAE8A959B13AF3 + +- ipfs uses encryption for all communication, but it's NOT PROVEN SECURE + YET! It may be totally broken. For now, the code is included to make + sure we benchmark our operations with encryption in mind. In the future, + there will be an "unsafe" mode for high performance intranet apps. + If this is a blocking feature for you, please contact us. +Š \ No newline at end of file diff --git a/packages/subgraph/graph-node/data/ipfs/blocks/PU/CIQIOIEBGMROIZACBESMVMJKD6UCC7EE3ZAZ5KWI4RCC4RDEWW77PUA.data b/packages/subgraph/graph-node/data/ipfs/blocks/PU/CIQIOIEBGMROIZACBESMVMJKD6UCC7EE3ZAZ5KWI4RCC4RDEWW77PUA.data new file mode 100644 index 0000000..8c41983 --- /dev/null +++ b/packages/subgraph/graph-node/data/ipfs/blocks/PU/CIQIOIEBGMROIZACBESMVMJKD6UCC7EE3ZAZ5KWI4RCC4RDEWW77PUA.data @@ -0,0 +1,19 @@ + +ż·type Greeting @entity { + id: ID! + sender: Sender! + greeting: String! + premium: Boolean + value: BigInt + createdAt: BigInt! + transactionHash: String! +} + +type Sender @entity { + id: ID! + address: Bytes! + greetings: [Greeting!] @derivedFrom(field: "sender") + createdAt: BigInt! + greetingCount: BigInt! +} +· \ No newline at end of file diff --git a/packages/subgraph/graph-node/data/ipfs/blocks/QD/CIQL4QZR6XGWMPEV5Q2FCTDFD7MF3G5OOC5CMEDUHNA5VXYZVDLFQDA.data b/packages/subgraph/graph-node/data/ipfs/blocks/QD/CIQL4QZR6XGWMPEV5Q2FCTDFD7MF3G5OOC5CMEDUHNA5VXYZVDLFQDA.data new file mode 100644 index 0000000..9a3a8ab --- /dev/null +++ b/packages/subgraph/graph-node/data/ipfs/blocks/QD/CIQL4QZR6XGWMPEV5Q2FCTDFD7MF3G5OOC5CMEDUHNA5VXYZVDLFQDA.data @@ -0,0 +1,3 @@ +. +" ' śM­ü8gÉԗ€öž%ŸS€Ö.A92Ż––Ì )œaboutœ + \ No newline at end of file diff --git a/packages/subgraph/graph-node/data/ipfs/blocks/QE/CIQDTRYQTNWVKTQ66XN74JSVOBN5EYVPXMBLPJU7BURFWKLDGUUYQEQ.data b/packages/subgraph/graph-node/data/ipfs/blocks/QE/CIQDTRYQTNWVKTQ66XN74JSVOBN5EYVPXMBLPJU7BURFWKLDGUUYQEQ.data new file mode 100644 index 0000000..3c52263 --- /dev/null +++ b/packages/subgraph/graph-node/data/ipfs/blocks/QE/CIQDTRYQTNWVKTQ66XN74JSVOBN5EYVPXMBLPJU7BURFWKLDGUUYQEQ.data @@ -0,0 +1,3 @@ +W +" Lń©6ˆȘ?w܁ńu5üSG2ÂG«„*ĆK6țăŻ.QmTTkQe22hx7Ajt1yxa2VCZzHFWAYHfQyfVRDsJ8qJsMyGä + \ No newline at end of file diff --git a/packages/subgraph/graph-node/data/ipfs/blocks/R3/CIQBED3K6YA5I3QQWLJOCHWXDRK5EXZQILBCKAPEDUJENZ5B5HJ5R3A.data b/packages/subgraph/graph-node/data/ipfs/blocks/R3/CIQBED3K6YA5I3QQWLJOCHWXDRK5EXZQILBCKAPEDUJENZ5B5HJ5R3A.data new file mode 100644 index 0000000..389e111 --- /dev/null +++ b/packages/subgraph/graph-node/data/ipfs/blocks/R3/CIQBED3K6YA5I3QQWLJOCHWXDRK5EXZQILBCKAPEDUJENZ5B5HJ5R3A.data @@ -0,0 +1,28 @@ + +ËĂHello and Welcome to IPFS! + +██╗██████╗ ███████╗███████╗ +██║██╔══██╗██╔════╝██╔════╝ +██║██████╔╝█████╗ ███████╗ +██║██╔═══╝ ██╔══╝ ╚════██║ +██║██║ ██║ ███████║ +╚═╝╚═╝ ╚═╝ ╚══════╝ + +If you're seeing this, you have successfully installed +IPFS and are now interfacing with the ipfs merkledag! + + ------------------------------------------------------- +| Warning: | +| This is alpha software. Use at your own discretion! | +| Much is missing or lacking polish. There are bugs. | +| Not yet secure. Read the security notes for more. | + ------------------------------------------------------- + +Check out some of the other files in this directory: + + ./about + ./help + ./quick-start <-- usage examples + ./readme <-- this file + ./security-notes +Ă \ No newline at end of file diff --git a/packages/subgraph/graph-node/data/ipfs/blocks/RO/CIQDRD2UT66U4EATJW53PSVWMFFPGNAN42PVWMDLHJD6FA5EVNNZROI.data b/packages/subgraph/graph-node/data/ipfs/blocks/RO/CIQDRD2UT66U4EATJW53PSVWMFFPGNAN42PVWMDLHJD6FA5EVNNZROI.data new file mode 100644 index 0000000..6908884 --- /dev/null +++ b/packages/subgraph/graph-node/data/ipfs/blocks/RO/CIQDRD2UT66U4EATJW53PSVWMFFPGNAN42PVWMDLHJD6FA5EVNNZROI.data @@ -0,0 +1,3 @@ +W +" UT†ĐŸiŸ&ê›ÌpO)Ê­ÀŸSvÏ|Ę&Đș .QmU5k7ter3RdjZXu3sHghsga1UQtrztnQxmTL22nPnsu3g + \ No newline at end of file diff --git a/packages/subgraph/graph-node/data/ipfs/blocks/SHARDING b/packages/subgraph/graph-node/data/ipfs/blocks/SHARDING new file mode 100644 index 0000000..a153331 --- /dev/null +++ b/packages/subgraph/graph-node/data/ipfs/blocks/SHARDING @@ -0,0 +1 @@ +/repo/flatfs/shard/v1/next-to-last/2 diff --git a/packages/subgraph/graph-node/data/ipfs/blocks/TP/CIQCODPXR5G237BYM7E5JF4A624CLH2TQDLC4QI6HEZK7FUWZQESTPI.data b/packages/subgraph/graph-node/data/ipfs/blocks/TP/CIQCODPXR5G237BYM7E5JF4A624CLH2TQDLC4QI6HEZK7FUWZQESTPI.data new file mode 100644 index 0000000..e2abc07 --- /dev/null +++ b/packages/subgraph/graph-node/data/ipfs/blocks/TP/CIQCODPXR5G237BYM7E5JF4A624CLH2TQDLC4QI6HEZK7FUWZQESTPI.data @@ -0,0 +1,55 @@ + +™ ‘ + IPFS -- Inter-Planetary File system + +IPFS is a global, versioned, peer-to-peer filesystem. It combines good ideas +from Git, BitTorrent, Kademlia, SFS, and the Web. It is like a single bit- +torrent swarm, exchanging git objects. IPFS provides an interface as simple +as the HTTP web, but with permanence built-in. You can also mount the world +at /ipfs. + +IPFS is a protocol: +- defines a content-addressed file system +- coordinates content delivery +- combines Kademlia + BitTorrent + Git + +IPFS is a filesystem: +- has directories and files +- mountable filesystem (via FUSE) + +IPFS is a web: +- can be used to view documents like the web +- files accessible via HTTP at `http://ipfs.io/` +- browsers or extensions can learn to use `ipfs://` directly +- hash-addressed content guarantees the authenticity + +IPFS is modular: +- connection layer over any network protocol +- routing layer +- uses a routing layer DHT (kademlia/coral) +- uses a path-based naming service +- uses BitTorrent-inspired block exchange + +IPFS uses crypto: +- cryptographic-hash content addressing +- block-level deduplication +- file integrity + versioning +- filesystem-level encryption + signing support + +IPFS is p2p: +- worldwide peer-to-peer file transfers +- completely decentralized architecture +- **no** central point of failure + +IPFS is a CDN: +- add a file to the filesystem locally, and it's now available to the world +- caching-friendly (content-hash naming) +- BitTorrent-based bandwidth distribution + +IPFS has a name service: +- IPNS, an SFS inspired name system +- global namespace based on PKI +- serves to build trust chains +- compatible with other NSes +- can map DNS, .onion, .bit, etc to IPNS +‘ \ No newline at end of file diff --git a/packages/subgraph/graph-node/data/ipfs/blocks/U2/CIQHFTCY7XL57YWLVDQ6UAXUOND3ADYQYJKYXA6G7A5IMD7SMO22U2A.data b/packages/subgraph/graph-node/data/ipfs/blocks/U2/CIQHFTCY7XL57YWLVDQ6UAXUOND3ADYQYJKYXA6G7A5IMD7SMO22U2A.data new file mode 100644 index 0000000..6852283 --- /dev/null +++ b/packages/subgraph/graph-node/data/ipfs/blocks/U2/CIQHFTCY7XL57YWLVDQ6UAXUOND3ADYQYJKYXA6G7A5IMD7SMO22U2A.data @@ -0,0 +1,5 @@ +. +" ' śM­ü8gÉԗ€öž%ŸS€Ö.A92Ż––Ì )œaboutœ - +" UT†ĐŸiŸ&ê›ÌpO)Ê­ÀŸSvÏ|Ę&Đș helpÂ/ +" jöÔnČÒáŚUÒ_0BÂ%äFçĄéÓŰìreadmeÎ + \ No newline at end of file diff --git a/packages/subgraph/graph-node/data/ipfs/blocks/UC/CIQFKVEG2CPWTPRG5KNRUAWMOABRSTYUFHFK3QF6KN3M67G5E3ILUCY.data b/packages/subgraph/graph-node/data/ipfs/blocks/UC/CIQFKVEG2CPWTPRG5KNRUAWMOABRSTYUFHFK3QF6KN3M67G5E3ILUCY.data new file mode 100644 index 0000000..d357459 --- /dev/null +++ b/packages/subgraph/graph-node/data/ipfs/blocks/UC/CIQFKVEG2CPWTPRG5KNRUAWMOABRSTYUFHFK3QF6KN3M67G5E3ILUCY.data @@ -0,0 +1,9 @@ + +ż·Some helpful resources for finding your way around ipfs: + +- quick-start: a quick show of various ipfs features. +- ipfs commands: a list of all commands +- ipfs --help: every command describes itself +- https://github.com/ipfs/go-ipfs -- the src repository +- #ipfs on irc.freenode.org -- the community IRC channel +· \ No newline at end of file diff --git a/packages/subgraph/graph-node/data/ipfs/blocks/V3/CIQAPZYJAKUKALYI4YTB5PUMEN5BZYZHUQZWGFL4Q3HZUV26SYX2V3Q.data b/packages/subgraph/graph-node/data/ipfs/blocks/V3/CIQAPZYJAKUKALYI4YTB5PUMEN5BZYZHUQZWGFL4Q3HZUV26SYX2V3Q.data new file mode 100644 index 0000000..cecf305 Binary files /dev/null and b/packages/subgraph/graph-node/data/ipfs/blocks/V3/CIQAPZYJAKUKALYI4YTB5PUMEN5BZYZHUQZWGFL4Q3HZUV26SYX2V3Q.data differ diff --git a/packages/subgraph/graph-node/data/ipfs/blocks/VN/CIQPEOA2TS3RMLOBOF55ZOEZE3TNBQG3HCNFOYC3BATAIJBOIE5FVNY.data b/packages/subgraph/graph-node/data/ipfs/blocks/VN/CIQPEOA2TS3RMLOBOF55ZOEZE3TNBQG3HCNFOYC3BATAIJBOIE5FVNY.data new file mode 100644 index 0000000..64b2d9e --- /dev/null +++ b/packages/subgraph/graph-node/data/ipfs/blocks/VN/CIQPEOA2TS3RMLOBOF55ZOEZE3TNBQG3HCNFOYC3BATAIJBOIE5FVNY.data @@ -0,0 +1,3 @@ +W +" ńż|șŒőÖ]C(ȘŚĐMîÊ2Z;jxÚäĂ|W:.QmQ5vhrL7uv6tuoN9KeVBwd4PwfQkXdVVmDLUZuTNxqgvm• + \ No newline at end of file diff --git a/packages/subgraph/graph-node/data/ipfs/blocks/VY/CIQPP4W54EMBWDJXBYCCE46WVTK6SFFYFGYOAADLLOCXDSYD6BPEVYI.data b/packages/subgraph/graph-node/data/ipfs/blocks/VY/CIQPP4W54EMBWDJXBYCCE46WVTK6SFFYFGYOAADLLOCXDSYD6BPEVYI.data new file mode 100644 index 0000000..2e8e3f3 Binary files /dev/null and b/packages/subgraph/graph-node/data/ipfs/blocks/VY/CIQPP4W54EMBWDJXBYCCE46WVTK6SFFYFGYOAADLLOCXDSYD6BPEVYI.data differ diff --git a/packages/subgraph/graph-node/data/ipfs/blocks/WG/CIQMBOR7N3GN54QCOKQHQK773KYXD5LHDYVT7BYXGZM42GDIFORMWGA.data b/packages/subgraph/graph-node/data/ipfs/blocks/WG/CIQMBOR7N3GN54QCOKQHQK773KYXD5LHDYVT7BYXGZM42GDIFORMWGA.data new file mode 100644 index 0000000..50c9ebb --- /dev/null +++ b/packages/subgraph/graph-node/data/ipfs/blocks/WG/CIQMBOR7N3GN54QCOKQHQK773KYXD5LHDYVT7BYXGZM42GDIFORMWGA.data @@ -0,0 +1,140 @@ + +ÍĆ[ + { + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "greetingSetter", + "type": "address" + }, + { + "indexed": false, + "internalType": "string", + "name": "newGreeting", + "type": "string" + }, + { + "indexed": false, + "internalType": "bool", + "name": "premium", + "type": "bool" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "GreetingChange", + "type": "event" + }, + { + "inputs": [], + "name": "greeting", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "premium", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "_newGreeting", + "type": "string" + } + ], + "name": "setGreeting", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "totalCounter", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "userGreetingCounter", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +]Ć \ No newline at end of file diff --git a/packages/subgraph/graph-node/data/ipfs/blocks/X3/CIQFTFEEHEDF6KLBT32BFAGLXEZL4UWFNWM4LFTLMXQBCERZ6CMLX3Y.data b/packages/subgraph/graph-node/data/ipfs/blocks/X3/CIQFTFEEHEDF6KLBT32BFAGLXEZL4UWFNWM4LFTLMXQBCERZ6CMLX3Y.data new file mode 100644 index 0000000..9553a94 --- /dev/null +++ b/packages/subgraph/graph-node/data/ipfs/blocks/X3/CIQFTFEEHEDF6KLBT32BFAGLXEZL4UWFNWM4LFTLMXQBCERZ6CMLX3Y.data @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/packages/subgraph/graph-node/data/ipfs/blocks/XV/CIQGAS6MQJCEC37C2IIH5ZFYJCSTT7TCKJP3F7SLGNVSDVZSMACCXVA.data b/packages/subgraph/graph-node/data/ipfs/blocks/XV/CIQGAS6MQJCEC37C2IIH5ZFYJCSTT7TCKJP3F7SLGNVSDVZSMACCXVA.data new file mode 100644 index 0000000..726f4f4 Binary files /dev/null and b/packages/subgraph/graph-node/data/ipfs/blocks/XV/CIQGAS6MQJCEC37C2IIH5ZFYJCSTT7TCKJP3F7SLGNVSDVZSMACCXVA.data differ diff --git a/packages/subgraph/graph-node/data/ipfs/blocks/_README b/packages/subgraph/graph-node/data/ipfs/blocks/_README new file mode 100644 index 0000000..23cb090 --- /dev/null +++ b/packages/subgraph/graph-node/data/ipfs/blocks/_README @@ -0,0 +1,30 @@ +This is a repository of IPLD objects. Each IPLD object is in a single file, +named .data. Where is the +"base32" encoding of the CID (as specified in +https://github.com/multiformats/multibase) without the 'B' prefix. +All the object files are placed in a tree of directories, based on a +function of the CID. This is a form of sharding similar to +the objects directory in git repositories. Previously, we used +prefixes, we now use the next-to-last two charters. + + func NextToLast(base32cid string) { + nextToLastLen := 2 + offset := len(base32cid) - nextToLastLen - 1 + return str[offset : offset+nextToLastLen] + } + +For example, an object with a base58 CIDv1 of + + zb2rhYSxw4ZjuzgCnWSt19Q94ERaeFhu9uSqRgjSdx9bsgM6f + +has a base32 CIDv1 of + + BAFKREIA22FLID5AJ2KU7URG47MDLROZIH6YF2KALU2PWEFPVI37YLKRSCA + +and will be placed at + + SC/AFKREIA22FLID5AJ2KU7URG47MDLROZIH6YF2KALU2PWEFPVI37YLKRSCA.data + +with 'SC' being the last-to-next two characters and the 'B' at the +beginning of the CIDv1 string is the multibase prefix that is not +stored in the filename. diff --git a/packages/subgraph/graph-node/data/ipfs/blocks/diskUsage.cache b/packages/subgraph/graph-node/data/ipfs/blocks/diskUsage.cache new file mode 100644 index 0000000..fceeed3 --- /dev/null +++ b/packages/subgraph/graph-node/data/ipfs/blocks/diskUsage.cache @@ -0,0 +1 @@ +{"diskUsage":183838,"accuracy":"initial-exact"} diff --git a/packages/subgraph/graph-node/data/ipfs/config b/packages/subgraph/graph-node/data/ipfs/config new file mode 100644 index 0000000..5d5abaa --- /dev/null +++ b/packages/subgraph/graph-node/data/ipfs/config @@ -0,0 +1,160 @@ +{ + "API": { + "HTTPHeaders": {} + }, + "Addresses": { + "API": "/ip4/0.0.0.0/tcp/5001", + "Announce": [], + "Gateway": "/ip4/0.0.0.0/tcp/8080", + "NoAnnounce": [], + "Swarm": [ + "/ip4/0.0.0.0/tcp/4001", + "/ip6/::/tcp/4001", + "/ip4/0.0.0.0/udp/4001/quic", + "/ip6/::/udp/4001/quic" + ] + }, + "AutoNAT": {}, + "Bootstrap": [ + "/ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", + "/ip4/104.131.131.82/udp/4001/quic/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", + "/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN", + "/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa", + "/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb", + "/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt" + ], + "DNS": { + "Resolvers": {} + }, + "Datastore": { + "BloomFilterSize": 0, + "GCPeriod": "1h", + "HashOnRead": false, + "Spec": { + "mounts": [ + { + "child": { + "path": "blocks", + "shardFunc": "/repo/flatfs/shard/v1/next-to-last/2", + "sync": true, + "type": "flatfs" + }, + "mountpoint": "/blocks", + "prefix": "flatfs.datastore", + "type": "measure" + }, + { + "child": { + "compression": "none", + "path": "datastore", + "type": "levelds" + }, + "mountpoint": "/", + "prefix": "leveldb.datastore", + "type": "measure" + } + ], + "type": "mount" + }, + "StorageGCWatermark": 90, + "StorageMax": "10GB" + }, + "Discovery": { + "MDNS": { + "Enabled": true, + "Interval": 10 + } + }, + "Experimental": { + "AcceleratedDHTClient": false, + "FilestoreEnabled": false, + "GraphsyncEnabled": false, + "Libp2pStreamMounting": false, + "P2pHttpProxy": false, + "ShardingEnabled": false, + "StrategicProviding": false, + "UrlstoreEnabled": false + }, + "Gateway": { + "APICommands": [], + "HTTPHeaders": { + "Access-Control-Allow-Headers": [ + "X-Requested-With", + "Range", + "User-Agent" + ], + "Access-Control-Allow-Methods": [ + "GET" + ], + "Access-Control-Allow-Origin": [ + "*" + ] + }, + "NoDNSLink": false, + "NoFetch": false, + "PathPrefixes": [], + "PublicGateways": null, + "RootRedirect": "", + "Writable": false + }, + "Identity": { + "PeerID": "12D3KooWEFoobCSqXhUn9syMidxZCNtYJ4oUpe2HsR4T4JDdyjUp", + "PrivKey": "CAESQGzrvrJUrhk2OyVZjRbZQyRdRRH3iSMe9pz7z19x4B1oQfNA+wug6ZTyQ3Z7q85jUA0q9WglWLBA36hUD5w3onU=" + }, + "Internal": {}, + "Ipns": { + "RecordLifetime": "", + "RepublishPeriod": "", + "ResolveCacheSize": 128 + }, + "Migration": { + "DownloadSources": [], + "Keep": "" + }, + "Mounts": { + "FuseAllowOther": false, + "IPFS": "/ipfs", + "IPNS": "/ipns" + }, + "Peering": { + "Peers": null + }, + "Pinning": { + "RemoteServices": {} + }, + "Plugins": { + "Plugins": null + }, + "Provider": { + "Strategy": "" + }, + "Pubsub": { + "DisableSigning": false, + "Router": "" + }, + "Reprovider": { + "Interval": "12h", + "Strategy": "all" + }, + "Routing": { + "Type": "dht" + }, + "Swarm": { + "AddrFilters": null, + "ConnMgr": { + "GracePeriod": "20s", + "HighWater": 900, + "LowWater": 600, + "Type": "basic" + }, + "DisableBandwidthMetrics": false, + "DisableNatPortMap": false, + "EnableAutoRelay": false, + "EnableRelayHop": false, + "Transports": { + "Multiplexers": {}, + "Network": {}, + "Security": {} + } + } +} \ No newline at end of file diff --git a/packages/subgraph/graph-node/data/ipfs/datastore/000002.ldb b/packages/subgraph/graph-node/data/ipfs/datastore/000002.ldb new file mode 100644 index 0000000..de8f3b5 Binary files /dev/null and b/packages/subgraph/graph-node/data/ipfs/datastore/000002.ldb differ diff --git a/packages/subgraph/graph-node/data/ipfs/datastore/000005.ldb b/packages/subgraph/graph-node/data/ipfs/datastore/000005.ldb new file mode 100644 index 0000000..56ed6a4 Binary files /dev/null and b/packages/subgraph/graph-node/data/ipfs/datastore/000005.ldb differ diff --git a/packages/subgraph/graph-node/data/ipfs/datastore/000010.log b/packages/subgraph/graph-node/data/ipfs/datastore/000010.log new file mode 100644 index 0000000..2a108a0 Binary files /dev/null and b/packages/subgraph/graph-node/data/ipfs/datastore/000010.log differ diff --git a/packages/subgraph/graph-node/data/ipfs/datastore/CURRENT b/packages/subgraph/graph-node/data/ipfs/datastore/CURRENT new file mode 100644 index 0000000..5b54010 --- /dev/null +++ b/packages/subgraph/graph-node/data/ipfs/datastore/CURRENT @@ -0,0 +1 @@ +MANIFEST-000011 diff --git a/packages/subgraph/graph-node/data/ipfs/datastore/CURRENT.bak b/packages/subgraph/graph-node/data/ipfs/datastore/CURRENT.bak new file mode 100644 index 0000000..6ba31a3 --- /dev/null +++ b/packages/subgraph/graph-node/data/ipfs/datastore/CURRENT.bak @@ -0,0 +1 @@ +MANIFEST-000009 diff --git a/packages/subgraph/graph-node/data/ipfs/datastore/LOCK b/packages/subgraph/graph-node/data/ipfs/datastore/LOCK new file mode 100644 index 0000000..e69de29 diff --git a/packages/subgraph/graph-node/data/ipfs/datastore/LOG b/packages/subgraph/graph-node/data/ipfs/datastore/LOG new file mode 100644 index 0000000..1f485db --- /dev/null +++ b/packages/subgraph/graph-node/data/ipfs/datastore/LOG @@ -0,0 +1,52 @@ +=============== Jun 1, 2024 (UTC) =============== +14:12:36.096134 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +14:12:36.098294 db@open opening +14:12:36.098829 version@stat F·[] S·0B[] Sc·[] +14:12:36.099718 db@janitor F·2 G·0 +14:12:36.099758 db@open done T·1.445411ms +14:12:36.181474 db@close closing +14:12:36.181566 db@close done T·73.493”s +=============== Jun 1, 2024 (UTC) =============== +14:12:36.182468 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +14:12:36.182655 version@stat F·[] S·0B[] Sc·[] +14:12:36.182661 db@open opening +14:12:36.182713 journal@recovery F·1 +14:12:36.183003 journal@recovery recovering @1 +14:12:36.185622 memdb@flush created L0@2 N·12 S·1KiB "/pi..YmY,v10":"/pr..crQ,v12" +14:12:36.187206 version@stat F·[1] S·1KiB[1KiB] Sc·[0.25] +14:12:36.195275 db@janitor F·3 G·0 +14:12:36.195300 db@open done T·12.632551ms +14:12:36.203892 db@close closing +14:12:36.204004 db@close done T·105.391”s +=============== Jun 1, 2024 (UTC) =============== +14:12:36.251916 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +14:12:36.252114 version@stat F·[1] S·1KiB[1KiB] Sc·[0.25] +14:12:36.252121 db@open opening +14:12:36.252162 journal@recovery F·1 +14:12:36.252918 journal@recovery recovering @3 +14:12:36.254343 memdb@flush created L0@5 N·6 S·1KiB "/F5..E5I,v19":"/pi..rty,v14" +14:12:36.254623 version@stat F·[2] S·2KiB[2KiB] Sc·[0.50] +14:12:36.257115 db@janitor F·4 G·0 +14:12:36.257138 db@open done T·5.010401ms +14:12:36.259806 db@close closing +14:12:36.259859 db@close done T·51.694”s +=============== Jun 1, 2024 (UTC) =============== +14:12:36.311005 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +14:12:36.311255 version@stat F·[2] S·2KiB[2KiB] Sc·[0.50] +14:12:36.311262 db@open opening +14:12:36.311314 journal@recovery F·1 +14:12:36.311571 journal@recovery recovering @6 +14:12:36.313790 version@stat F·[2] S·2KiB[2KiB] Sc·[0.50] +14:12:36.316874 db@janitor F·4 G·0 +14:12:36.316916 db@open done T·5.638668ms +14:12:36.319921 db@close closing +14:12:36.319978 db@close done T·56.051”s +=============== Jun 1, 2024 (UTC) =============== +14:12:36.370417 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed +14:12:36.370643 version@stat F·[2] S·2KiB[2KiB] Sc·[0.50] +14:12:36.370650 db@open opening +14:12:36.370701 journal@recovery F·1 +14:12:36.371182 journal@recovery recovering @8 +14:12:36.372367 version@stat F·[2] S·2KiB[2KiB] Sc·[0.50] +14:12:36.381606 db@janitor F·4 G·0 +14:12:36.381633 db@open done T·10.974927ms diff --git a/packages/subgraph/graph-node/data/ipfs/datastore/MANIFEST-000011 b/packages/subgraph/graph-node/data/ipfs/datastore/MANIFEST-000011 new file mode 100644 index 0000000..dabaadd Binary files /dev/null and b/packages/subgraph/graph-node/data/ipfs/datastore/MANIFEST-000011 differ diff --git a/packages/subgraph/graph-node/data/ipfs/datastore_spec b/packages/subgraph/graph-node/data/ipfs/datastore_spec new file mode 100644 index 0000000..7bf9626 --- /dev/null +++ b/packages/subgraph/graph-node/data/ipfs/datastore_spec @@ -0,0 +1 @@ +{"mounts":[{"mountpoint":"/blocks","path":"blocks","shardFunc":"/repo/flatfs/shard/v1/next-to-last/2","type":"flatfs"},{"mountpoint":"/","path":"datastore","type":"levelds"}],"type":"mount"} \ No newline at end of file diff --git a/packages/subgraph/graph-node/data/ipfs/repo.lock b/packages/subgraph/graph-node/data/ipfs/repo.lock new file mode 100644 index 0000000..e69de29 diff --git a/packages/subgraph/graph-node/data/ipfs/version b/packages/subgraph/graph-node/data/ipfs/version new file mode 100644 index 0000000..b4de394 --- /dev/null +++ b/packages/subgraph/graph-node/data/ipfs/version @@ -0,0 +1 @@ +11 diff --git a/packages/subgraph/graph-node/docker-compose.yml b/packages/subgraph/graph-node/docker-compose.yml new file mode 100644 index 0000000..ba3b66e --- /dev/null +++ b/packages/subgraph/graph-node/docker-compose.yml @@ -0,0 +1,45 @@ +version: "3" +services: + graph-node: + image: graphprotocol/graph-node:v0.32.0 + ports: + - "8000:8000" + - "8001:8001" + - "8020:8020" + - "8030:8030" + - "8040:8040" + depends_on: + - ipfs + - postgres + environment: + postgres_host: postgres + postgres_user: graph-node + postgres_pass: let-me-in + postgres_db: graph-node + ipfs: "ipfs:5001" + ethereum: "localhost:http://host.docker.internal:8545" + GRAPH_LOG: info + extra_hosts: + - "host.docker.internal:host-gateway" + ipfs: + image: ipfs/go-ipfs:v0.10.0 + ports: + - "5001:5001" + volumes: + - ./data/ipfs:/data/ipfs + postgres: + image: postgres + ports: + - "5432:5432" + command: + [ + "postgres", + "-cshared_preload_libraries=pg_stat_statements" + ] + environment: + POSTGRES_USER: graph-node + POSTGRES_PASSWORD: let-me-in + POSTGRES_DB: graph-node + POSTGRES_INITDB_ARGS: "-E UTF8 --locale=C" + volumes: + - ./data/postgres:/var/lib/postgresql/data diff --git a/packages/subgraph/graph-node/matchstick/Dockerfile b/packages/subgraph/graph-node/matchstick/Dockerfile new file mode 100644 index 0000000..7edfd76 --- /dev/null +++ b/packages/subgraph/graph-node/matchstick/Dockerfile @@ -0,0 +1,22 @@ +FROM --platform=linux/x86_64 ubuntu:22.04 + +ARG DEBIAN_FRONTEND=noninteractive + +ENV ARGS="" + +RUN apt update \ + && apt install -y sudo curl postgresql postgresql-contrib + +RUN curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash - \ + && sudo apt-get install -y nodejs + +RUN curl -OL https://github.com/LimeChain/matchstick/releases/download/0.6.0/binary-linux-22 \ + && chmod a+x binary-linux-22 + +RUN mkdir matchstick +WORKDIR /matchstick + +# Commenting out for now as it seems there's no need to copy when using bind mount +# COPY ./ . + +CMD ../binary-linux-22 ${ARGS} \ No newline at end of file diff --git a/packages/subgraph/networks.json b/packages/subgraph/networks.json new file mode 100644 index 0000000..2c058db --- /dev/null +++ b/packages/subgraph/networks.json @@ -0,0 +1,10 @@ +{ + "localhost": { + "YourContract": { + "address": "0x5FbDB2315678afecb367f032d93F642f64180aa3" + }, + "Think2Earn": { + "address": "0x5FbDB2315678afecb367f032d93F642f64180aa3" + } + } +} \ No newline at end of file diff --git a/packages/subgraph/package.json b/packages/subgraph/package.json new file mode 100644 index 0000000..8cbd572 --- /dev/null +++ b/packages/subgraph/package.json @@ -0,0 +1,30 @@ +{ + "name": "@se-2/subgraph", + "version": "0.0.1", + "type": "module", + "scripts": { + "abi-copy": "node --loader ts-node/esm --experimental-specifier-resolution=node scripts/abi_copy.ts", + "codegen": "graph codegen", + "build": "graph build", + "deploy": "graph deploy --node https://api.thegraph.com/deploy/ --ipfs https://api.thegraph.com/ipfs/ GITHUB_USERNAME/your-contract", + "local-create": "graph create --node http://localhost:8020/ scaffold-eth/your-contract", + "local-remove": "graph remove --node http://localhost:8020/ scaffold-eth/your-contract", + "local-deploy": "graph deploy --node http://localhost:8020/ --ipfs http://localhost:5001 scaffold-eth/your-contract", + "local-ship": "yarn abi-copy && yarn codegen && yarn build --network localhost && yarn local-deploy", + "test": "graph test", + "run-node": "cd graph-node && docker-compose up", + "stop-node": "cd graph-node && docker-compose down", + "clean-node": "rm -rf graph-node/data/" + }, + "dependencies": { + "@graphprotocol/graph-cli": "^0.55.0", + "@graphprotocol/graph-ts": "^0.31.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" + }, + "devDependencies": { + "@types/chalk": "^2.2.0", + "@types/node": "^20.11.17", + "matchstick-as": "^0.6.0" + } +} diff --git a/packages/subgraph/scripts/abi_copy.ts b/packages/subgraph/scripts/abi_copy.ts new file mode 100644 index 0000000..3de4e3c --- /dev/null +++ b/packages/subgraph/scripts/abi_copy.ts @@ -0,0 +1,71 @@ +import * as fs from "fs"; +import chalk from "chalk"; + +const graphDir = "./"; +const deploymentsDir = "../hardhat/deployments"; + +function publishContract(contractName: string, networkName: string) { + try { + let contract = fs + .readFileSync(`${deploymentsDir}/${networkName}/${contractName}.json`) + .toString(); + let contractObject = JSON.parse(contract); + const graphConfigPath = `${graphDir}/networks.json`; + let graphConfig = "{}"; + try { + if (fs.existsSync(graphConfigPath)) { + graphConfig = fs.readFileSync(graphConfigPath).toString(); + } + } catch (e) { + console.log(e); + } + + let graphConfigObject = JSON.parse(graphConfig); + if (!(networkName in graphConfigObject)) { + graphConfigObject[networkName] = {}; + } + if (!(contractName in graphConfigObject[networkName])) { + graphConfigObject[networkName][contractName] = {}; + } + graphConfigObject[networkName][contractName].address = + contractObject.address; + + fs.writeFileSync( + graphConfigPath, + JSON.stringify(graphConfigObject, null, 2) + ); + if (!fs.existsSync(`${graphDir}/abis`)) fs.mkdirSync(`${graphDir}/abis`); + fs.writeFileSync( + `${graphDir}/abis/${networkName}_${contractName}.json`, + JSON.stringify(contractObject.abi, null, 2) + ); + + return true; + } catch (e) { + console.log( + "Failed to publish " + chalk.red(contractName) + " to the subgraph." + ); + console.log(e); + return false; + } +} + +async function main() { + const directories = fs.readdirSync(deploymentsDir); + directories.forEach(function(directory) { + const files = fs.readdirSync(`${deploymentsDir}/${directory}`); + files.forEach(function(file) { + if (file.indexOf(".json") >= 0) { + const contractName = file.replace(".json", ""); + publishContract(contractName, directory); + } + }); + }); + console.log("✅ Published contracts to the subgraph package."); +} +main() + .then(() => process.exit(0)) + .catch(error => { + console.error(error); + process.exit(1); + }); diff --git a/packages/subgraph/src/mapping.ts b/packages/subgraph/src/mapping.ts new file mode 100644 index 0000000..794f8a1 --- /dev/null +++ b/packages/subgraph/src/mapping.ts @@ -0,0 +1,59 @@ +import { BigInt } from "@graphprotocol/graph-ts"; +import { + Think2Earn, + BountyCreated, + EEGDataSubmitted, + BountyCompleted, + EtherDeposited, + PaymentMade +} from "../generated/Think2Earn/Think2Earn"; +import { Bounty, Submission, Deposit, Payment, User } from "../generated/schema"; + +export function handleBountyCreated(event: BountyCreated): void { + let bounty = new Bounty(event.params.bountyId.toString()); + bounty.name = event.params.name; + bounty.description = event.params.description; + bounty.mediaURIHash = event.params.mediaURIHash; + bounty.reward = event.params.reward; + bounty.duration = event.params.duration; + bounty.judgeTime = event.params.judgeTime; + bounty.maxProgress = event.params.maxProgress; + bounty.creator = event.params.creator; + bounty.creationBlock = event.block.number; + bounty.isActive = true; + + bounty.save(); +} + +export function handleEEGDataSubmitted(event: EEGDataSubmitted): void { + let submissionId = event.params.bountyId.toString() + "-" + event.params.submissionId.toString(); + let submission = new Submission(submissionId); + submission.bounty = event.params.bountyId.toString(); + submission.submitter = event.params.submitter; + submission.eegDataHash = event.params.eegDataHash; + submission.save(); +} + +export function handleBountyCompleted(event: BountyCompleted): void { + let bounty = Bounty.load(event.params.bountyId.toString()); + if (bounty !== null) { + bounty.isActive = false; + bounty.save(); + } +} + +export function handleEtherDeposited(event: EtherDeposited): void { + let deposit = new Deposit(event.transaction.hash.toHex() + "-" + event.logIndex.toString()); + deposit.sender = event.params.sender; + deposit.amount = event.params.amount; + deposit.save(); +} + +export function handlePaymentMade(event: PaymentMade): void { + let paymentId = event.params.bountyId.toString() + "-" + event.params.submissionId.toString(); + let payment = new Payment(paymentId); + payment.bounty = event.params.bountyId.toString(); + payment.submission = event.params.submissionId.toString(); + payment.amount = event.params.amount; + payment.save(); +} diff --git a/packages/subgraph/src/schema.graphql b/packages/subgraph/src/schema.graphql new file mode 100644 index 0000000..39c14a8 --- /dev/null +++ b/packages/subgraph/src/schema.graphql @@ -0,0 +1,44 @@ +type Bounty @entity { + id: ID! + name: String! + description: String! + mediaURIHash: String! + reward: BigInt! + duration: BigInt! + judgeTime: BigInt! + maxProgress: BigInt! + creationBlock: BigInt! + creator: Bytes! + isActive: Boolean! + submissions: [Submission!] @derivedFrom(field: "bounty") + completed: Boolean! + numAcceptedSubmissions: BigInt! +} + +type Submission @entity { + id: ID! + bounty: Bounty! + submitter: Bytes! + eegDataHash: Bytes! +} + +type Deposit @entity { + id: ID! + sender: Bytes! + amount: BigInt! +} + +type Payment @entity { + id: ID! + bounty: Bounty! + submission: Submission! + amount: BigInt! +} + +type User @entity { + id: ID! + address: Bytes! + submissionCount: BigInt! + depositCount: BigInt! + paymentReceivedCount: BigInt! +} diff --git a/packages/subgraph/subgraph.yaml b/packages/subgraph/subgraph.yaml new file mode 100644 index 0000000..ac728bd --- /dev/null +++ b/packages/subgraph/subgraph.yaml @@ -0,0 +1,26 @@ +specVersion: 0.0.4 +description: Greetings +repository: https://github.com/scaffold-eth/se-2/packages/subgraph/ +schema: + file: ./src/schema.graphql +dataSources: + - kind: ethereum/contract + name: YourContract + network: localhost + source: + abi: YourContract + address: "0x5FbDB2315678afecb367f032d93F642f64180aa3" + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - Greeting + - Sender + abis: + - name: YourContract + file: ./abis/localhost_YourContract.json + eventHandlers: + - event: GreetingChange(indexed address,string,bool,uint256) + handler: handleGreetingChange + file: ./src/mapping.ts diff --git a/packages/subgraph/tests/asserts.test.ts b/packages/subgraph/tests/asserts.test.ts new file mode 100644 index 0000000..95091de --- /dev/null +++ b/packages/subgraph/tests/asserts.test.ts @@ -0,0 +1,69 @@ +import { describe, test, assert, beforeAll } from "matchstick-as"; +import { BigInt, Bytes } from "@graphprotocol/graph-ts"; +import { Greeting, Sender } from "../generated/schema"; + +describe("Asserts", () => { + beforeAll(() => { + // Mocking the Sender + let sender = new Sender("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"); + sender.address = Bytes.fromHexString( + "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" + ); + sender.createdAt = BigInt.fromI32(1709849870); + sender.greetingCount = BigInt.fromI32(1); + sender.save(); + + // Mocking Greeting + let greeting = new Greeting( + "0x1909fcb0b41989e28308afcb0cf55adb6faba28e14fcbf66c489c69b8fe95dd6" + ); + greeting.sender = sender.id; // Linking Greeting to Sender by ID + greeting.createdAt = BigInt.fromI32(1709849870); + greeting.greeting = "Building COOL Apps!"; + greeting.premium = false; + greeting.transactionHash = + "0x1909fcb0b41989e28308afcb0cf55adb6faba28e14fcbf66c489c69b8fe95dd6"; + greeting.value = BigInt.fromI32(0); + greeting.save(); + }); + + test("Greeting and Sender entities", () => { + // Testing proper entity creation and field assertion + let id = + "0x1909fcb0b41989e28308afcb0cf55adb6faba28e14fcbf66c489c69b8fe95dd7"; + let senderAddress = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96046"; + let greetingText = "Building AWESOME Apps!"; + + // Creating a new Sender entity for the Greeting + let newSender = new Sender(senderAddress); + newSender.address = Bytes.fromHexString(senderAddress); + newSender.createdAt = BigInt.fromI32(1709859870); // A different timestamp + newSender.greetingCount = BigInt.fromI32(2); // Updated greeting count + newSender.save(); + + // Creating a new Greeting entity + let entity = new Greeting(id); + entity.sender = newSender.id; // Linking to new Sender + entity.greeting = greetingText; + entity.createdAt = BigInt.fromI32(1709859870); // Make sure to have the correct time + entity.premium = true; // Assuming a different scenario + entity.transactionHash = id; // Mock transaction hash as the ID + entity.value = BigInt.fromI32(100); // Some value + entity.save(); + + // Loading the Greeting entity and asserting its fields + let loadedEntity = Greeting.load(id); + assert.assertNotNull( + loadedEntity, + "Loaded Greeting entity should not be null" + ); + assert.fieldEquals("Greeting", id, "sender", newSender.id); + assert.fieldEquals("Greeting", id, "greeting", greetingText); + assert.fieldEquals("Greeting", id, "premium", "true"); // Assuming premium is a boolean + assert.fieldEquals("Greeting", id, "value", "100"); // Assuming value is stored as a BigInt + + // Corrected entity and field names according to the mock + assert.entityCount("Sender", 2); // Assuming there are 2 Sender entities now + assert.entityCount("Greeting", 2); // Assuming there are 2 Greeting entities now + }); +}); diff --git a/packages/subgraph/tsconfig.json b/packages/subgraph/tsconfig.json new file mode 100644 index 0000000..8db86be --- /dev/null +++ b/packages/subgraph/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + /* Base Options: */ + "esModuleInterop": true, + "skipLibCheck": true, + "target": "es2022", + "allowJs": true, + "resolveJsonModule": true, + "moduleDetection": "force", + "isolatedModules": true, + "strict": true, + "noUncheckedIndexedAccess": true, + "sourceMap": true, + "declaration": true, + "moduleResolution": "Bundler", + "module": "ESNext", + "noEmit": true, + "lib": ["es2022"] + } +}