From df35629e0d2ad4448c8b63040640ae1c15fb32f6 Mon Sep 17 00:00:00 2001 From: Paul2 Date: Tue, 21 Nov 2023 10:59:19 -0600 Subject: [PATCH] Initial commit --- .eslintrc.js | 10 + .github/workflows/ci.yaml | 52 + .gitignore | 47 + .gitmodules | 3 + .npmrc | 1 + .vscode/settings.json | 9 + README.md | 81 + apps/docs/.eslintrc.js | 4 + apps/docs/README.md | 30 + apps/docs/next-env.d.ts | 5 + apps/docs/next.config.js | 6 + apps/docs/package.json | 27 + apps/docs/pages/index.tsx | 10 + apps/docs/tsconfig.json | 5 + apps/web/.eslintrc.js | 4 + apps/web/README.md | 30 + apps/web/components/Account.tsx | 13 + apps/web/components/index.ts | 1 + apps/web/next-env.d.ts | 5 + apps/web/next.config.js | 6 + apps/web/package.json | 30 + apps/web/pages/_app.tsx | 26 + apps/web/pages/index.tsx | 18 + apps/web/tsconfig.json | 11 + apps/web/wagmi.ts | 23 + foundry.toml | 7 + lib/forge-std | 1 + package.json | 26 + pkg/contracts/package.json | 12 + pkg/contracts/script/Counter.s.sol | 18 + pkg/contracts/src/Counter.sol | 17 + pkg/contracts/test/Counter.t.sol | 24 + pkg/eslint-config-custom/index.js | 7 + pkg/eslint-config-custom/package.json | 19 + pkg/services/graph-node/README.md | 79 + pkg/services/graph-node/docker-compose.yml | 40 + pkg/services/package.json | 10 + pkg/subgraph/package.json | 18 + pkg/subgraph/src/mapping.ts | 30 + pkg/subgraph/src/schema.graphql | 15 + pkg/subgraph/subgraph.yaml | 25 + pkg/tsconfig/README.md | 3 + pkg/tsconfig/base.json | 20 + pkg/tsconfig/nextjs.json | 22 + pkg/tsconfig/package.json | 10 + pkg/tsconfig/react-library.json | 11 + pkg/ui/Button.tsx | 3 + pkg/ui/__tests__/Button.test.tsx | 8 + pkg/ui/index.tsx | 1 + pkg/ui/package.json | 29 + pkg/ui/setupTests.ts | 1 + pkg/ui/tsconfig.json | 5 + pkg/ui/vite.config.ts | 15 + pnpm-lock.yaml | 8909 ++++++++++++++++++++ pnpm-workspace.yaml | 3 + remappings.txt | 2 + turbo.json | 33 + 57 files changed, 9880 insertions(+) create mode 100644 .eslintrc.js create mode 100644 .github/workflows/ci.yaml create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 .npmrc create mode 100644 .vscode/settings.json create mode 100644 README.md create mode 100644 apps/docs/.eslintrc.js create mode 100644 apps/docs/README.md create mode 100644 apps/docs/next-env.d.ts create mode 100644 apps/docs/next.config.js create mode 100644 apps/docs/package.json create mode 100644 apps/docs/pages/index.tsx create mode 100644 apps/docs/tsconfig.json create mode 100644 apps/web/.eslintrc.js create mode 100644 apps/web/README.md create mode 100644 apps/web/components/Account.tsx create mode 100644 apps/web/components/index.ts create mode 100644 apps/web/next-env.d.ts create mode 100644 apps/web/next.config.js create mode 100644 apps/web/package.json create mode 100644 apps/web/pages/_app.tsx create mode 100644 apps/web/pages/index.tsx create mode 100644 apps/web/tsconfig.json create mode 100644 apps/web/wagmi.ts create mode 100644 foundry.toml create mode 160000 lib/forge-std create mode 100644 package.json create mode 100644 pkg/contracts/package.json create mode 100644 pkg/contracts/script/Counter.s.sol create mode 100644 pkg/contracts/src/Counter.sol create mode 100644 pkg/contracts/test/Counter.t.sol create mode 100644 pkg/eslint-config-custom/index.js create mode 100644 pkg/eslint-config-custom/package.json create mode 100644 pkg/services/graph-node/README.md create mode 100644 pkg/services/graph-node/docker-compose.yml create mode 100644 pkg/services/package.json create mode 100644 pkg/subgraph/package.json create mode 100644 pkg/subgraph/src/mapping.ts create mode 100644 pkg/subgraph/src/schema.graphql create mode 100644 pkg/subgraph/subgraph.yaml create mode 100644 pkg/tsconfig/README.md create mode 100644 pkg/tsconfig/base.json create mode 100644 pkg/tsconfig/nextjs.json create mode 100644 pkg/tsconfig/package.json create mode 100644 pkg/tsconfig/react-library.json create mode 100644 pkg/ui/Button.tsx create mode 100644 pkg/ui/__tests__/Button.test.tsx create mode 100644 pkg/ui/index.tsx create mode 100644 pkg/ui/package.json create mode 100644 pkg/ui/setupTests.ts create mode 100644 pkg/ui/tsconfig.json create mode 100644 pkg/ui/vite.config.ts create mode 100644 pnpm-lock.yaml create mode 100644 pnpm-workspace.yaml create mode 100644 remappings.txt create mode 100644 turbo.json diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 000000000..5b999efa4 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,10 @@ +module.exports = { + root: true, + // This tells ESLint to load the config from the package `eslint-config-custom` + extends: ["custom"], + settings: { + next: { + rootDir: ["apps/*/"], + }, + }, +}; diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 000000000..3e04b76a4 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,52 @@ +name: CI + +on: + push: + branches: ["master"] + pull_request: + types: [opened, synchronize] + +jobs: + build: + name: Build, Lint and Test + timeout-minutes: 15 + runs-on: ubuntu-latest + env: + TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} + TURBO_TEAM: ${{ secrets.TURBO_TEAM }} + + steps: + - name: Check out code + uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + + - uses: pnpm/action-setup@v2.0.1 + with: + version: 6.32.2 + + - name: Setup Node.js environment + uses: actions/setup-node@v3 + with: + node-version: 16 + cache: "pnpm" + + - name: Cache pnpm modules + uses: actions/cache@v2 + with: + path: "**/node_modules" + key: pnpm-${{ hashFiles('pnpm-lock.yaml') }} + + - name: Install dependencies + run: pnpm install + + - name: Lint + run: pnpm lint + + - name: Build and Test + run: pnpm test diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..df7bb3184 --- /dev/null +++ b/.gitignore @@ -0,0 +1,47 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +node_modules +.pnp +.pnp.js + +# testing +coverage + +# next.js +.next/ +out/ +build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# local env files +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# turbo +.turbo + + +# foundry +cache/ + +# Ignores development broadcast logs +!pkg/contracts/broadcast +pkg/contracts/broadcast/*/31337/ +pkg/contracts/broadcast/**/dry-run/ + +# subgraph +pkg/subgraph/generated +pkg/**/data diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..888d42dcd --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib/forge-std"] + path = lib/forge-std + url = https://github.com/foundry-rs/forge-std diff --git a/.npmrc b/.npmrc new file mode 100644 index 000000000..fa4e09523 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +strict-peer-dependencies=false \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..5a23bb8e6 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "solidity.packageDefaultDependenciesContractsDirectory": "pkg/contracts/src", + "solidity.packageDefaultDependenciesDirectory": "lib", + "[solidity]": { + "editor.defaultFormatter": "JuanBlanco.solidity", + "editor.formatOnSave": true + }, + "solidity.formatter": "forge", +} diff --git a/README.md b/README.md new file mode 100644 index 000000000..6f8f440ae --- /dev/null +++ b/README.md @@ -0,0 +1,81 @@ +# Turborepo starter for web3 projects + +This is a starter turborepo for web3 development. + +## What's inside? + +This turborepo uses [pnpm](https://pnpm.io) as a package manager. It includes the following packages/apps: + +### Apps and Packages + +- `docs`: a [Next.js](https://nextjs.org/) app +- `web`: another [Next.js](https://nextjs.org/) app +- `ui`: a stub React component library shared by both `web` and `docs` applications +- `eslint-config-custom`: `eslint` configurations (includes `eslint-config-next` and `eslint-config-prettier`) +- `tsconfig`: `tsconfig.json`s used throughout the monorepo +- `contracts`: a `Ethereum` smart contract development using Foundry +- `subgraph`: a subgraph development environment using `The Graph` +- `services`: a preconfigured Docker image for running a `Graph Node` + +Each package/app is 100% [TypeScript](https://www.typescriptlang.org/). + +If this is your first time with Foundry, check out the [installation](https://github.com/foundry-rs/foundry#installation) instructions. + +### Utilities + +This turborepo has some additional tools already setup for you: + +- [TypeScript](https://www.typescriptlang.org/) for static type checking +- [ESLint](https://eslint.org/) for code linting +- [Prettier](https://prettier.io) for code formatting + +### Build + +To build all apps and packages, run the following command: + +``` +cd my-turborepo +pnpm run build +``` + +### Develop + +To develop all apps and packages, run the following command: + +``` +cd my-turborepo +pnpm run dev +``` + +### Remote Caching + +Turborepo can use a technique known as [Remote Caching](https://turbo.build/repo/docs/core-concepts/remote-caching) to share cache artifacts across machines, enabling you to share build caches with your team and CI/CD pipelines. + +By default, Turborepo will cache locally. To enable Remote Caching you will need an account with Vercel. If you don't have an account you can [create one](https://vercel.com/signup), then enter the following commands: + +``` +cd my-turborepo +pnpm dlx turbo login +``` + +This will authenticate the Turborepo CLI with your [Vercel account](https://vercel.com/docs/concepts/personal-accounts/overview). + +Next, you can link your Turborepo to your Remote Cache by running the following command from the root of your turborepo: + +``` +pnpm dlx turbo link +``` + +## Useful Links + +Learn more about the power of Turborepo: + +- [Pipelines](https://turbo.build/repo/docs/core-concepts/monorepos/running-tasks) +- [Caching](https://turbo.build/repo/docs/core-concepts/caching) +- [Remote Caching](https://turbo.build/repo/docs/core-concepts/remote-caching) +- [Filtering](https://turbo.build/repo/docs/core-concepts/monorepos/filtering) +- [Configuration Options](https://turbo.build/repo/docs/reference/configuration) +- [CLI Usage](https://turbo.build/repo/docs/reference/command-line-reference) +- [Foundry book](https://book.getfoundry.sh) +- [Creating a Subgraph](https://thegraph.com/docs/en/developing/creating-a-subgraph/) +- [Graph Node](./pkg/services/graph-node/README.md) diff --git a/apps/docs/.eslintrc.js b/apps/docs/.eslintrc.js new file mode 100644 index 000000000..c8df60750 --- /dev/null +++ b/apps/docs/.eslintrc.js @@ -0,0 +1,4 @@ +module.exports = { + root: true, + extends: ["custom"], +}; diff --git a/apps/docs/README.md b/apps/docs/README.md new file mode 100644 index 000000000..4fae62aff --- /dev/null +++ b/apps/docs/README.md @@ -0,0 +1,30 @@ +## Getting Started + +First, run the development server: + +```bash +yarn dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. + +[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`. + +The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn/foundations/about-nextjs) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_source=github.com&utm_medium=referral&utm_campaign=turborepo-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/apps/docs/next-env.d.ts b/apps/docs/next-env.d.ts new file mode 100644 index 000000000..4f11a03dc --- /dev/null +++ b/apps/docs/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/apps/docs/next.config.js b/apps/docs/next.config.js new file mode 100644 index 000000000..5e578c4f4 --- /dev/null +++ b/apps/docs/next.config.js @@ -0,0 +1,6 @@ +module.exports = { + reactStrictMode: true, + experimental: { + transpilePackages: ["ui"], + }, +}; diff --git a/apps/docs/package.json b/apps/docs/package.json new file mode 100644 index 000000000..3442289d7 --- /dev/null +++ b/apps/docs/package.json @@ -0,0 +1,27 @@ +{ + "name": "docs", + "version": "0.0.0", + "private": true, + "scripts": { + "dev": "next dev --port 3001", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "next": "13.0.0", + "react": "18.2.0", + "react-dom": "18.2.0", + "ui": "workspace:*" + }, + "devDependencies": { + "@babel/core": "^7.0.0", + "eslint-config-custom": "workspace:*", + "eslint": "7.32.0", + "tsconfig": "workspace:*", + "@types/node": "^17.0.12", + "@types/react": "^18.0.22", + "@types/react-dom": "^18.0.7", + "typescript": "^4.5.3" + } +} diff --git a/apps/docs/pages/index.tsx b/apps/docs/pages/index.tsx new file mode 100644 index 000000000..0d1dabc12 --- /dev/null +++ b/apps/docs/pages/index.tsx @@ -0,0 +1,10 @@ +import { Button } from "ui"; + +export default function Docs() { + return ( +
+

Docs

+
+ ); +} diff --git a/apps/docs/tsconfig.json b/apps/docs/tsconfig.json new file mode 100644 index 000000000..a355365ba --- /dev/null +++ b/apps/docs/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "tsconfig/nextjs.json", + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], + "exclude": ["node_modules"] +} diff --git a/apps/web/.eslintrc.js b/apps/web/.eslintrc.js new file mode 100644 index 000000000..c8df60750 --- /dev/null +++ b/apps/web/.eslintrc.js @@ -0,0 +1,4 @@ +module.exports = { + root: true, + extends: ["custom"], +}; diff --git a/apps/web/README.md b/apps/web/README.md new file mode 100644 index 000000000..4fae62aff --- /dev/null +++ b/apps/web/README.md @@ -0,0 +1,30 @@ +## Getting Started + +First, run the development server: + +```bash +yarn dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. + +[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`. + +The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn/foundations/about-nextjs) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_source=github.com&utm_medium=referral&utm_campaign=turborepo-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/apps/web/components/Account.tsx b/apps/web/components/Account.tsx new file mode 100644 index 000000000..545db9352 --- /dev/null +++ b/apps/web/components/Account.tsx @@ -0,0 +1,13 @@ +import { useAccount, useEnsName } from 'wagmi' + +export function Account() { + const { address } = useAccount() + const { data: ensName } = useEnsName({ address }) + + return ( +

+ {ensName ?? address} + {ensName ? ` (${address})` : null} +

+ ) +} diff --git a/apps/web/components/index.ts b/apps/web/components/index.ts new file mode 100644 index 000000000..1f9f004ba --- /dev/null +++ b/apps/web/components/index.ts @@ -0,0 +1 @@ +export { Account } from './Account' diff --git a/apps/web/next-env.d.ts b/apps/web/next-env.d.ts new file mode 100644 index 000000000..4f11a03dc --- /dev/null +++ b/apps/web/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/apps/web/next.config.js b/apps/web/next.config.js new file mode 100644 index 000000000..5e578c4f4 --- /dev/null +++ b/apps/web/next.config.js @@ -0,0 +1,6 @@ +module.exports = { + reactStrictMode: true, + experimental: { + transpilePackages: ["ui"], + }, +}; diff --git a/apps/web/package.json b/apps/web/package.json new file mode 100644 index 000000000..9a96b6c1f --- /dev/null +++ b/apps/web/package.json @@ -0,0 +1,30 @@ +{ + "name": "web", + "version": "0.0.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "@rainbow-me/rainbowkit": "^0.8.1", + "ethers": "^5.7.2", + "next": "13.0.0", + "react": "18.2.0", + "react-dom": "18.2.0", + "ui": "workspace:*", + "wagmi": "^0.10.10" + }, + "devDependencies": { + "@babel/core": "^7.0.0", + "@types/node": "^17.0.12", + "@types/react": "^18.0.22", + "@types/react-dom": "^18.0.7", + "eslint": "7.32.0", + "eslint-config-custom": "workspace:*", + "tsconfig": "workspace:*", + "typescript": "^4.5.3" + } +} diff --git a/apps/web/pages/_app.tsx b/apps/web/pages/_app.tsx new file mode 100644 index 000000000..3407fa3b8 --- /dev/null +++ b/apps/web/pages/_app.tsx @@ -0,0 +1,26 @@ +import "@rainbow-me/rainbowkit/styles.css"; +import { RainbowKitProvider } from "@rainbow-me/rainbowkit"; +import type { AppProps } from "next/app"; +import NextHead from "next/head"; +import * as React from "react"; +import { WagmiConfig } from "wagmi"; + +import { chains, client } from "../wagmi"; + +function App({ Component, pageProps }: AppProps) { + const [mounted, setMounted] = React.useState(false); + React.useEffect(() => setMounted(true), []); + return ( + + + + My App + + + {mounted && } + + + ); +} + +export default App; diff --git a/apps/web/pages/index.tsx b/apps/web/pages/index.tsx new file mode 100644 index 000000000..eb9295189 --- /dev/null +++ b/apps/web/pages/index.tsx @@ -0,0 +1,18 @@ +import { ConnectButton } from "@rainbow-me/rainbowkit"; +import { useAccount } from "wagmi"; + +import { Account } from "~/components"; + +function Page() { + const { isConnected } = useAccount(); + return ( + <> +

wagmi + RainbowKit + Next.js

+ + + {isConnected && } + + ); +} + +export default Page; diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json new file mode 100644 index 000000000..1badd610b --- /dev/null +++ b/apps/web/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "tsconfig/nextjs.json", + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], + "compilerOptions": { + "preserveSymlinks": true, + "paths": { + "~/*": ["./*"] + } + }, + "exclude": ["node_modules"] +} diff --git a/apps/web/wagmi.ts b/apps/web/wagmi.ts new file mode 100644 index 000000000..4ec8b9c85 --- /dev/null +++ b/apps/web/wagmi.ts @@ -0,0 +1,23 @@ +import { getDefaultWallets } from "@rainbow-me/rainbowkit"; +import { configureChains, createClient } from "wagmi"; +import { goerli, mainnet } from "wagmi/chains"; +import { publicProvider } from "wagmi/providers/public"; + +const { chains, provider, webSocketProvider } = configureChains( + [mainnet, ...(process.env.NODE_ENV === "development" ? [goerli] : [])], + [publicProvider()] +); + +const { connectors } = getDefaultWallets({ + appName: "My wagmi + RainbowKit App", + chains, +}); + +export const client = createClient({ + autoConnect: true, + connectors, + provider, + webSocketProvider, +}); + +export { chains }; diff --git a/foundry.toml b/foundry.toml new file mode 100644 index 000000000..cd753cfe6 --- /dev/null +++ b/foundry.toml @@ -0,0 +1,7 @@ +[profile.default] +src = 'pkg/contracts/src' +test = 'pkg/contracts/test' +out = 'pkg/contracts/out' +libs = ['lib'] + +# See more config options https://github.com/foundry-rs/foundry/tree/master/config \ No newline at end of file diff --git a/lib/forge-std b/lib/forge-std new file mode 160000 index 000000000..066ff16c5 --- /dev/null +++ b/lib/forge-std @@ -0,0 +1 @@ +Subproject commit 066ff16c5c03e6f931cd041fd366bc4be1fae82a diff --git a/package.json b/package.json new file mode 100644 index 000000000..63dbd637c --- /dev/null +++ b/package.json @@ -0,0 +1,26 @@ +{ + "name": "osmotic", + "version": "0.0.0", + "private": true, + "workspaces": [ + "apps/*", + "pkg/*" + ], + "scripts": { + "build": "turbo run build", + "dev": "turbo run dev --parallel", + "lint": "turbo run lint", + "test": "turbo run test", + "format": "prettier --write \"**/*.{ts,tsx,md}\"" + }, + "devDependencies": { + "eslint-config-custom": "workspace:*", + "prettier": "latest", + "turbo": "latest" + }, + "engines": { + "node": ">=14.0.0" + }, + "dependencies": {}, + "packageManager": "pnpm@7.16.1" +} diff --git a/pkg/contracts/package.json b/pkg/contracts/package.json new file mode 100644 index 000000000..6a759f9ab --- /dev/null +++ b/pkg/contracts/package.json @@ -0,0 +1,12 @@ +{ + "name": "foundry", + "version": "0.0.0", + "private": true, + "scripts": { + "dev": "anvil", + "build": "forge build --sizes", + "lint": "forge fmt", + "test": "forge test -vvv", + "deploy": "forge script script/Counter.s.sol --rpc-url http://localhost:8545 --broadcast" + } +} diff --git a/pkg/contracts/script/Counter.s.sol b/pkg/contracts/script/Counter.s.sol new file mode 100644 index 000000000..fccbfd9dd --- /dev/null +++ b/pkg/contracts/script/Counter.s.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "forge-std/Script.sol"; +import "../src/Counter.sol"; + +contract CounterScript is Script { + function setUp() public {} + + function run() public { + uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); + vm.startBroadcast(deployerPrivateKey); + + new Counter(); + + vm.stopBroadcast(); + } +} diff --git a/pkg/contracts/src/Counter.sol b/pkg/contracts/src/Counter.sol new file mode 100644 index 000000000..8ccf33f88 --- /dev/null +++ b/pkg/contracts/src/Counter.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +contract Counter { + uint256 public number; + + event SetNumber(address sender, uint256 newNumber); + + function setNumber(uint256 newNumber) public { + number = newNumber; + emit SetNumber(msg.sender, newNumber); + } + + function increment() public { + number++; + } +} diff --git a/pkg/contracts/test/Counter.t.sol b/pkg/contracts/test/Counter.t.sol new file mode 100644 index 000000000..30235e8a8 --- /dev/null +++ b/pkg/contracts/test/Counter.t.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; +import "../src/Counter.sol"; + +contract CounterTest is Test { + Counter public counter; + + function setUp() public { + counter = new Counter(); + counter.setNumber(0); + } + + function testIncrement() public { + counter.increment(); + assertEq(counter.number(), 1); + } + + function testSetNumber(uint256 x) public { + counter.setNumber(x); + assertEq(counter.number(), x); + } +} diff --git a/pkg/eslint-config-custom/index.js b/pkg/eslint-config-custom/index.js new file mode 100644 index 000000000..2ea526a5e --- /dev/null +++ b/pkg/eslint-config-custom/index.js @@ -0,0 +1,7 @@ +module.exports = { + extends: ["next", "turbo", "prettier"], + rules: { + "@next/next/no-html-link-for-pages": "off", + "react/jsx-key": "off", + }, +}; diff --git a/pkg/eslint-config-custom/package.json b/pkg/eslint-config-custom/package.json new file mode 100644 index 000000000..16fed7a78 --- /dev/null +++ b/pkg/eslint-config-custom/package.json @@ -0,0 +1,19 @@ +{ + "name": "eslint-config-custom", + "version": "0.0.0", + "main": "index.js", + "license": "MIT", + "dependencies": { + "eslint": "^7.23.0", + "eslint-config-next": "13.0.0", + "eslint-config-prettier": "^8.3.0", + "eslint-plugin-react": "7.31.8", + "eslint-config-turbo": "latest" + }, + "devDependencies": { + "typescript": "^4.7.4" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/pkg/services/graph-node/README.md b/pkg/services/graph-node/README.md new file mode 100644 index 000000000..fdfb0a6b4 --- /dev/null +++ b/pkg/services/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/pkg/services/graph-node/docker-compose.yml b/pkg/services/graph-node/docker-compose.yml new file mode 100644 index 000000000..8231ad4e9 --- /dev/null +++ b/pkg/services/graph-node/docker-compose.yml @@ -0,0 +1,40 @@ +version: "3" +services: + graph-node: + image: graphprotocol/graph-node:latest + 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 + volumes: + - ./data/postgres:/var/lib/postgresql/data diff --git a/pkg/services/package.json b/pkg/services/package.json new file mode 100644 index 000000000..e8099c8c6 --- /dev/null +++ b/pkg/services/package.json @@ -0,0 +1,10 @@ +{ + "name": "services", + "version": "0.0.0", + "private": true, + "scripts": { + "run-graph-node": "cd graph-node && docker-compose up", + "remove-graph-node": "cd graph-node && docker-compose down", + "clean-graph-node": "rm -rf graph-node/data/" + } +} diff --git a/pkg/subgraph/package.json b/pkg/subgraph/package.json new file mode 100644 index 000000000..c3e05d81b --- /dev/null +++ b/pkg/subgraph/package.json @@ -0,0 +1,18 @@ +{ + "name": "subgraph", + "version": "0.0.0", + "private": true, + "scripts": { + "build": "graph codegen && graph build", + "create-local": "graph create --node http://localhost:8020/ blossom/counter", + "remove-local": "graph remove --node http://localhost:8020/ blossom/counter", + "deploy-local": "graph deploy --node http://localhost:8020/ --ipfs http://localhost:5001 blossom/counter" + }, + "dependencies": { + "@graphprotocol/graph-cli": "^0.37.2", + "@graphprotocol/graph-ts": "^0.29.1" + }, + "devDependencies": { + "mustache": "^4.2.0" + } +} diff --git a/pkg/subgraph/src/mapping.ts b/pkg/subgraph/src/mapping.ts new file mode 100644 index 000000000..84756da3a --- /dev/null +++ b/pkg/subgraph/src/mapping.ts @@ -0,0 +1,30 @@ +import { BigInt } from "@graphprotocol/graph-ts"; +import { SetNumber } from "../generated/Counter/Counter"; +import { Number, Sender } from "../generated/schema"; + +export function handleSetNumber(event: SetNumber): void { + let senderString = event.params.sender.toHexString(); + + let sender = Sender.load(senderString); + + if (sender === null) { + sender = new Sender(senderString); + sender.address = event.params.sender; + sender.createdAt = event.block.timestamp; + sender.numberCount = BigInt.fromI32(1); + } else { + sender.numberCount = sender.numberCount.plus(BigInt.fromI32(1)); + } + + let number = new Number( + event.transaction.hash.toHex() + "-" + event.logIndex.toString() + ); + + number.number = event.params.newNumber; + number.sender = senderString; + number.createdAt = event.block.timestamp; + number.transactionHash = event.transaction.hash.toHex(); + + number.save(); + sender.save(); +} diff --git a/pkg/subgraph/src/schema.graphql b/pkg/subgraph/src/schema.graphql new file mode 100644 index 000000000..29a75832f --- /dev/null +++ b/pkg/subgraph/src/schema.graphql @@ -0,0 +1,15 @@ +type Number @entity { + id: ID! + sender: Sender! + number: BigInt! + createdAt: BigInt! + transactionHash: String! +} + +type Sender @entity { + id: ID! + address: Bytes! + numbers: [Number!] @derivedFrom(field: "sender") + createdAt: BigInt! + numberCount: BigInt! +} diff --git a/pkg/subgraph/subgraph.yaml b/pkg/subgraph/subgraph.yaml new file mode 100644 index 000000000..19736ca3a --- /dev/null +++ b/pkg/subgraph/subgraph.yaml @@ -0,0 +1,25 @@ +specVersion: 0.0.4 +schema: + file: ./src/schema.graphql +dataSources: + - kind: ethereum/contract + name: Counter + network: localhost + source: + address: "0x5fbdb2315678afecb367f032d93f642f64180aa3" + abi: Counter + startBlock: 1 + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - Number + - Sender + abis: + - name: Counter + file: ../contracts/out/Counter.sol/Counter.json + eventHandlers: + - event: SetNumber(address,uint256) + handler: handleSetNumber + file: ./src/mapping.ts diff --git a/pkg/tsconfig/README.md b/pkg/tsconfig/README.md new file mode 100644 index 000000000..0da79cf23 --- /dev/null +++ b/pkg/tsconfig/README.md @@ -0,0 +1,3 @@ +# `tsconfig` + +These are base shared `tsconfig.json`s from which all other `tsconfig.json`'s inherit from. diff --git a/pkg/tsconfig/base.json b/pkg/tsconfig/base.json new file mode 100644 index 000000000..d72a9f3a2 --- /dev/null +++ b/pkg/tsconfig/base.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "display": "Default", + "compilerOptions": { + "composite": false, + "declaration": true, + "declarationMap": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "inlineSources": false, + "isolatedModules": true, + "moduleResolution": "node", + "noUnusedLocals": false, + "noUnusedParameters": false, + "preserveWatchOutput": true, + "skipLibCheck": true, + "strict": true + }, + "exclude": ["node_modules"] +} diff --git a/pkg/tsconfig/nextjs.json b/pkg/tsconfig/nextjs.json new file mode 100644 index 000000000..3b7dfa900 --- /dev/null +++ b/pkg/tsconfig/nextjs.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "display": "Next.js", + "extends": "./base.json", + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "incremental": true, + "esModuleInterop": true, + "module": "esnext", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve" + }, + "include": ["src", "next-env.d.ts"], + "exclude": ["node_modules"] +} diff --git a/pkg/tsconfig/package.json b/pkg/tsconfig/package.json new file mode 100644 index 000000000..f4810fc3f --- /dev/null +++ b/pkg/tsconfig/package.json @@ -0,0 +1,10 @@ +{ + "name": "tsconfig", + "version": "0.0.0", + "private": true, + "files": [ + "base.json", + "nextjs.json", + "react-library.json" + ] +} diff --git a/pkg/tsconfig/react-library.json b/pkg/tsconfig/react-library.json new file mode 100644 index 000000000..af8711c52 --- /dev/null +++ b/pkg/tsconfig/react-library.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "display": "React Library", + "extends": "./base.json", + "compilerOptions": { + "jsx": "react-jsx", + "lib": ["ES2015"], + "module": "ESNext", + "target": "es6" + } +} diff --git a/pkg/ui/Button.tsx b/pkg/ui/Button.tsx new file mode 100644 index 000000000..893a1c1da --- /dev/null +++ b/pkg/ui/Button.tsx @@ -0,0 +1,3 @@ +export default function Button() { + return ; +} diff --git a/pkg/ui/__tests__/Button.test.tsx b/pkg/ui/__tests__/Button.test.tsx new file mode 100644 index 000000000..98951d7ca --- /dev/null +++ b/pkg/ui/__tests__/Button.test.tsx @@ -0,0 +1,8 @@ +import { expect, test } from "vitest"; +import { render, screen } from "@testing-library/react"; +import Button from "../Button"; + +test("Button", () => { + render(