├── .github └── workflows │ └── push.yml ├── .gitignore ├── .gitmodules ├── .vscode └── settings.json ├── README.md ├── READMEV0.md ├── apps ├── contracts │ ├── .env.example │ ├── .gitignore │ ├── .solhint.json │ ├── README.md │ ├── deployments │ │ ├── 5 │ │ │ └── Counter.json │ │ ├── 1337 │ │ │ └── Counter.json │ │ ├── 80001 │ │ │ └── Counter.json │ │ └── 11155111 │ │ │ └── Counter.json │ ├── docker-compose.yaml │ ├── foundry.toml │ ├── package.json │ ├── remappings.txt │ ├── script │ │ ├── Base.s.sol │ │ └── Counter.s.sol │ ├── src │ │ ├── UUPSProxy.sol │ │ └── UpgradeableCounter.sol │ └── test │ │ └── Counter.t.sol ├── subgraph │ ├── abis │ │ └── Counter.json │ ├── config │ │ ├── 11155111.json │ │ ├── 1337.json │ │ ├── 5.json │ │ └── 80001.json │ ├── copy-artifacts.ts │ ├── generated │ │ ├── Counter │ │ │ └── Counter.ts │ │ └── schema.ts │ ├── networks.json │ ├── package.json │ ├── schema.graphql │ ├── src │ │ └── counter.ts │ ├── template.yaml │ ├── tests │ │ ├── counter-utils.ts │ │ └── counter.test.ts │ └── tsconfig.json └── web │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── README.md │ ├── index.html │ ├── package.json │ ├── postcss.config.js │ ├── public │ └── vite.svg │ ├── src │ ├── App.tsx │ ├── assets │ │ └── react.svg │ ├── components │ │ ├── ConnectButton.tsx │ │ └── Layout.tsx │ ├── hooks │ │ ├── useChain.ts │ │ ├── useIndexed.ts │ │ └── useOwnedNFTs.ts │ ├── index.css │ ├── main.tsx │ ├── services │ │ └── alchemy.ts │ ├── utils.ts │ └── vite-env.d.ts │ ├── tailwind.config.js │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts ├── package.json ├── packages ├── shared-config │ ├── README.md │ ├── assets.ts │ ├── icons │ │ ├── base.svg │ │ ├── ethereum.svg │ │ ├── op.svg │ │ ├── os.svg │ │ └── polygon.svg │ ├── index.t.ts │ ├── index.ts │ ├── nfts.ts │ ├── package.json │ ├── tailwind │ │ ├── postcss.config.js │ │ └── tailwind.config.js │ ├── tsconfig.json │ └── variables.ts ├── tsconfig │ ├── base.json │ └── package.json ├── wagmi-config │ ├── README.md │ ├── generated.ts │ ├── index.ts │ ├── package.json │ ├── tsconfig.json │ ├── wagmi.config.ts │ └── wagmi.ts ├── web-kit │ ├── codegen.ts │ ├── index.ts │ ├── operations.graphql │ ├── ops.cjs │ ├── package.json │ ├── src │ │ ├── generated.ts │ │ ├── store.tsx │ │ └── subgraph.ts │ └── tsconfig.json └── web-ui │ ├── .eslintrc.cjs │ ├── .gitignore │ ├── README.md │ ├── index.html │ ├── lib │ ├── components │ │ ├── Button.tsx │ │ ├── Card.tsx │ │ ├── Table.tsx │ │ ├── WalletButton.tsx │ │ └── Web3Account.tsx │ ├── main.css │ ├── main.ts │ └── vite-env.d.ts │ ├── package.json │ ├── postcss.config.js │ ├── public │ └── vite.svg │ ├── src │ ├── App.tsx │ ├── assets │ │ └── react.svg │ ├── main.tsx │ └── vite-env.d.ts │ ├── tailwind.config.js │ ├── tsconfig.build.json │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts ├── patches └── @graphql-codegen+typescript-rtk-query+3.1.0.patch ├── turbo.json └── yarn.lock /.github/workflows/push.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | - feat/* 6 | 7 | name: test 8 | 9 | jobs: 10 | check: 11 | name: Foundry project 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | with: 16 | submodules: recursive 17 | 18 | - name: Install Foundry 19 | uses: foundry-rs/foundry-toolchain@v1 20 | with: 21 | version: nightly 22 | 23 | - name: Run Forge build 24 | run: | 25 | forge --version 26 | forge build 27 | working-directory: apps/contracts 28 | id: build 29 | 30 | - name: Run Forge tests 31 | run: | 32 | forge test -vvv 33 | working-directory: apps/contracts 34 | id: test 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | **/node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # misc 9 | .DS_Store 10 | *.pem 11 | 12 | # debug 13 | npm-debug.log* 14 | yarn-debug.log* 15 | yarn-error.log* 16 | 17 | # local env files 18 | **/*/.env 19 | **/*/.env.local 20 | **/*/.env.development.local 21 | **/*/.env.test.local 22 | **/*/.env.production.local 23 | 24 | # vercel 25 | .vercel 26 | 27 | node_modules 28 | 29 | apps/subgraph/subgraph.yaml 30 | apps/subgraph/build 31 | apps/contracts/data 32 | apps/contracts/broadcast/** 33 | 34 | packages/kit/operations.graphql 35 | 36 | ## Hardhat files ## 37 | packages/hardhat/*.txt 38 | packages/hardhat/cache 39 | packages/hardhat/artifacts 40 | packages/hardhat/deployments 41 | 42 | ## Frontend Files ## 43 | # testing 44 | packages/frontend/coverage 45 | packages/react-app 46 | 47 | # next.js 48 | packages/frontend/.next/ 49 | packages/frontend/out/ 50 | # production 51 | packages/frontend/build 52 | # contracts 53 | packages/frontend/artifacts/* 54 | !packages/frontend/artifacts/contracts 55 | packages/frontend/artifacts/contracts/**/*.sol 56 | 57 | .turbo 58 | build/** 59 | dist/** 60 | .next/** 61 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "apps/contracts/lib/forge-std"] 2 | path = apps/contracts/lib/forge-std 3 | url = https://github.com/foundry-rs/forge-std 4 | [submodule "apps/contracts/lib/openzeppelin-contracts-upgradeable"] 5 | path = apps/contracts/lib/openzeppelin-contracts-upgradeable 6 | url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable 7 | [submodule "apps/contracts/lib/openzeppelin-contracts"] 8 | path = apps/contracts/lib/openzeppelin-contracts 9 | url = https://github.com/OpenZeppelin/openzeppelin-contracts 10 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.codeActionsOnSave": { 3 | "source.fixAll.eslint": true 4 | }, 5 | "[typescript]": { 6 | "editor.defaultFormatter": "esbenp.prettier-vscode", 7 | "editor.formatOnSave": true 8 | }, 9 | "[typescriptreact]": { 10 | "editor.defaultFormatter": "esbenp.prettier-vscode", 11 | "editor.formatOnSave": true 12 | }, 13 | "[json]": { 14 | "editor.defaultFormatter": "esbenp.prettier-vscode", 15 | "editor.formatOnSave": true 16 | }, 17 | "autoimport.filesToScan": "./{packages, apps}/**/*.{ts,tsx,d.ts}", 18 | "[solidity]": { 19 | "editor.defaultFormatter": "NomicFoundation.hardhat-solidity", 20 | "editor.formatOnSave": true 21 | }, 22 | "solidity.packageDefaultDependenciesContractsDirectory": "apps/contracts/src", 23 | "solidity.packageDefaultDependenciesDirectory": "apps/contracts/lib", 24 | "solidity.formatter": "prettier", 25 | "solidity.compileUsingRemoteVersion": "v0.8.20+commit.a1b79de6" 26 | } 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🔌 ETH Basic Typescript Starter 2 | 3 | ### ✨ New version (V3!) ✨ 4 | 5 | This monorepo should allow you to get started with a simple Counter smart contract on your local anvil instance, and a dapp 6 | 7 | - React / Typescript (Vite ⚡) 8 | - 🏎️ [Turborepo](https://turborepo.org/) 9 | - ⚒️ [Foundry](https://github.com/foundry-rs/foundry), with tests & local anvil instance: 10 | - Multi chain deployments utils 11 | - Upgradeable Counter example 12 | - Subgraph to index your smart contracts 13 | - 🐋 Docker Compose file to run you anvil & graph-node locally quickly 14 | - 🚀 [wagmi](https://wagmi.sh/) & 🌈 [RainbowKit](https://www.rainbowkit.com/) ! 15 | - Generated custom hooks with the wagmi-cli ! 16 | - graphql-codegen generated hooks to query your subgraph from your apps quickly 17 | - [Tailwind CSS](https://tailwindcss.com/) with [DaisyUI](https://daisyui.com/) 🌼 ! 18 | 19 | Recommended: 20 | - [Rivet](https://github.com/paradigmxyz/rivet): Developper wallet & devtool for you local developpement 21 | 22 | ## Get Started 23 | 24 | ### 0. Set environment variables (Optional) 25 | 26 | 27 | 28 | ### 1. Install dependencies 29 | 30 | ``` 31 | yarn 32 | ``` 33 | 34 | ### 2. Start developement process 35 | Will concurrently: 36 | 37 | - launch your anvil instance 38 | - start your react app dev server 39 | 40 | ``` 41 | yarn run dev 42 | ``` 43 | 44 | ### 3. Deploy 45 | 46 | ``` 47 | yarn run deploy:local 48 | ``` 49 | 50 | Will: 51 | - Run your deploy script on your local chain 52 | - Regenerate your custom hooks stright into your wagmi-config 53 | 54 | 55 | ```bash 56 | /apps 57 | # You foundry project 58 | /contracts 59 | # Your dapp 60 | /web 61 | /packages 62 | # Contains wagmi & rainbowkit config and generated code with the wagmi-cli 63 | /wagmi-config 64 | # Hosting app constants 65 | /shared-config 66 | ``` 67 | -------------------------------------------------------------------------------- /READMEV0.md: -------------------------------------------------------------------------------- 1 | # 🔌 ETH Basic Typescript Starter 2 | 3 | DEPRECATED: This README documents the older version of this starter available [HERE](https://github.com/nezz0746/typescript-eth-starter/tree/12d7cd88a70251b134abea7ea7c0666aa2142de0) 4 | 5 | This monorepo should allow you to get started with a simple Greeter smart contract on localhost and Rinkeby and the Polygon POS chain (L2 network), in record time with this basic react stack using 6 | 7 | - Typescript 8 | - NextJS 9 | - 👷 [Hardhat](https://hardhat.org/) 10 | - 🤝 [useDapp](https://usedapp.readthedocs.io/en/latest/) 11 | - Tailwind CSS 12 | - Redux Toolkits 13 | - Yarn Workspace 14 | 15 | Heavily influenced by Scaffold-Eth and the existing [Typescript branch](https://github.com/austintgriffith/scaffold-eth/tree/nextjs-typescript). Go 🏗 [check it out](https://github.com/austintgriffith/scaffold-eth) 🏗 if you haven't ! 16 | 17 | ## Table of Content 18 | 19 | - [🔌 ETH Basic Typescript Starter](#-eth-basic-typescript-starter) 20 | - [Table of Content](#table-of-content) 21 | - [Get Started](#get-started) 22 | - [Examples](#examples) 23 | - [1. Greeter Contract](#1-greeter-contract) 24 | - [2. NFT Sale Dashboard](#2-nft-sale-dashboard) 25 | - [Deploy your Smart Contracts on Rinkeby](#deploy-your-smart-contracts-on-rinkeby) 26 | - [Deploy your Dapp on IPFS](#deploy-your-dapp-on-ipfs) 27 | 28 | ## Get Started 29 | 30 | Clone the project and install dependencies, 31 | 32 | ``` 33 | git clone https://github.com/nezz0746/typescript-eth-starter.git 34 | cd typescript-eth-starter 35 | yarn install 36 | ``` 37 | 38 | _Environement variables setup_ 39 | 40 | First create an Infura projetct and add your project id to the .env variable **INFURA_ID**. 41 | 42 | This starter is setup to be usable/deployable on a local node and on Rinkeby. So before you start make sure you fill the **RINKEBY_PRIVATE_KEY** or **POLYGON_PRIVATE_KEY** variable. (Checkout [this section](#deploy-your-smart-contracts-on-rinkeby) for more info on the private key), or comment out the rinkeby section if you juste want to start working asap on localhost. 43 | 44 | ```js 45 | // packages/hardhat/hardhat.config.ts 46 | ... 47 | networks: { 48 | hardhat: { 49 | chainId: 1337, 50 | }, 51 | rinkeby: { 52 | url: `https://rinkeby.infura.io/v3/${INFURA_ID}`, 53 | accounts: [`0x${RINKEBY_PRIVATE_KEY}`] 54 | }, 55 | matic: { 56 | url: `https://polygon-mainnet.g.alchemy.com/v2/${ALCHEMY_ID}`, 57 | accounts: [`0x${POLYGON_PRIVATE_KEY}`], 58 | }, 59 | } 60 | ... 61 | ``` 62 | 63 | Then open 3 separate terminals and run 64 | 65 | 1. Start your local node 66 | 67 | ``` 68 | yarn chain 69 | ``` 70 | 71 | 2. Deploy your Greeter contract to your local node with 72 | 73 | ``` 74 | yarn deploy 75 | ``` 76 | 77 | 3. Then start your frontend ! 78 | 79 | ``` 80 | yarn dev 81 | ``` 82 | 83 | Finnally, open to see the app. 84 | 85 | You frontend should be live on localhost and ready to submit and new `setGreeting` transaction to the local chain ! 86 | 87 | Here is what it should look like when launched ! 88 | 89 | 90 | 91 | ## Examples 92 | 93 | ### 1. Greeter Contract 94 | 95 | ### 2. NFT Sale Dashboard 96 | 97 | What you will find on this page is an interface which interacts with the ERC721 contract: MyNFT, found in `packages/hardhat/MyNFT.sol`. It contains two tabs which themselves contain actions typically done as 1. an **Owner** of a contract and 2. a **Client** of the contract. 98 | 99 | 100 | 101 | For now two actions are availbale as Owner: The ability to list a new NFT, and pause the sale as the contract is pausable ([see the ERC721Pausable extension on OpenZepplin](https://docs.openzeppelin.com/contracts/2.x/api/token/erc721#ERC721Pausable)). As a client you'll be able to see the mintable NFTs, mint one and see the NFTs you own. 102 | 103 |
104 | 105 |
106 | 107 | To improve the UX and understanding of ownership, Alerts and Warnings have been added. For example you'll be warned on the client side if you are minting as the owner or if the sale is paused. And you'll shown an warning as well if you're trying to trigger owner actions while not being the owner. 108 | 109 | - Metadata 110 | 111 | The crucial thing on the frontend part is to make sure you upload the correct metadata for your NFT. [OpenSea documentation](https://docs.opensea.io/docs/metadata-standards) is a good place to get a first look at standards. Here we upload the most basic metadatas possible with **name**, **description** and **image** (url) 112 | 113 | ```js 114 | // Upload NFT Metadata to IPFS 115 | const added = await client.add( 116 | JSON.stringify({ 117 | name, 118 | description: "MyNFT", 119 | image: `https://ipfs.infura.io/ipfs/${imageIPFS.path}`, 120 | }) 121 | ); 122 | 123 | // Use the resulting URI for our tokenURI 124 | const url = `https://ipfs.infura.io/ipfs/${added.path}`; 125 | ``` 126 | 127 | - Opensea 128 | 129 | Once you listed a couple of NFT, you should find them on [OpenSea's Testnet App](https://testnets.opensea.io/) ! 130 | 131 |
132 | 133 |
134 | 135 | ## Deploy your Smart Contracts on Rinkeby 136 | 137 | To deploy your app on Rinkeby, you'll first need to populate two environment variables in your `.env` file, that are used in `packages/hardhat/hardhat.config.ts`. Checkout [Infura](https://infura.io/) it is a suite of tools that make it easy for developpers to deploy things on Ethereum and IPFS. Create a project there, go to _Settings_ and copy the **Project ID**. 138 | 139 | For your private key make sure you use an account you do not have any real funds on and export the private key. As a good practive, never share any account's private key in your repository. `Metamask > (Select a dev account) > Account details > Export private key` 140 | 141 | ``` 142 | const INFURA_ID = process.env.INFURA_ID; 143 | const RINKEBY_PRIVATE_KEY = process.env.RINKEBY_PRIVATE_KEY 144 | ``` 145 | 146 | Now after you deploy, 147 | 148 | ``` 149 | yarn deploy:rinbeky 150 | ``` 151 | 152 | your smart contracts should be live on Rinkeby ! 153 | 154 | ## Deploy your Dapp on IPFS 155 | 156 | IPFS uses **Content-Addressing** to reference content on the network, instead of location-based addressing as used in traditional web. Whenever you add content to IPFS, a cryptographic hash is produced to identify your content called a **CID**. 157 | 158 | After running, 159 | 160 | ``` 161 | yarn ipfs 162 | ``` 163 | 164 | you will be given a hash which reprensent the root CID of the root directory of your website. Then all subdirectories and consequent files will have their own CID since every file on IPFS no matter it's type (folder, image, text, etc) has it's own CID. Using the [IPFS CLI](https://docs.ipfs.io/install/command-line/) you'll be able to visualize what has been hosted on IPFS with all your content by running 165 | 166 | ``` 167 | > ipfs ls [YOUR_SITE_ROOT_CID] 168 | 169 | > QmTgXrRyvfb8su1YaaafBPYDqdjbSG82qMFyW5XPftdjv6 5884 404.html 170 | QmUJUdDtY1hLt73buoaaaDm2rARRVpkYYDHa8BvnZq3TY3 - _next/ 171 | Qme1DM3r38NsjAXYAoazamgrdpk1CffLiPc14ZHp5Dgya1 15086 favicon.ico 172 | QmNufHi8Rgwx6K4854aaa8mSpHS5pXznHvMXrrU4yvk8HC - images/ 173 | QmeUAy5yNYo67C8id7acWyrrUtm6aks4AQcoDhcveYXrKE 5130 index.html 174 | Qmdd8btEki1ASFBjMeeeaDNyAAzXH1skok8csiPQWWrnaS 1101 vercel.svg 175 | ``` 176 | 177 | You could also use the [IPFS Desktop App](https://github.com/ipfs/ipfs-desktop) to see your content, it has a nice GUI and extra content to better understand how IPFS works. It'll also run a IPFS node for you locally so you can obtain content from other peers. 178 | 179 |
180 | 181 | 182 |
183 | 184 | ([This video](https://www.youtube.com/watch?v=hnigvVuoaIA&t=338s&ab_channel=OurNetworks) provides a great introdction to how IPFS works) 185 | 186 | TODO 187 | 188 | - [ ] Add Mainnet fork as dev chain 189 | - [ ] Add ENS name converter 190 | -------------------------------------------------------------------------------- /apps/contracts/.env.example: -------------------------------------------------------------------------------- 1 | 2 | # ALCHEMY 3 | ALCHEMY_GOERLI_API_KEY= 4 | 5 | # ETHERSCAN 6 | API_KEY_ETHERSCAN= 7 | -------------------------------------------------------------------------------- /apps/contracts/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiler files 2 | cache/ 3 | out/ 4 | 5 | # Ignores development broadcast logs 6 | !/broadcast 7 | /broadcast/*/1337/ 8 | /broadcast/*/5/ 9 | /broadcast/**/dry-run/ 10 | 11 | # Docs 12 | docs/ 13 | 14 | # Dotenv file 15 | .env 16 | -------------------------------------------------------------------------------- /apps/contracts/.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:default", 3 | "rules": { 4 | "no-console": "off", 5 | "private-vars-leading-underscore": "off" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /apps/contracts/README.md: -------------------------------------------------------------------------------- 1 | ## Foundry 2 | 3 | **Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** 4 | 5 | Foundry consists of: 6 | 7 | - **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). 8 | - **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. 9 | - **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. 10 | - **Chisel**: Fast, utilitarian, and verbose solidity REPL. 11 | 12 | ## Documentation 13 | 14 | https://book.getfoundry.sh/ 15 | 16 | ## Usage 17 | 18 | ### Build 19 | 20 | ```shell 21 | $ forge build 22 | ``` 23 | 24 | ### Test 25 | 26 | ```shell 27 | $ forge test 28 | ``` 29 | 30 | ### Format 31 | 32 | ```shell 33 | $ forge fmt 34 | ``` 35 | 36 | ### Gas Snapshots 37 | 38 | ```shell 39 | $ forge snapshot 40 | ``` 41 | 42 | ### Anvil 43 | 44 | ```shell 45 | $ anvil 46 | ``` 47 | 48 | ### Deploy 49 | 50 | ```shell 51 | $ forge script script/Counter.s.sol:CounterScript --rpc-url --private-key 52 | ``` 53 | 54 | ### Cast 55 | 56 | ```shell 57 | $ cast 58 | ``` 59 | 60 | ### Help 61 | 62 | ```shell 63 | $ forge --help 64 | $ anvil --help 65 | $ cast --help 66 | ``` 67 | -------------------------------------------------------------------------------- /apps/contracts/deployments/11155111/Counter.json: -------------------------------------------------------------------------------- 1 | {"address":"0xc4793A8CB76A59Fd9E8341882784cD87fE8aFe87","startBlock":5102019} -------------------------------------------------------------------------------- /apps/contracts/deployments/1337/Counter.json: -------------------------------------------------------------------------------- 1 | {"address":"0x0f5D1ef48f12b6f691401bfe88c2037c690a6afe","startBlock":17} -------------------------------------------------------------------------------- /apps/contracts/deployments/5/Counter.json: -------------------------------------------------------------------------------- 1 | {"address":"0x5e28e947EcC3684b6F385Dd1bB0C7Fa6f66F8619"} -------------------------------------------------------------------------------- /apps/contracts/deployments/80001/Counter.json: -------------------------------------------------------------------------------- 1 | {"address":"0x4dEd60AC9C6859e19bb809026aFE8128DD810992","startBlock":43248929} -------------------------------------------------------------------------------- /apps/contracts/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | anvil: 4 | image: ghcr.io/foundry-rs/foundry:nightly-c948388a0768eb0017dd579b1ad1d8b4f33627c4 5 | ports: 6 | - "8545:8545" 7 | command: ["anvil --chain-id 1337 --host 0.0.0.0 --steps-tracing"] 8 | graph-node: 9 | image: graphprotocol/graph-node 10 | ports: 11 | - "8000:8000" 12 | - "8001:8001" 13 | - "8020:8020" 14 | - "8030:8030" 15 | - "8040:8040" 16 | depends_on: 17 | - ipfs 18 | - postgres 19 | extra_hosts: 20 | - host.docker.internal:host-gateway 21 | environment: 22 | postgres_host: postgres 23 | postgres_user: graph-node 24 | postgres_pass: let-me-in 25 | postgres_db: graph-node 26 | ipfs: "ipfs:5001" 27 | ethereum: "localhost:http://anvil:8545" 28 | GRAPH_LOG: debug 29 | GRAPH_ALLOW_NON_DETERMINISTIC_IPFS: 1 30 | GRAPH_ETHEREUM_CLEANUP_BLOCKS: 1 31 | ETHEREUM_REORG_THRESHOLD: 1 32 | ETHEREUM_ANCESTOR_COUNT: 1 33 | ipfs: 34 | image: ipfs/kubo:v0.14.0 35 | ports: 36 | - "5001:5001" 37 | volumes: 38 | - ./data/ipfs:/data/ipfs 39 | postgres: 40 | image: postgres:14 41 | ports: 42 | - "5432:5432" 43 | command: 44 | [ 45 | "postgres", 46 | "-cshared_preload_libraries=pg_stat_statements", 47 | "-cmax_connections=200", 48 | ] 49 | environment: 50 | POSTGRES_USER: graph-node 51 | POSTGRES_PASSWORD: let-me-in 52 | POSTGRES_DB: graph-node 53 | # FIXME: remove this env. var. which we shouldn't need. Introduced by 54 | # , maybe as a 55 | # workaround for https://github.com/docker/for-mac/issues/6270? 56 | PGDATA: "/var/lib/postgresql/data" 57 | POSTGRES_INITDB_ARGS: "-E UTF8 --locale=C" 58 | volumes: 59 | - ./data/postgres:/var/lib/postgresql/data 60 | -------------------------------------------------------------------------------- /apps/contracts/foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | src = "src" 3 | out = "out" 4 | libs = ["lib"] 5 | fs_permissions = [ 6 | { access = "read-write", path = "./deployments" }, 7 | { access = "read", path = "./script" }, 8 | ] 9 | 10 | [etherscan] 11 | mumbai = { key = "${API_KEY_POLYGON}" } 12 | sepolia = { key = "${API_KEY_ETHERSCAN}" } 13 | goerli = { key = "${API_KEY_ETHERSCAN}" } 14 | base_goerli = { key = "${API_KEY_BASESCAN}" } 15 | 16 | [rpc_endpoints] 17 | local = "http://localhost:8545" 18 | mumbai = "https://polygon-mumbai.g.alchemy.com/v2/${ALCHEMY_KEY}" 19 | sepolia = "https://eth-sepolia.g.alchemy.com/v2/${ALCHEMY_KEY}" 20 | goerli = "https://eth-goerli.g.alchemy.com/v2/${ALCHEMY_KEY}" 21 | base_goerli = "https://base-goerli.g.alchemy.com/v2/${ALCHEMY_KEY}" 22 | 23 | 24 | # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options 25 | -------------------------------------------------------------------------------- /apps/contracts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "contracts", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "test": "forge test", 6 | "chain": "rm -rf ./data && docker-compose up", 7 | "deploy:local": "forge script CounterScript -s 'deployCounterLocal()' --broadcast", 8 | "deploy:testnets": "forge script CounterScript -s 'deployCounterTesnet()' --broadcast --verify", 9 | "build": "forge compile" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /apps/contracts/remappings.txt: -------------------------------------------------------------------------------- 1 | ds-test/=lib/forge-std/lib/ds-test/src/ 2 | forge-std/=lib/forge-std/src/ 3 | @openzeppelin/contracts/=lib/openzeppelin-contracts/contracts 4 | @openzeppelin-upgradeable/contracts/=lib/openzeppelin-contracts-upgradeable/contracts -------------------------------------------------------------------------------- /apps/contracts/script/Base.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.19; 3 | 4 | import {Script, console2} from "forge-std/Script.sol"; 5 | 6 | contract BaseScript is Script { 7 | enum Cycle { 8 | Local, 9 | Testnet, 10 | Mainnet 11 | } 12 | 13 | enum DeployementChain { 14 | Anvil, 15 | Goerli, 16 | Sepolia, 17 | Mumbai 18 | } 19 | string internal mnemonic = 20 | "test test test test test test test test test test test junk"; 21 | 22 | uint256 internal deployerPrivateKey; 23 | 24 | mapping(DeployementChain => string forkId) public forks; 25 | 26 | modifier broadcastOn(DeployementChain[] memory targetChains) { 27 | for (uint256 i = 0; i < targetChains.length; i++) { 28 | vm.createSelectFork(forks[targetChains[i]]); 29 | console2.log("Broadcasting on chain: ", forks[targetChains[i]]); 30 | vm.startBroadcast(deployerPrivateKey); 31 | _; 32 | vm.stopBroadcast(); 33 | console2.log( 34 | "Broadcasting on chain: ", 35 | forks[targetChains[i]], 36 | " done" 37 | ); 38 | } 39 | } 40 | 41 | modifier setEnvDeploy(Cycle cycle) { 42 | if (cycle == Cycle.Local) { 43 | (, deployerPrivateKey) = deriveRememberKey({ 44 | mnemonic: mnemonic, 45 | index: 1 46 | }); 47 | } else if (cycle == Cycle.Testnet) { 48 | deployerPrivateKey = vm.envUint("TESTNET_PK"); 49 | } else if (cycle == Cycle.Mainnet) { 50 | deployerPrivateKey = vm.envUint("MAINNET_PK"); 51 | } 52 | 53 | _; 54 | } 55 | 56 | function _saveImplementations( 57 | address contractAddress, 58 | string memory contractName 59 | ) internal { 60 | string memory objectName = "export"; 61 | string memory json; 62 | 63 | string memory filePathWithEncodePacked = string( 64 | abi.encodePacked( 65 | "./deployments/", 66 | vm.toString(block.chainid), 67 | "/", 68 | contractName, 69 | ".json" 70 | ) 71 | ); 72 | 73 | json = vm.serializeAddress(objectName, "address", contractAddress); 74 | json = vm.serializeUint(objectName, "startBlock", block.number); 75 | 76 | vm.writeFile(filePathWithEncodePacked, json); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /apps/contracts/script/Counter.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.19; 3 | 4 | import {Script, console2} from "forge-std/Script.sol"; 5 | import {BaseScript} from "./Base.s.sol"; 6 | import {UpgradeableCounter} from "../src/UpgradeableCounter.sol"; 7 | import {UUPSProxy} from "../src/UUPSProxy.sol"; 8 | 9 | contract CounterScript is BaseScript { 10 | DeployementChain[] deploymentChains; 11 | 12 | function setUp() public { 13 | forks[DeployementChain.Anvil] = "local"; 14 | forks[DeployementChain.Goerli] = "goerli"; 15 | forks[DeployementChain.Mumbai] = "mumbai"; 16 | forks[DeployementChain.Sepolia] = "sepolia"; 17 | } 18 | 19 | function deployCounterLocal() public setEnvDeploy(Cycle.Local) { 20 | deploymentChains.push(DeployementChain.Anvil); 21 | 22 | _deployCounter(deploymentChains); 23 | } 24 | 25 | function deployCounterTesnet() public setEnvDeploy(Cycle.Testnet) { 26 | deploymentChains.push(DeployementChain.Sepolia); 27 | 28 | _deployCounter(deploymentChains); 29 | } 30 | 31 | function _deployCounter( 32 | DeployementChain[] memory targetChains 33 | ) internal broadcastOn(targetChains) { 34 | UpgradeableCounter counterImpl = new UpgradeableCounter(); 35 | 36 | UUPSProxy proxy = new UUPSProxy( 37 | address(counterImpl), 38 | abi.encodeWithSelector(counterImpl.initialize.selector) 39 | ); 40 | 41 | UpgradeableCounter(address(proxy)).setNumber(42); 42 | 43 | _saveImplementations(address(proxy), "Counter"); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /apps/contracts/src/UUPSProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.18; 3 | 4 | import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; 5 | 6 | contract UUPSProxy is ERC1967Proxy { 7 | /*solhint-disable no-empty-blocks*/ 8 | constructor( 9 | address _implementation, 10 | bytes memory _data 11 | ) ERC1967Proxy(_implementation, _data) {} 12 | } 13 | -------------------------------------------------------------------------------- /apps/contracts/src/UpgradeableCounter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.19; 3 | 4 | import "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; 5 | import "@openzeppelin-upgradeable/contracts/access/OwnableUpgradeable.sol"; 6 | 7 | contract UpgradeableCounter is OwnableUpgradeable, UUPSUpgradeable { 8 | uint256 public number; 9 | 10 | event NumberSet(uint256 newValue); 11 | 12 | function initialize() public initializer { 13 | __Ownable_init(); 14 | number = 0; 15 | } 16 | 17 | function setNumber(uint256 _number) public { 18 | number = _number; 19 | 20 | emit NumberSet(_number); 21 | } 22 | 23 | function getNumber() public view returns (uint256) { 24 | return number; 25 | } 26 | 27 | function _authorizeUpgrade( 28 | address newImplementation 29 | ) internal override onlyOwner {} 30 | } 31 | -------------------------------------------------------------------------------- /apps/contracts/test/Counter.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.13; 3 | 4 | import {Test, console2} from "forge-std/Test.sol"; 5 | 6 | contract CounterTest is Test { 7 | function setUp() public {} 8 | } 9 | -------------------------------------------------------------------------------- /apps/subgraph/abis/Counter.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "event", 4 | "anonymous": false, 5 | "inputs": [ 6 | { 7 | "name": "previousAdmin", 8 | "internalType": "address", 9 | "type": "address", 10 | "indexed": false 11 | }, 12 | { 13 | "name": "newAdmin", 14 | "internalType": "address", 15 | "type": "address", 16 | "indexed": false 17 | } 18 | ], 19 | "name": "AdminChanged" 20 | }, 21 | { 22 | "type": "event", 23 | "anonymous": false, 24 | "inputs": [ 25 | { 26 | "name": "beacon", 27 | "internalType": "address", 28 | "type": "address", 29 | "indexed": true 30 | } 31 | ], 32 | "name": "BeaconUpgraded" 33 | }, 34 | { 35 | "type": "event", 36 | "anonymous": false, 37 | "inputs": [ 38 | { 39 | "name": "version", 40 | "internalType": "uint8", 41 | "type": "uint8", 42 | "indexed": false 43 | } 44 | ], 45 | "name": "Initialized" 46 | }, 47 | { 48 | "type": "event", 49 | "anonymous": false, 50 | "inputs": [ 51 | { 52 | "name": "newValue", 53 | "internalType": "uint256", 54 | "type": "uint256", 55 | "indexed": false 56 | } 57 | ], 58 | "name": "NumberSet" 59 | }, 60 | { 61 | "type": "event", 62 | "anonymous": false, 63 | "inputs": [ 64 | { 65 | "name": "previousOwner", 66 | "internalType": "address", 67 | "type": "address", 68 | "indexed": true 69 | }, 70 | { 71 | "name": "newOwner", 72 | "internalType": "address", 73 | "type": "address", 74 | "indexed": true 75 | } 76 | ], 77 | "name": "OwnershipTransferred" 78 | }, 79 | { 80 | "type": "event", 81 | "anonymous": false, 82 | "inputs": [ 83 | { 84 | "name": "implementation", 85 | "internalType": "address", 86 | "type": "address", 87 | "indexed": true 88 | } 89 | ], 90 | "name": "Upgraded" 91 | }, 92 | { 93 | "stateMutability": "view", 94 | "type": "function", 95 | "inputs": [], 96 | "name": "getNumber", 97 | "outputs": [ 98 | { 99 | "name": "", 100 | "internalType": "uint256", 101 | "type": "uint256" 102 | } 103 | ] 104 | }, 105 | { 106 | "stateMutability": "nonpayable", 107 | "type": "function", 108 | "inputs": [], 109 | "name": "initialize", 110 | "outputs": [] 111 | }, 112 | { 113 | "stateMutability": "view", 114 | "type": "function", 115 | "inputs": [], 116 | "name": "number", 117 | "outputs": [ 118 | { 119 | "name": "", 120 | "internalType": "uint256", 121 | "type": "uint256" 122 | } 123 | ] 124 | }, 125 | { 126 | "stateMutability": "view", 127 | "type": "function", 128 | "inputs": [], 129 | "name": "owner", 130 | "outputs": [ 131 | { 132 | "name": "", 133 | "internalType": "address", 134 | "type": "address" 135 | } 136 | ] 137 | }, 138 | { 139 | "stateMutability": "view", 140 | "type": "function", 141 | "inputs": [], 142 | "name": "proxiableUUID", 143 | "outputs": [ 144 | { 145 | "name": "", 146 | "internalType": "bytes32", 147 | "type": "bytes32" 148 | } 149 | ] 150 | }, 151 | { 152 | "stateMutability": "nonpayable", 153 | "type": "function", 154 | "inputs": [], 155 | "name": "renounceOwnership", 156 | "outputs": [] 157 | }, 158 | { 159 | "stateMutability": "nonpayable", 160 | "type": "function", 161 | "inputs": [ 162 | { 163 | "name": "_number", 164 | "internalType": "uint256", 165 | "type": "uint256" 166 | } 167 | ], 168 | "name": "setNumber", 169 | "outputs": [] 170 | }, 171 | { 172 | "stateMutability": "nonpayable", 173 | "type": "function", 174 | "inputs": [ 175 | { 176 | "name": "newOwner", 177 | "internalType": "address", 178 | "type": "address" 179 | } 180 | ], 181 | "name": "transferOwnership", 182 | "outputs": [] 183 | }, 184 | { 185 | "stateMutability": "nonpayable", 186 | "type": "function", 187 | "inputs": [ 188 | { 189 | "name": "newImplementation", 190 | "internalType": "address", 191 | "type": "address" 192 | } 193 | ], 194 | "name": "upgradeTo", 195 | "outputs": [] 196 | }, 197 | { 198 | "stateMutability": "payable", 199 | "type": "function", 200 | "inputs": [ 201 | { 202 | "name": "newImplementation", 203 | "internalType": "address", 204 | "type": "address" 205 | }, 206 | { 207 | "name": "data", 208 | "internalType": "bytes", 209 | "type": "bytes" 210 | } 211 | ], 212 | "name": "upgradeToAndCall", 213 | "outputs": [] 214 | } 215 | ] -------------------------------------------------------------------------------- /apps/subgraph/config/11155111.json: -------------------------------------------------------------------------------- 1 | { 2 | "counterAddress": "0xc4793A8CB76A59Fd9E8341882784cD87fE8aFe87", 3 | "startBlock": 5102019, 4 | "network": "sepolia" 5 | } -------------------------------------------------------------------------------- /apps/subgraph/config/1337.json: -------------------------------------------------------------------------------- 1 | { 2 | "counterAddress": "0x71C95911E9a5D330f4D621842EC243EE1343292e", 3 | "startBlock": 108, 4 | "network": "localhost" 5 | } -------------------------------------------------------------------------------- /apps/subgraph/config/5.json: -------------------------------------------------------------------------------- 1 | { 2 | "counterAddress": "0x5e28e947EcC3684b6F385Dd1bB0C7Fa6f66F8619", 3 | "network": "goerli" 4 | } -------------------------------------------------------------------------------- /apps/subgraph/config/80001.json: -------------------------------------------------------------------------------- 1 | { 2 | "counterAddress": "0x4dEd60AC9C6859e19bb809026aFE8128DD810992", 3 | "startBlock": 43248929, 4 | "network": "mumbai" 5 | } -------------------------------------------------------------------------------- /apps/subgraph/copy-artifacts.ts: -------------------------------------------------------------------------------- 1 | import { counterABI, counterAddress } from "wagmi-config/generated"; 2 | import fs from "fs/promises"; 3 | 4 | const chainIdToNetwork = { 5 | 1337: "localhost", 6 | 5: "goerli", 7 | 80001: "mumbai", 8 | 84531: "base-testnet", 9 | 11155111: "sepolia", 10 | }; 11 | 12 | /** 13 | * This is a list of all the artifacts we want to copy over to the subgraph 14 | * or any other package that needs them. 15 | */ 16 | const artifacts = [ 17 | { 18 | name: "Counter", 19 | abi: counterABI, 20 | addresses: counterAddress, 21 | chainIds: Object.keys(counterAddress).map((chainId) => parseInt(chainId)), 22 | }, 23 | ] as const; 24 | 25 | const getDeploymentStartBlock = async (name: string, chainId: number) => { 26 | return fs 27 | .readFile(`../contracts/deployments/${chainId}/${name}.json`, "utf-8") 28 | .then(JSON.parse) 29 | .then((dep) => dep.startBlock); 30 | }; 31 | 32 | const main = async () => { 33 | for (const artifact of artifacts) { 34 | for (const chainId of artifact.chainIds) { 35 | await fs.writeFile( 36 | `./config/${chainId}.json`, 37 | JSON.stringify( 38 | { 39 | counterAddress: artifact.addresses[chainId], 40 | startBlock: await getDeploymentStartBlock(artifact.name, chainId), 41 | network: chainIdToNetwork[chainId], 42 | }, 43 | null, 44 | 2 45 | ) 46 | ); 47 | await fs.writeFile( 48 | `./abis/${artifact.name}.json`, 49 | JSON.stringify(artifact.abi, null, 2) 50 | ); 51 | } 52 | } 53 | }; 54 | 55 | main(); 56 | -------------------------------------------------------------------------------- /apps/subgraph/generated/Counter/Counter.ts: -------------------------------------------------------------------------------- 1 | // THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | 3 | import { 4 | ethereum, 5 | JSONValue, 6 | TypedMap, 7 | Entity, 8 | Bytes, 9 | Address, 10 | BigInt 11 | } from "@graphprotocol/graph-ts"; 12 | 13 | export class AdminChanged extends ethereum.Event { 14 | get params(): AdminChanged__Params { 15 | return new AdminChanged__Params(this); 16 | } 17 | } 18 | 19 | export class AdminChanged__Params { 20 | _event: AdminChanged; 21 | 22 | constructor(event: AdminChanged) { 23 | this._event = event; 24 | } 25 | 26 | get previousAdmin(): Address { 27 | return this._event.parameters[0].value.toAddress(); 28 | } 29 | 30 | get newAdmin(): Address { 31 | return this._event.parameters[1].value.toAddress(); 32 | } 33 | } 34 | 35 | export class BeaconUpgraded extends ethereum.Event { 36 | get params(): BeaconUpgraded__Params { 37 | return new BeaconUpgraded__Params(this); 38 | } 39 | } 40 | 41 | export class BeaconUpgraded__Params { 42 | _event: BeaconUpgraded; 43 | 44 | constructor(event: BeaconUpgraded) { 45 | this._event = event; 46 | } 47 | 48 | get beacon(): Address { 49 | return this._event.parameters[0].value.toAddress(); 50 | } 51 | } 52 | 53 | export class Initialized extends ethereum.Event { 54 | get params(): Initialized__Params { 55 | return new Initialized__Params(this); 56 | } 57 | } 58 | 59 | export class Initialized__Params { 60 | _event: Initialized; 61 | 62 | constructor(event: Initialized) { 63 | this._event = event; 64 | } 65 | 66 | get version(): i32 { 67 | return this._event.parameters[0].value.toI32(); 68 | } 69 | } 70 | 71 | export class NumberSet extends ethereum.Event { 72 | get params(): NumberSet__Params { 73 | return new NumberSet__Params(this); 74 | } 75 | } 76 | 77 | export class NumberSet__Params { 78 | _event: NumberSet; 79 | 80 | constructor(event: NumberSet) { 81 | this._event = event; 82 | } 83 | 84 | get newValue(): BigInt { 85 | return this._event.parameters[0].value.toBigInt(); 86 | } 87 | } 88 | 89 | export class OwnershipTransferred extends ethereum.Event { 90 | get params(): OwnershipTransferred__Params { 91 | return new OwnershipTransferred__Params(this); 92 | } 93 | } 94 | 95 | export class OwnershipTransferred__Params { 96 | _event: OwnershipTransferred; 97 | 98 | constructor(event: OwnershipTransferred) { 99 | this._event = event; 100 | } 101 | 102 | get previousOwner(): Address { 103 | return this._event.parameters[0].value.toAddress(); 104 | } 105 | 106 | get newOwner(): Address { 107 | return this._event.parameters[1].value.toAddress(); 108 | } 109 | } 110 | 111 | export class Upgraded extends ethereum.Event { 112 | get params(): Upgraded__Params { 113 | return new Upgraded__Params(this); 114 | } 115 | } 116 | 117 | export class Upgraded__Params { 118 | _event: Upgraded; 119 | 120 | constructor(event: Upgraded) { 121 | this._event = event; 122 | } 123 | 124 | get implementation(): Address { 125 | return this._event.parameters[0].value.toAddress(); 126 | } 127 | } 128 | 129 | export class Counter extends ethereum.SmartContract { 130 | static bind(address: Address): Counter { 131 | return new Counter("Counter", address); 132 | } 133 | 134 | getNumber(): BigInt { 135 | let result = super.call("getNumber", "getNumber():(uint256)", []); 136 | 137 | return result[0].toBigInt(); 138 | } 139 | 140 | try_getNumber(): ethereum.CallResult { 141 | let result = super.tryCall("getNumber", "getNumber():(uint256)", []); 142 | if (result.reverted) { 143 | return new ethereum.CallResult(); 144 | } 145 | let value = result.value; 146 | return ethereum.CallResult.fromValue(value[0].toBigInt()); 147 | } 148 | 149 | number(): BigInt { 150 | let result = super.call("number", "number():(uint256)", []); 151 | 152 | return result[0].toBigInt(); 153 | } 154 | 155 | try_number(): ethereum.CallResult { 156 | let result = super.tryCall("number", "number():(uint256)", []); 157 | if (result.reverted) { 158 | return new ethereum.CallResult(); 159 | } 160 | let value = result.value; 161 | return ethereum.CallResult.fromValue(value[0].toBigInt()); 162 | } 163 | 164 | owner(): Address { 165 | let result = super.call("owner", "owner():(address)", []); 166 | 167 | return result[0].toAddress(); 168 | } 169 | 170 | try_owner(): ethereum.CallResult
{ 171 | let result = super.tryCall("owner", "owner():(address)", []); 172 | if (result.reverted) { 173 | return new ethereum.CallResult(); 174 | } 175 | let value = result.value; 176 | return ethereum.CallResult.fromValue(value[0].toAddress()); 177 | } 178 | 179 | proxiableUUID(): Bytes { 180 | let result = super.call("proxiableUUID", "proxiableUUID():(bytes32)", []); 181 | 182 | return result[0].toBytes(); 183 | } 184 | 185 | try_proxiableUUID(): ethereum.CallResult { 186 | let result = super.tryCall( 187 | "proxiableUUID", 188 | "proxiableUUID():(bytes32)", 189 | [] 190 | ); 191 | if (result.reverted) { 192 | return new ethereum.CallResult(); 193 | } 194 | let value = result.value; 195 | return ethereum.CallResult.fromValue(value[0].toBytes()); 196 | } 197 | } 198 | 199 | export class InitializeCall extends ethereum.Call { 200 | get inputs(): InitializeCall__Inputs { 201 | return new InitializeCall__Inputs(this); 202 | } 203 | 204 | get outputs(): InitializeCall__Outputs { 205 | return new InitializeCall__Outputs(this); 206 | } 207 | } 208 | 209 | export class InitializeCall__Inputs { 210 | _call: InitializeCall; 211 | 212 | constructor(call: InitializeCall) { 213 | this._call = call; 214 | } 215 | } 216 | 217 | export class InitializeCall__Outputs { 218 | _call: InitializeCall; 219 | 220 | constructor(call: InitializeCall) { 221 | this._call = call; 222 | } 223 | } 224 | 225 | export class RenounceOwnershipCall extends ethereum.Call { 226 | get inputs(): RenounceOwnershipCall__Inputs { 227 | return new RenounceOwnershipCall__Inputs(this); 228 | } 229 | 230 | get outputs(): RenounceOwnershipCall__Outputs { 231 | return new RenounceOwnershipCall__Outputs(this); 232 | } 233 | } 234 | 235 | export class RenounceOwnershipCall__Inputs { 236 | _call: RenounceOwnershipCall; 237 | 238 | constructor(call: RenounceOwnershipCall) { 239 | this._call = call; 240 | } 241 | } 242 | 243 | export class RenounceOwnershipCall__Outputs { 244 | _call: RenounceOwnershipCall; 245 | 246 | constructor(call: RenounceOwnershipCall) { 247 | this._call = call; 248 | } 249 | } 250 | 251 | export class SetNumberCall extends ethereum.Call { 252 | get inputs(): SetNumberCall__Inputs { 253 | return new SetNumberCall__Inputs(this); 254 | } 255 | 256 | get outputs(): SetNumberCall__Outputs { 257 | return new SetNumberCall__Outputs(this); 258 | } 259 | } 260 | 261 | export class SetNumberCall__Inputs { 262 | _call: SetNumberCall; 263 | 264 | constructor(call: SetNumberCall) { 265 | this._call = call; 266 | } 267 | 268 | get _number(): BigInt { 269 | return this._call.inputValues[0].value.toBigInt(); 270 | } 271 | } 272 | 273 | export class SetNumberCall__Outputs { 274 | _call: SetNumberCall; 275 | 276 | constructor(call: SetNumberCall) { 277 | this._call = call; 278 | } 279 | } 280 | 281 | export class TransferOwnershipCall extends ethereum.Call { 282 | get inputs(): TransferOwnershipCall__Inputs { 283 | return new TransferOwnershipCall__Inputs(this); 284 | } 285 | 286 | get outputs(): TransferOwnershipCall__Outputs { 287 | return new TransferOwnershipCall__Outputs(this); 288 | } 289 | } 290 | 291 | export class TransferOwnershipCall__Inputs { 292 | _call: TransferOwnershipCall; 293 | 294 | constructor(call: TransferOwnershipCall) { 295 | this._call = call; 296 | } 297 | 298 | get newOwner(): Address { 299 | return this._call.inputValues[0].value.toAddress(); 300 | } 301 | } 302 | 303 | export class TransferOwnershipCall__Outputs { 304 | _call: TransferOwnershipCall; 305 | 306 | constructor(call: TransferOwnershipCall) { 307 | this._call = call; 308 | } 309 | } 310 | 311 | export class UpgradeToCall extends ethereum.Call { 312 | get inputs(): UpgradeToCall__Inputs { 313 | return new UpgradeToCall__Inputs(this); 314 | } 315 | 316 | get outputs(): UpgradeToCall__Outputs { 317 | return new UpgradeToCall__Outputs(this); 318 | } 319 | } 320 | 321 | export class UpgradeToCall__Inputs { 322 | _call: UpgradeToCall; 323 | 324 | constructor(call: UpgradeToCall) { 325 | this._call = call; 326 | } 327 | 328 | get newImplementation(): Address { 329 | return this._call.inputValues[0].value.toAddress(); 330 | } 331 | } 332 | 333 | export class UpgradeToCall__Outputs { 334 | _call: UpgradeToCall; 335 | 336 | constructor(call: UpgradeToCall) { 337 | this._call = call; 338 | } 339 | } 340 | 341 | export class UpgradeToAndCallCall extends ethereum.Call { 342 | get inputs(): UpgradeToAndCallCall__Inputs { 343 | return new UpgradeToAndCallCall__Inputs(this); 344 | } 345 | 346 | get outputs(): UpgradeToAndCallCall__Outputs { 347 | return new UpgradeToAndCallCall__Outputs(this); 348 | } 349 | } 350 | 351 | export class UpgradeToAndCallCall__Inputs { 352 | _call: UpgradeToAndCallCall; 353 | 354 | constructor(call: UpgradeToAndCallCall) { 355 | this._call = call; 356 | } 357 | 358 | get newImplementation(): Address { 359 | return this._call.inputValues[0].value.toAddress(); 360 | } 361 | 362 | get data(): Bytes { 363 | return this._call.inputValues[1].value.toBytes(); 364 | } 365 | } 366 | 367 | export class UpgradeToAndCallCall__Outputs { 368 | _call: UpgradeToAndCallCall; 369 | 370 | constructor(call: UpgradeToAndCallCall) { 371 | this._call = call; 372 | } 373 | } 374 | -------------------------------------------------------------------------------- /apps/subgraph/generated/schema.ts: -------------------------------------------------------------------------------- 1 | // THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | 3 | import { 4 | TypedMap, 5 | Entity, 6 | Value, 7 | ValueKind, 8 | store, 9 | Bytes, 10 | BigInt, 11 | BigDecimal 12 | } from "@graphprotocol/graph-ts"; 13 | 14 | export class Account extends Entity { 15 | constructor(id: Bytes) { 16 | super(); 17 | this.set("id", Value.fromBytes(id)); 18 | } 19 | 20 | save(): void { 21 | let id = this.get("id"); 22 | assert(id != null, "Cannot save Account entity without an ID"); 23 | if (id) { 24 | assert( 25 | id.kind == ValueKind.BYTES, 26 | `Entities of type Account must have an ID of type Bytes but the id '${id.displayData()}' is of type ${id.displayKind()}` 27 | ); 28 | store.set("Account", id.toBytes().toHexString(), this); 29 | } 30 | } 31 | 32 | static loadInBlock(id: Bytes): Account | null { 33 | return changetype( 34 | store.get_in_block("Account", id.toHexString()) 35 | ); 36 | } 37 | 38 | static load(id: Bytes): Account | null { 39 | return changetype(store.get("Account", id.toHexString())); 40 | } 41 | 42 | get id(): Bytes { 43 | let value = this.get("id"); 44 | if (!value || value.kind == ValueKind.NULL) { 45 | throw new Error("Cannot return null for a required field."); 46 | } else { 47 | return value.toBytes(); 48 | } 49 | } 50 | 51 | set id(value: Bytes) { 52 | this.set("id", Value.fromBytes(value)); 53 | } 54 | } 55 | 56 | export class Number extends Entity { 57 | constructor(id: Bytes) { 58 | super(); 59 | this.set("id", Value.fromBytes(id)); 60 | } 61 | 62 | save(): void { 63 | let id = this.get("id"); 64 | assert(id != null, "Cannot save Number entity without an ID"); 65 | if (id) { 66 | assert( 67 | id.kind == ValueKind.BYTES, 68 | `Entities of type Number must have an ID of type Bytes but the id '${id.displayData()}' is of type ${id.displayKind()}` 69 | ); 70 | store.set("Number", id.toBytes().toHexString(), this); 71 | } 72 | } 73 | 74 | static loadInBlock(id: Bytes): Number | null { 75 | return changetype( 76 | store.get_in_block("Number", id.toHexString()) 77 | ); 78 | } 79 | 80 | static load(id: Bytes): Number | null { 81 | return changetype(store.get("Number", id.toHexString())); 82 | } 83 | 84 | get id(): Bytes { 85 | let value = this.get("id"); 86 | if (!value || value.kind == ValueKind.NULL) { 87 | throw new Error("Cannot return null for a required field."); 88 | } else { 89 | return value.toBytes(); 90 | } 91 | } 92 | 93 | set id(value: Bytes) { 94 | this.set("id", Value.fromBytes(value)); 95 | } 96 | 97 | get value(): BigInt { 98 | let value = this.get("value"); 99 | if (!value || value.kind == ValueKind.NULL) { 100 | throw new Error("Cannot return null for a required field."); 101 | } else { 102 | return value.toBigInt(); 103 | } 104 | } 105 | 106 | set value(value: BigInt) { 107 | this.set("value", Value.fromBigInt(value)); 108 | } 109 | } 110 | 111 | export class NumberSet extends Entity { 112 | constructor(id: Bytes) { 113 | super(); 114 | this.set("id", Value.fromBytes(id)); 115 | } 116 | 117 | save(): void { 118 | let id = this.get("id"); 119 | assert(id != null, "Cannot save NumberSet entity without an ID"); 120 | if (id) { 121 | assert( 122 | id.kind == ValueKind.BYTES, 123 | `Entities of type NumberSet must have an ID of type Bytes but the id '${id.displayData()}' is of type ${id.displayKind()}` 124 | ); 125 | store.set("NumberSet", id.toBytes().toHexString(), this); 126 | } 127 | } 128 | 129 | static loadInBlock(id: Bytes): NumberSet | null { 130 | return changetype( 131 | store.get_in_block("NumberSet", id.toHexString()) 132 | ); 133 | } 134 | 135 | static load(id: Bytes): NumberSet | null { 136 | return changetype( 137 | store.get("NumberSet", id.toHexString()) 138 | ); 139 | } 140 | 141 | get id(): Bytes { 142 | let value = this.get("id"); 143 | if (!value || value.kind == ValueKind.NULL) { 144 | throw new Error("Cannot return null for a required field."); 145 | } else { 146 | return value.toBytes(); 147 | } 148 | } 149 | 150 | set id(value: Bytes) { 151 | this.set("id", Value.fromBytes(value)); 152 | } 153 | 154 | get owner(): Bytes { 155 | let value = this.get("owner"); 156 | if (!value || value.kind == ValueKind.NULL) { 157 | throw new Error("Cannot return null for a required field."); 158 | } else { 159 | return value.toBytes(); 160 | } 161 | } 162 | 163 | set owner(value: Bytes) { 164 | this.set("owner", Value.fromBytes(value)); 165 | } 166 | 167 | get newValue(): BigInt { 168 | let value = this.get("newValue"); 169 | if (!value || value.kind == ValueKind.NULL) { 170 | throw new Error("Cannot return null for a required field."); 171 | } else { 172 | return value.toBigInt(); 173 | } 174 | } 175 | 176 | set newValue(value: BigInt) { 177 | this.set("newValue", Value.fromBigInt(value)); 178 | } 179 | 180 | get blockNumber(): BigInt { 181 | let value = this.get("blockNumber"); 182 | if (!value || value.kind == ValueKind.NULL) { 183 | throw new Error("Cannot return null for a required field."); 184 | } else { 185 | return value.toBigInt(); 186 | } 187 | } 188 | 189 | set blockNumber(value: BigInt) { 190 | this.set("blockNumber", Value.fromBigInt(value)); 191 | } 192 | 193 | get blockTimestamp(): BigInt { 194 | let value = this.get("blockTimestamp"); 195 | if (!value || value.kind == ValueKind.NULL) { 196 | throw new Error("Cannot return null for a required field."); 197 | } else { 198 | return value.toBigInt(); 199 | } 200 | } 201 | 202 | set blockTimestamp(value: BigInt) { 203 | this.set("blockTimestamp", Value.fromBigInt(value)); 204 | } 205 | 206 | get transactionHash(): Bytes { 207 | let value = this.get("transactionHash"); 208 | if (!value || value.kind == ValueKind.NULL) { 209 | throw new Error("Cannot return null for a required field."); 210 | } else { 211 | return value.toBytes(); 212 | } 213 | } 214 | 215 | set transactionHash(value: Bytes) { 216 | this.set("transactionHash", Value.fromBytes(value)); 217 | } 218 | } 219 | 220 | export class AdminChanged extends Entity { 221 | constructor(id: Bytes) { 222 | super(); 223 | this.set("id", Value.fromBytes(id)); 224 | } 225 | 226 | save(): void { 227 | let id = this.get("id"); 228 | assert(id != null, "Cannot save AdminChanged entity without an ID"); 229 | if (id) { 230 | assert( 231 | id.kind == ValueKind.BYTES, 232 | `Entities of type AdminChanged must have an ID of type Bytes but the id '${id.displayData()}' is of type ${id.displayKind()}` 233 | ); 234 | store.set("AdminChanged", id.toBytes().toHexString(), this); 235 | } 236 | } 237 | 238 | static loadInBlock(id: Bytes): AdminChanged | null { 239 | return changetype( 240 | store.get_in_block("AdminChanged", id.toHexString()) 241 | ); 242 | } 243 | 244 | static load(id: Bytes): AdminChanged | null { 245 | return changetype( 246 | store.get("AdminChanged", id.toHexString()) 247 | ); 248 | } 249 | 250 | get id(): Bytes { 251 | let value = this.get("id"); 252 | if (!value || value.kind == ValueKind.NULL) { 253 | throw new Error("Cannot return null for a required field."); 254 | } else { 255 | return value.toBytes(); 256 | } 257 | } 258 | 259 | set id(value: Bytes) { 260 | this.set("id", Value.fromBytes(value)); 261 | } 262 | 263 | get previousAdmin(): Bytes { 264 | let value = this.get("previousAdmin"); 265 | if (!value || value.kind == ValueKind.NULL) { 266 | throw new Error("Cannot return null for a required field."); 267 | } else { 268 | return value.toBytes(); 269 | } 270 | } 271 | 272 | set previousAdmin(value: Bytes) { 273 | this.set("previousAdmin", Value.fromBytes(value)); 274 | } 275 | 276 | get newAdmin(): Bytes { 277 | let value = this.get("newAdmin"); 278 | if (!value || value.kind == ValueKind.NULL) { 279 | throw new Error("Cannot return null for a required field."); 280 | } else { 281 | return value.toBytes(); 282 | } 283 | } 284 | 285 | set newAdmin(value: Bytes) { 286 | this.set("newAdmin", Value.fromBytes(value)); 287 | } 288 | 289 | get blockNumber(): BigInt { 290 | let value = this.get("blockNumber"); 291 | if (!value || value.kind == ValueKind.NULL) { 292 | throw new Error("Cannot return null for a required field."); 293 | } else { 294 | return value.toBigInt(); 295 | } 296 | } 297 | 298 | set blockNumber(value: BigInt) { 299 | this.set("blockNumber", Value.fromBigInt(value)); 300 | } 301 | 302 | get blockTimestamp(): BigInt { 303 | let value = this.get("blockTimestamp"); 304 | if (!value || value.kind == ValueKind.NULL) { 305 | throw new Error("Cannot return null for a required field."); 306 | } else { 307 | return value.toBigInt(); 308 | } 309 | } 310 | 311 | set blockTimestamp(value: BigInt) { 312 | this.set("blockTimestamp", Value.fromBigInt(value)); 313 | } 314 | 315 | get transactionHash(): Bytes { 316 | let value = this.get("transactionHash"); 317 | if (!value || value.kind == ValueKind.NULL) { 318 | throw new Error("Cannot return null for a required field."); 319 | } else { 320 | return value.toBytes(); 321 | } 322 | } 323 | 324 | set transactionHash(value: Bytes) { 325 | this.set("transactionHash", Value.fromBytes(value)); 326 | } 327 | } 328 | 329 | export class BeaconUpgraded extends Entity { 330 | constructor(id: Bytes) { 331 | super(); 332 | this.set("id", Value.fromBytes(id)); 333 | } 334 | 335 | save(): void { 336 | let id = this.get("id"); 337 | assert(id != null, "Cannot save BeaconUpgraded entity without an ID"); 338 | if (id) { 339 | assert( 340 | id.kind == ValueKind.BYTES, 341 | `Entities of type BeaconUpgraded must have an ID of type Bytes but the id '${id.displayData()}' is of type ${id.displayKind()}` 342 | ); 343 | store.set("BeaconUpgraded", id.toBytes().toHexString(), this); 344 | } 345 | } 346 | 347 | static loadInBlock(id: Bytes): BeaconUpgraded | null { 348 | return changetype( 349 | store.get_in_block("BeaconUpgraded", id.toHexString()) 350 | ); 351 | } 352 | 353 | static load(id: Bytes): BeaconUpgraded | null { 354 | return changetype( 355 | store.get("BeaconUpgraded", id.toHexString()) 356 | ); 357 | } 358 | 359 | get id(): Bytes { 360 | let value = this.get("id"); 361 | if (!value || value.kind == ValueKind.NULL) { 362 | throw new Error("Cannot return null for a required field."); 363 | } else { 364 | return value.toBytes(); 365 | } 366 | } 367 | 368 | set id(value: Bytes) { 369 | this.set("id", Value.fromBytes(value)); 370 | } 371 | 372 | get beacon(): Bytes { 373 | let value = this.get("beacon"); 374 | if (!value || value.kind == ValueKind.NULL) { 375 | throw new Error("Cannot return null for a required field."); 376 | } else { 377 | return value.toBytes(); 378 | } 379 | } 380 | 381 | set beacon(value: Bytes) { 382 | this.set("beacon", Value.fromBytes(value)); 383 | } 384 | 385 | get blockNumber(): BigInt { 386 | let value = this.get("blockNumber"); 387 | if (!value || value.kind == ValueKind.NULL) { 388 | throw new Error("Cannot return null for a required field."); 389 | } else { 390 | return value.toBigInt(); 391 | } 392 | } 393 | 394 | set blockNumber(value: BigInt) { 395 | this.set("blockNumber", Value.fromBigInt(value)); 396 | } 397 | 398 | get blockTimestamp(): BigInt { 399 | let value = this.get("blockTimestamp"); 400 | if (!value || value.kind == ValueKind.NULL) { 401 | throw new Error("Cannot return null for a required field."); 402 | } else { 403 | return value.toBigInt(); 404 | } 405 | } 406 | 407 | set blockTimestamp(value: BigInt) { 408 | this.set("blockTimestamp", Value.fromBigInt(value)); 409 | } 410 | 411 | get transactionHash(): Bytes { 412 | let value = this.get("transactionHash"); 413 | if (!value || value.kind == ValueKind.NULL) { 414 | throw new Error("Cannot return null for a required field."); 415 | } else { 416 | return value.toBytes(); 417 | } 418 | } 419 | 420 | set transactionHash(value: Bytes) { 421 | this.set("transactionHash", Value.fromBytes(value)); 422 | } 423 | } 424 | 425 | export class Initialized extends Entity { 426 | constructor(id: Bytes) { 427 | super(); 428 | this.set("id", Value.fromBytes(id)); 429 | } 430 | 431 | save(): void { 432 | let id = this.get("id"); 433 | assert(id != null, "Cannot save Initialized entity without an ID"); 434 | if (id) { 435 | assert( 436 | id.kind == ValueKind.BYTES, 437 | `Entities of type Initialized must have an ID of type Bytes but the id '${id.displayData()}' is of type ${id.displayKind()}` 438 | ); 439 | store.set("Initialized", id.toBytes().toHexString(), this); 440 | } 441 | } 442 | 443 | static loadInBlock(id: Bytes): Initialized | null { 444 | return changetype( 445 | store.get_in_block("Initialized", id.toHexString()) 446 | ); 447 | } 448 | 449 | static load(id: Bytes): Initialized | null { 450 | return changetype( 451 | store.get("Initialized", id.toHexString()) 452 | ); 453 | } 454 | 455 | get id(): Bytes { 456 | let value = this.get("id"); 457 | if (!value || value.kind == ValueKind.NULL) { 458 | throw new Error("Cannot return null for a required field."); 459 | } else { 460 | return value.toBytes(); 461 | } 462 | } 463 | 464 | set id(value: Bytes) { 465 | this.set("id", Value.fromBytes(value)); 466 | } 467 | 468 | get version(): i32 { 469 | let value = this.get("version"); 470 | if (!value || value.kind == ValueKind.NULL) { 471 | return 0; 472 | } else { 473 | return value.toI32(); 474 | } 475 | } 476 | 477 | set version(value: i32) { 478 | this.set("version", Value.fromI32(value)); 479 | } 480 | 481 | get blockNumber(): BigInt { 482 | let value = this.get("blockNumber"); 483 | if (!value || value.kind == ValueKind.NULL) { 484 | throw new Error("Cannot return null for a required field."); 485 | } else { 486 | return value.toBigInt(); 487 | } 488 | } 489 | 490 | set blockNumber(value: BigInt) { 491 | this.set("blockNumber", Value.fromBigInt(value)); 492 | } 493 | 494 | get blockTimestamp(): BigInt { 495 | let value = this.get("blockTimestamp"); 496 | if (!value || value.kind == ValueKind.NULL) { 497 | throw new Error("Cannot return null for a required field."); 498 | } else { 499 | return value.toBigInt(); 500 | } 501 | } 502 | 503 | set blockTimestamp(value: BigInt) { 504 | this.set("blockTimestamp", Value.fromBigInt(value)); 505 | } 506 | 507 | get transactionHash(): Bytes { 508 | let value = this.get("transactionHash"); 509 | if (!value || value.kind == ValueKind.NULL) { 510 | throw new Error("Cannot return null for a required field."); 511 | } else { 512 | return value.toBytes(); 513 | } 514 | } 515 | 516 | set transactionHash(value: Bytes) { 517 | this.set("transactionHash", Value.fromBytes(value)); 518 | } 519 | } 520 | 521 | export class OwnershipTransferred extends Entity { 522 | constructor(id: Bytes) { 523 | super(); 524 | this.set("id", Value.fromBytes(id)); 525 | } 526 | 527 | save(): void { 528 | let id = this.get("id"); 529 | assert(id != null, "Cannot save OwnershipTransferred entity without an ID"); 530 | if (id) { 531 | assert( 532 | id.kind == ValueKind.BYTES, 533 | `Entities of type OwnershipTransferred must have an ID of type Bytes but the id '${id.displayData()}' is of type ${id.displayKind()}` 534 | ); 535 | store.set("OwnershipTransferred", id.toBytes().toHexString(), this); 536 | } 537 | } 538 | 539 | static loadInBlock(id: Bytes): OwnershipTransferred | null { 540 | return changetype( 541 | store.get_in_block("OwnershipTransferred", id.toHexString()) 542 | ); 543 | } 544 | 545 | static load(id: Bytes): OwnershipTransferred | null { 546 | return changetype( 547 | store.get("OwnershipTransferred", id.toHexString()) 548 | ); 549 | } 550 | 551 | get id(): Bytes { 552 | let value = this.get("id"); 553 | if (!value || value.kind == ValueKind.NULL) { 554 | throw new Error("Cannot return null for a required field."); 555 | } else { 556 | return value.toBytes(); 557 | } 558 | } 559 | 560 | set id(value: Bytes) { 561 | this.set("id", Value.fromBytes(value)); 562 | } 563 | 564 | get previousOwner(): Bytes { 565 | let value = this.get("previousOwner"); 566 | if (!value || value.kind == ValueKind.NULL) { 567 | throw new Error("Cannot return null for a required field."); 568 | } else { 569 | return value.toBytes(); 570 | } 571 | } 572 | 573 | set previousOwner(value: Bytes) { 574 | this.set("previousOwner", Value.fromBytes(value)); 575 | } 576 | 577 | get newOwner(): Bytes { 578 | let value = this.get("newOwner"); 579 | if (!value || value.kind == ValueKind.NULL) { 580 | throw new Error("Cannot return null for a required field."); 581 | } else { 582 | return value.toBytes(); 583 | } 584 | } 585 | 586 | set newOwner(value: Bytes) { 587 | this.set("newOwner", Value.fromBytes(value)); 588 | } 589 | 590 | get blockNumber(): BigInt { 591 | let value = this.get("blockNumber"); 592 | if (!value || value.kind == ValueKind.NULL) { 593 | throw new Error("Cannot return null for a required field."); 594 | } else { 595 | return value.toBigInt(); 596 | } 597 | } 598 | 599 | set blockNumber(value: BigInt) { 600 | this.set("blockNumber", Value.fromBigInt(value)); 601 | } 602 | 603 | get blockTimestamp(): BigInt { 604 | let value = this.get("blockTimestamp"); 605 | if (!value || value.kind == ValueKind.NULL) { 606 | throw new Error("Cannot return null for a required field."); 607 | } else { 608 | return value.toBigInt(); 609 | } 610 | } 611 | 612 | set blockTimestamp(value: BigInt) { 613 | this.set("blockTimestamp", Value.fromBigInt(value)); 614 | } 615 | 616 | get transactionHash(): Bytes { 617 | let value = this.get("transactionHash"); 618 | if (!value || value.kind == ValueKind.NULL) { 619 | throw new Error("Cannot return null for a required field."); 620 | } else { 621 | return value.toBytes(); 622 | } 623 | } 624 | 625 | set transactionHash(value: Bytes) { 626 | this.set("transactionHash", Value.fromBytes(value)); 627 | } 628 | } 629 | 630 | export class Upgraded extends Entity { 631 | constructor(id: Bytes) { 632 | super(); 633 | this.set("id", Value.fromBytes(id)); 634 | } 635 | 636 | save(): void { 637 | let id = this.get("id"); 638 | assert(id != null, "Cannot save Upgraded entity without an ID"); 639 | if (id) { 640 | assert( 641 | id.kind == ValueKind.BYTES, 642 | `Entities of type Upgraded must have an ID of type Bytes but the id '${id.displayData()}' is of type ${id.displayKind()}` 643 | ); 644 | store.set("Upgraded", id.toBytes().toHexString(), this); 645 | } 646 | } 647 | 648 | static loadInBlock(id: Bytes): Upgraded | null { 649 | return changetype( 650 | store.get_in_block("Upgraded", id.toHexString()) 651 | ); 652 | } 653 | 654 | static load(id: Bytes): Upgraded | null { 655 | return changetype(store.get("Upgraded", id.toHexString())); 656 | } 657 | 658 | get id(): Bytes { 659 | let value = this.get("id"); 660 | if (!value || value.kind == ValueKind.NULL) { 661 | throw new Error("Cannot return null for a required field."); 662 | } else { 663 | return value.toBytes(); 664 | } 665 | } 666 | 667 | set id(value: Bytes) { 668 | this.set("id", Value.fromBytes(value)); 669 | } 670 | 671 | get implementation(): Bytes { 672 | let value = this.get("implementation"); 673 | if (!value || value.kind == ValueKind.NULL) { 674 | throw new Error("Cannot return null for a required field."); 675 | } else { 676 | return value.toBytes(); 677 | } 678 | } 679 | 680 | set implementation(value: Bytes) { 681 | this.set("implementation", Value.fromBytes(value)); 682 | } 683 | 684 | get blockNumber(): BigInt { 685 | let value = this.get("blockNumber"); 686 | if (!value || value.kind == ValueKind.NULL) { 687 | throw new Error("Cannot return null for a required field."); 688 | } else { 689 | return value.toBigInt(); 690 | } 691 | } 692 | 693 | set blockNumber(value: BigInt) { 694 | this.set("blockNumber", Value.fromBigInt(value)); 695 | } 696 | 697 | get blockTimestamp(): BigInt { 698 | let value = this.get("blockTimestamp"); 699 | if (!value || value.kind == ValueKind.NULL) { 700 | throw new Error("Cannot return null for a required field."); 701 | } else { 702 | return value.toBigInt(); 703 | } 704 | } 705 | 706 | set blockTimestamp(value: BigInt) { 707 | this.set("blockTimestamp", Value.fromBigInt(value)); 708 | } 709 | 710 | get transactionHash(): Bytes { 711 | let value = this.get("transactionHash"); 712 | if (!value || value.kind == ValueKind.NULL) { 713 | throw new Error("Cannot return null for a required field."); 714 | } else { 715 | return value.toBytes(); 716 | } 717 | } 718 | 719 | set transactionHash(value: Bytes) { 720 | this.set("transactionHash", Value.fromBytes(value)); 721 | } 722 | } 723 | -------------------------------------------------------------------------------- /apps/subgraph/networks.json: -------------------------------------------------------------------------------- 1 | { 2 | "mainnet": { 3 | "Counter": { 4 | "address": "0x5e28e947EcC3684b6F385Dd1bB0C7Fa6f66F8619", 5 | "startBlock": 0 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /apps/subgraph/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "subgraph", 3 | "version": "1.0.0", 4 | "license": "UNLICENSED", 5 | "scripts": { 6 | "codegen": "graph codegen", 7 | "build": "graph build", 8 | "copy-artifacts": "npx tsx copy-artifacts.ts", 9 | "prepare:local": "yarn run copy-artifacts && mustache config/1337.json template.yaml > subgraph.yaml && yarn run codegen", 10 | "prepare:mumbai": "yarn run copy-artifacts && mustache config/80001.json template.yaml > subgraph.yaml && yarn run codegen", 11 | "prepare:sepolia": "yarn run copy-artifacts && mustache config/11155111.json template.yaml > subgraph.yaml && yarn run codegen", 12 | "create-local": "graph create --node http://localhost:8020/ local-graph", 13 | "deploy:local": "yarn run prepare:local && yarn run create-local && graph deploy -l 0.0.1 --node http://localhost:8020/ --ipfs http://localhost:5001 local-graph", 14 | "deploy:mumbai": "yarn run prepare:mumbai && graph deploy --product hosted-service nezz0746/starter-counter-mumbai", 15 | "deploy:sepolia": "yarn run prepare:sepolia && graph deploy --product hosted-service nezz0746/starter-counter-sepolia", 16 | "deploy:testnets": "yarn run deploy:sepolia", 17 | "test": "graph test" 18 | }, 19 | "dependencies": { 20 | "@graphprotocol/graph-cli": "0.61.0", 21 | "@graphprotocol/graph-ts": "0.30.0", 22 | "mustache": "^4.2.0", 23 | "wagmi-config": "*" 24 | }, 25 | "devDependencies": { 26 | "matchstick-as": "0.6.0" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /apps/subgraph/schema.graphql: -------------------------------------------------------------------------------- 1 | type Account @entity(immutable: true) { 2 | id: Bytes! # address 3 | } 4 | 5 | type Number @entity(immutable: false) { 6 | id: Bytes! 7 | value: BigInt! # uint256 8 | } 9 | 10 | type NumberSet @entity(immutable: true) { 11 | id: Bytes! 12 | owner: Account! # address 13 | newValue: BigInt! # uint256 14 | blockNumber: BigInt! 15 | blockTimestamp: BigInt! 16 | transactionHash: Bytes! 17 | } 18 | 19 | type AdminChanged @entity(immutable: true) { 20 | id: Bytes! 21 | previousAdmin: Bytes! # address 22 | newAdmin: Bytes! # address 23 | blockNumber: BigInt! 24 | blockTimestamp: BigInt! 25 | transactionHash: Bytes! 26 | } 27 | 28 | type BeaconUpgraded @entity(immutable: true) { 29 | id: Bytes! 30 | beacon: Bytes! # address 31 | blockNumber: BigInt! 32 | blockTimestamp: BigInt! 33 | transactionHash: Bytes! 34 | } 35 | 36 | type Initialized @entity(immutable: true) { 37 | id: Bytes! 38 | version: Int! # uint8 39 | blockNumber: BigInt! 40 | blockTimestamp: BigInt! 41 | transactionHash: Bytes! 42 | } 43 | 44 | type OwnershipTransferred @entity(immutable: true) { 45 | id: Bytes! 46 | previousOwner: Bytes! # address 47 | newOwner: Bytes! # address 48 | blockNumber: BigInt! 49 | blockTimestamp: BigInt! 50 | transactionHash: Bytes! 51 | } 52 | 53 | type Upgraded @entity(immutable: true) { 54 | id: Bytes! 55 | implementation: Bytes! # address 56 | blockNumber: BigInt! 57 | blockTimestamp: BigInt! 58 | transactionHash: Bytes! 59 | } 60 | -------------------------------------------------------------------------------- /apps/subgraph/src/counter.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AdminChanged as AdminChangedEvent, 3 | BeaconUpgraded as BeaconUpgradedEvent, 4 | Initialized as InitializedEvent, 5 | NumberSet as NumberSetEvent, 6 | OwnershipTransferred as OwnershipTransferredEvent, 7 | Upgraded as UpgradedEvent, 8 | } from "../generated/Counter/Counter"; 9 | import { 10 | Account, 11 | AdminChanged, 12 | BeaconUpgraded, 13 | Initialized, 14 | Number, 15 | NumberSet, 16 | OwnershipTransferred, 17 | Upgraded, 18 | } from "../generated/schema"; 19 | 20 | export function handleAdminChanged(event: AdminChangedEvent): void { 21 | let entity = new AdminChanged( 22 | event.transaction.hash.concatI32(event.logIndex.toI32()) 23 | ); 24 | entity.previousAdmin = event.params.previousAdmin; 25 | entity.newAdmin = event.params.newAdmin; 26 | 27 | entity.blockNumber = event.block.number; 28 | entity.blockTimestamp = event.block.timestamp; 29 | entity.transactionHash = event.transaction.hash; 30 | 31 | entity.save(); 32 | } 33 | 34 | export function handleBeaconUpgraded(event: BeaconUpgradedEvent): void { 35 | let entity = new BeaconUpgraded( 36 | event.transaction.hash.concatI32(event.logIndex.toI32()) 37 | ); 38 | entity.beacon = event.params.beacon; 39 | 40 | entity.blockNumber = event.block.number; 41 | entity.blockTimestamp = event.block.timestamp; 42 | entity.transactionHash = event.transaction.hash; 43 | 44 | entity.save(); 45 | } 46 | 47 | export function handleInitialized(event: InitializedEvent): void { 48 | let entity = new Initialized( 49 | event.transaction.hash.concatI32(event.logIndex.toI32()) 50 | ); 51 | entity.version = event.params.version; 52 | 53 | entity.blockNumber = event.block.number; 54 | entity.blockTimestamp = event.block.timestamp; 55 | entity.transactionHash = event.transaction.hash; 56 | 57 | entity.save(); 58 | } 59 | 60 | export function handleNumberSet(event: NumberSetEvent): void { 61 | let account = Account.load(event.transaction.from); 62 | 63 | if (account == null) { 64 | account = new Account(event.transaction.from); 65 | } 66 | 67 | account.save(); 68 | 69 | let number = Number.load(event.address); 70 | 71 | if (number == null) { 72 | number = new Number(event.address); 73 | } 74 | 75 | number.value = event.params.newValue; 76 | number.save(); 77 | 78 | let numberSet = new NumberSet( 79 | event.transaction.hash.concatI32(event.logIndex.toI32()) 80 | ); 81 | numberSet.newValue = event.params.newValue; 82 | 83 | numberSet.owner = event.transaction.from; 84 | numberSet.blockNumber = event.block.number; 85 | numberSet.blockTimestamp = event.block.timestamp; 86 | numberSet.transactionHash = event.transaction.hash; 87 | 88 | numberSet.save(); 89 | } 90 | 91 | export function handleOwnershipTransferred( 92 | event: OwnershipTransferredEvent 93 | ): void { 94 | let entity = new OwnershipTransferred( 95 | event.transaction.hash.concatI32(event.logIndex.toI32()) 96 | ); 97 | entity.previousOwner = event.params.previousOwner; 98 | entity.newOwner = event.params.newOwner; 99 | 100 | entity.blockNumber = event.block.number; 101 | entity.blockTimestamp = event.block.timestamp; 102 | entity.transactionHash = event.transaction.hash; 103 | 104 | entity.save(); 105 | } 106 | 107 | export function handleUpgraded(event: UpgradedEvent): void { 108 | let entity = new Upgraded( 109 | event.transaction.hash.concatI32(event.logIndex.toI32()) 110 | ); 111 | entity.implementation = event.params.implementation; 112 | 113 | entity.blockNumber = event.block.number; 114 | entity.blockTimestamp = event.block.timestamp; 115 | entity.transactionHash = event.transaction.hash; 116 | 117 | entity.save(); 118 | } 119 | -------------------------------------------------------------------------------- /apps/subgraph/template.yaml: -------------------------------------------------------------------------------- 1 | specVersion: 0.0.5 2 | schema: 3 | file: ./schema.graphql 4 | dataSources: 5 | - kind: ethereum 6 | name: Counter 7 | network: {{ network }} 8 | source: 9 | address: "{{ counterAddress }}" 10 | abi: Counter 11 | startBlock: {{ startBlock }} 12 | mapping: 13 | kind: ethereum/events 14 | apiVersion: 0.0.7 15 | language: wasm/assemblyscript 16 | entities: 17 | - AdminChanged 18 | - BeaconUpgraded 19 | - Initialized 20 | - NumberSet 21 | - Number 22 | - OwnershipTransferred 23 | - Upgraded 24 | abis: 25 | - name: Counter 26 | file: ./abis/Counter.json 27 | eventHandlers: 28 | - event: AdminChanged(address,address) 29 | handler: handleAdminChanged 30 | - event: BeaconUpgraded(indexed address) 31 | handler: handleBeaconUpgraded 32 | - event: Initialized(uint8) 33 | handler: handleInitialized 34 | - event: NumberSet(uint256) 35 | handler: handleNumberSet 36 | - event: OwnershipTransferred(indexed address,indexed address) 37 | handler: handleOwnershipTransferred 38 | - event: Upgraded(indexed address) 39 | handler: handleUpgraded 40 | file: ./src/counter.ts 41 | -------------------------------------------------------------------------------- /apps/subgraph/tests/counter-utils.ts: -------------------------------------------------------------------------------- 1 | import { newMockEvent } from "matchstick-as" 2 | import { ethereum, Address, BigInt } from "@graphprotocol/graph-ts" 3 | import { 4 | AdminChanged, 5 | BeaconUpgraded, 6 | Initialized, 7 | NumberSet, 8 | OwnershipTransferred, 9 | Upgraded 10 | } from "../generated/Counter/Counter" 11 | 12 | export function createAdminChangedEvent( 13 | previousAdmin: Address, 14 | newAdmin: Address 15 | ): AdminChanged { 16 | let adminChangedEvent = changetype(newMockEvent()) 17 | 18 | adminChangedEvent.parameters = new Array() 19 | 20 | adminChangedEvent.parameters.push( 21 | new ethereum.EventParam( 22 | "previousAdmin", 23 | ethereum.Value.fromAddress(previousAdmin) 24 | ) 25 | ) 26 | adminChangedEvent.parameters.push( 27 | new ethereum.EventParam("newAdmin", ethereum.Value.fromAddress(newAdmin)) 28 | ) 29 | 30 | return adminChangedEvent 31 | } 32 | 33 | export function createBeaconUpgradedEvent(beacon: Address): BeaconUpgraded { 34 | let beaconUpgradedEvent = changetype(newMockEvent()) 35 | 36 | beaconUpgradedEvent.parameters = new Array() 37 | 38 | beaconUpgradedEvent.parameters.push( 39 | new ethereum.EventParam("beacon", ethereum.Value.fromAddress(beacon)) 40 | ) 41 | 42 | return beaconUpgradedEvent 43 | } 44 | 45 | export function createInitializedEvent(version: i32): Initialized { 46 | let initializedEvent = changetype(newMockEvent()) 47 | 48 | initializedEvent.parameters = new Array() 49 | 50 | initializedEvent.parameters.push( 51 | new ethereum.EventParam( 52 | "version", 53 | ethereum.Value.fromUnsignedBigInt(BigInt.fromI32(version)) 54 | ) 55 | ) 56 | 57 | return initializedEvent 58 | } 59 | 60 | export function createNumberSetEvent(newValue: BigInt): NumberSet { 61 | let numberSetEvent = changetype(newMockEvent()) 62 | 63 | numberSetEvent.parameters = new Array() 64 | 65 | numberSetEvent.parameters.push( 66 | new ethereum.EventParam( 67 | "newValue", 68 | ethereum.Value.fromUnsignedBigInt(newValue) 69 | ) 70 | ) 71 | 72 | return numberSetEvent 73 | } 74 | 75 | export function createOwnershipTransferredEvent( 76 | previousOwner: Address, 77 | newOwner: Address 78 | ): OwnershipTransferred { 79 | let ownershipTransferredEvent = changetype( 80 | newMockEvent() 81 | ) 82 | 83 | ownershipTransferredEvent.parameters = new Array() 84 | 85 | ownershipTransferredEvent.parameters.push( 86 | new ethereum.EventParam( 87 | "previousOwner", 88 | ethereum.Value.fromAddress(previousOwner) 89 | ) 90 | ) 91 | ownershipTransferredEvent.parameters.push( 92 | new ethereum.EventParam("newOwner", ethereum.Value.fromAddress(newOwner)) 93 | ) 94 | 95 | return ownershipTransferredEvent 96 | } 97 | 98 | export function createUpgradedEvent(implementation: Address): Upgraded { 99 | let upgradedEvent = changetype(newMockEvent()) 100 | 101 | upgradedEvent.parameters = new Array() 102 | 103 | upgradedEvent.parameters.push( 104 | new ethereum.EventParam( 105 | "implementation", 106 | ethereum.Value.fromAddress(implementation) 107 | ) 108 | ) 109 | 110 | return upgradedEvent 111 | } 112 | -------------------------------------------------------------------------------- /apps/subgraph/tests/counter.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | assert, 3 | describe, 4 | test, 5 | clearStore, 6 | beforeAll, 7 | afterAll 8 | } from "matchstick-as/assembly/index" 9 | import { Address, BigInt } from "@graphprotocol/graph-ts" 10 | import { AdminChanged } from "../generated/schema" 11 | import { AdminChanged as AdminChangedEvent } from "../generated/Counter/Counter" 12 | import { handleAdminChanged } from "../src/counter" 13 | import { createAdminChangedEvent } from "./counter-utils" 14 | 15 | // Tests structure (matchstick-as >=0.5.0) 16 | // https://thegraph.com/docs/en/developer/matchstick/#tests-structure-0-5-0 17 | 18 | describe("Describe entity assertions", () => { 19 | beforeAll(() => { 20 | let previousAdmin = Address.fromString( 21 | "0x0000000000000000000000000000000000000001" 22 | ) 23 | let newAdmin = Address.fromString( 24 | "0x0000000000000000000000000000000000000001" 25 | ) 26 | let newAdminChangedEvent = createAdminChangedEvent(previousAdmin, newAdmin) 27 | handleAdminChanged(newAdminChangedEvent) 28 | }) 29 | 30 | afterAll(() => { 31 | clearStore() 32 | }) 33 | 34 | // For more test scenarios, see: 35 | // https://thegraph.com/docs/en/developer/matchstick/#write-a-unit-test 36 | 37 | test("AdminChanged created and stored", () => { 38 | assert.entityCount("AdminChanged", 1) 39 | 40 | // 0xa16081f360e3847006db660bae1c6d1b2e17ec2a is the default address used in newMockEvent() function 41 | assert.fieldEquals( 42 | "AdminChanged", 43 | "0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1", 44 | "previousAdmin", 45 | "0x0000000000000000000000000000000000000001" 46 | ) 47 | assert.fieldEquals( 48 | "AdminChanged", 49 | "0xa16081f360e3847006db660bae1c6d1b2e17ec2a-1", 50 | "newAdmin", 51 | "0x0000000000000000000000000000000000000001" 52 | ) 53 | 54 | // More assert options: 55 | // https://thegraph.com/docs/en/developer/matchstick/#asserts 56 | }) 57 | }) 58 | -------------------------------------------------------------------------------- /apps/subgraph/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@graphprotocol/graph-ts/types/tsconfig.base.json", 3 | "include": ["src", "tests"] 4 | } 5 | -------------------------------------------------------------------------------- /apps/web/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:@typescript-eslint/recommended', 7 | 'plugin:react-hooks/recommended', 8 | ], 9 | ignorePatterns: ['dist', '.eslintrc.cjs'], 10 | parser: '@typescript-eslint/parser', 11 | plugins: ['react-refresh'], 12 | rules: { 13 | 'react-refresh/only-export-components': [ 14 | 'warn', 15 | { allowConstantExport: true }, 16 | ], 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /apps/web/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /apps/web/README.md: -------------------------------------------------------------------------------- 1 | # React + TypeScript + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | 10 | ## Expanding the ESLint configuration 11 | 12 | If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: 13 | 14 | - Configure the top-level `parserOptions` property like this: 15 | 16 | ```js 17 | export default { 18 | // other rules... 19 | parserOptions: { 20 | ecmaVersion: 'latest', 21 | sourceType: 'module', 22 | project: ['./tsconfig.json', './tsconfig.node.json'], 23 | tsconfigRootDir: __dirname, 24 | }, 25 | } 26 | ``` 27 | 28 | - Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` 29 | - Optionally add `plugin:@typescript-eslint/stylistic-type-checked` 30 | - Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list 31 | -------------------------------------------------------------------------------- /apps/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "@rainbow-me/rainbowkit": "^1.1.3", 14 | "alchemy-sdk": "3.1.0", 15 | "classnames": "^2.3.1", 16 | "daisyui": "4.4.19", 17 | "dayjs": "^1.11.10", 18 | "ethers": "^5.6.8", 19 | "react": "^18.2.0", 20 | "react-dom": "^18.2.0", 21 | "shared-config": "*", 22 | "swr": "^2.2.4", 23 | "wagmi": "1.4.8", 24 | "wagmi-config": "*", 25 | "web-kit": "*", 26 | "web-ui": "*" 27 | }, 28 | "devDependencies": { 29 | "@types/react": "^18.2.37", 30 | "@types/react-dom": "^18.2.15", 31 | "@typescript-eslint/eslint-plugin": "^6.10.0", 32 | "@typescript-eslint/parser": "^6.10.0", 33 | "@vitejs/plugin-react": "^4.2.0", 34 | "autoprefixer": "^10.4.16", 35 | "eslint": "^8.53.0", 36 | "eslint-plugin-react-hooks": "^4.6.0", 37 | "eslint-plugin-react-refresh": "^0.4.4", 38 | "postcss": "^8.4.32", 39 | "tailwindcss": "^3.3.6", 40 | "typescript": "^5.2.2", 41 | "vite": "^5.0.0" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /apps/web/postcss.config.js: -------------------------------------------------------------------------------- 1 | import conf from "shared-config/tailwind/postcss.config.js" 2 | 3 | export default conf -------------------------------------------------------------------------------- /apps/web/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/web/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { useCallback, useState } from "react"; 2 | import useChain from "./hooks/useChain"; 3 | import useIndexedTransaction from "./hooks/useIndexed"; 4 | import { counterAddress, usePrepareCounterSetNumber } from "wagmi-config"; 5 | import { 6 | NumberSet, 7 | NumberSet_OrderBy, 8 | OrderDirection, 9 | useNumberQuery, 10 | useNumberSetsQuery, 11 | } from "web-kit"; 12 | import { Button, Card, Table, Web3Account } from "web-ui"; 13 | import dayjs from "dayjs"; 14 | import { useAccount } from "wagmi"; 15 | 16 | function App() { 17 | const { address } = useAccount(); 18 | const { chainId, explorer, isLocal } = useChain(); 19 | 20 | const [number, setNumber] = useState(BigInt(0)); 21 | 22 | const [pendingNumberSet, setPendingNumberSet] = useState< 23 | (NumberSet & { pending?: boolean }) | null 24 | >(null); 25 | 26 | const { data: currentNumber, refetch: refetchCurrentNumber } = useNumberQuery( 27 | { 28 | variables: { 29 | id: counterAddress[ 30 | chainId as keyof typeof counterAddress 31 | ]?.toLowerCase(), 32 | }, 33 | chainId, 34 | } 35 | ); 36 | 37 | const { 38 | data: numberSets, 39 | refetch: refetchNumberUpdates, 40 | } = useNumberSetsQuery( 41 | { 42 | variables: { 43 | orderBy: NumberSet_OrderBy.BlockTimestamp, 44 | orderDirection: OrderDirection.Desc, 45 | }, 46 | chainId, 47 | }, 48 | { 49 | selectFromResult: ({ data, ...rest }) => { 50 | return { data: data?.numberSets ?? [], ...rest }; 51 | }, 52 | } 53 | ); 54 | 55 | const tableDataNumberSets = pendingNumberSet 56 | ? [pendingNumberSet, ...numberSets] 57 | : numberSets; 58 | 59 | const { config } = usePrepareCounterSetNumber({ 60 | args: [number], 61 | }); 62 | 63 | const { execute, loading } = useIndexedTransaction( 64 | config, 65 | useNumberSetsQuery, 66 | ({ numberSets }) => { 67 | return { indexed: Boolean(numberSets?.length) }; 68 | }, 69 | () => { 70 | setPendingNumberSet(null); 71 | refetchNumberUpdates(); 72 | refetchCurrentNumber(); 73 | } 74 | ); 75 | 76 | const executeNumberSet = useCallback(async () => { 77 | await execute()?.then((result) => { 78 | setPendingNumberSet({ 79 | id: result.hash + "-pending", 80 | pending: true, 81 | newValue: number.toString(), 82 | blockNumber: "...", 83 | blockTimestamp: (Date.now() / 1000).toString(), 84 | transactionHash: result.hash, 85 | owner: { 86 | id: address, 87 | }, 88 | }); 89 | }); 90 | }, [number, execute, address, setPendingNumberSet]); 91 | 92 | return ( 93 |
94 |
95 |
96 | 97 |
98 |

99 | Update Counter 100 |

101 |
102 |
103 |
104 |

105 | Current value 106 |

107 |

{currentNumber?.number?.value}

108 |
109 |
110 | 116 |
117 | setNumber(BigInt(e.target.value))} 124 | /> 125 | 132 |
133 |
134 |
135 |
136 |
137 |
138 | 139 |
140 |

141 | Counter Updates History 142 |

143 |
144 |
145 |
    146 | 147 | head={[ 148 | { 149 | label: "Account", 150 | render: (row) => { 151 | return ; 152 | }, 153 | }, 154 | { 155 | label: "Number", 156 | key: "newValue", 157 | }, 158 | { 159 | label: "Block", 160 | key: "blockNumber", 161 | }, 162 | { 163 | label: "Time", 164 | render: (row) => { 165 | return dayjs( 166 | parseInt(row.blockTimestamp) * 1000 167 | ).format("YYYY-MM-DD HH:mm:ss"); 168 | }, 169 | }, 170 | { 171 | label: "Transaction", 172 | render: (row) => { 173 | return ( 174 | !isLocal && ( 175 | 180 | transaction 181 | 182 | ) 183 | ); 184 | }, 185 | }, 186 | ]} 187 | data={tableDataNumberSets} 188 | /> 189 |
190 |
191 |
192 |
193 |
194 |
195 | ); 196 | } 197 | 198 | export default App; 199 | -------------------------------------------------------------------------------- /apps/web/src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/web/src/components/ConnectButton.tsx: -------------------------------------------------------------------------------- 1 | import { ConnectButton as RainbowKitConnectButton } from "@rainbow-me/rainbowkit"; 2 | import { Button, WalletButton } from "web-ui"; 3 | 4 | const ConnectButton = () => { 5 | return ( 6 | 7 | {({ 8 | account, 9 | chain, 10 | openAccountModal, 11 | openChainModal, 12 | openConnectModal, 13 | authenticationStatus, 14 | mounted, 15 | }) => { 16 | // Note: If your app doesn't use authentication, you 17 | // can remove all 'authenticationStatus' checks 18 | const ready = mounted && authenticationStatus !== "loading"; 19 | const connected = 20 | ready && 21 | account && 22 | chain && 23 | (!authenticationStatus || authenticationStatus === "authenticated"); 24 | return ( 25 |
35 | {(() => { 36 | if (!connected) { 37 | return ( 38 | 45 | ); 46 | } 47 | if (chain.unsupported) { 48 | return ( 49 | 56 | ); 57 | } 58 | return ( 59 |
60 | 83 | 89 | {account.displayName} 90 | {account.displayBalance 91 | ? ` (${account.displayBalance})` 92 | : ""} 93 | 94 |
95 | ); 96 | })()} 97 |
98 | ); 99 | }} 100 |
101 | ); 102 | }; 103 | 104 | export default ConnectButton; 105 | -------------------------------------------------------------------------------- /apps/web/src/components/Layout.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from "web-ui"; 2 | import ConnectButton from "./ConnectButton"; 3 | 4 | const Layout = ({ children }: { children: React.ReactNode }) => { 5 | return ( 6 |
7 |
8 |
9 | 10 |
11 |
12 | 13 |
14 |
15 |
{children}
16 |
17 | ); 18 | }; 19 | 20 | export default Layout; 21 | -------------------------------------------------------------------------------- /apps/web/src/hooks/useChain.ts: -------------------------------------------------------------------------------- 1 | import { useNetwork } from "wagmi"; 2 | import { defaultChain } from "wagmi-config"; 3 | import { localhost } from "wagmi/chains"; 4 | 5 | const useChain = () => { 6 | const { chain } = useNetwork(); 7 | 8 | const chainId = chain?.id ?? defaultChain.id; 9 | 10 | return { 11 | isLocal: chainId === localhost.id, 12 | chainId, 13 | explorer: chain?.blockExplorers?.etherscan?.url, 14 | }; 15 | }; 16 | 17 | export default useChain; 18 | -------------------------------------------------------------------------------- /apps/web/src/hooks/useIndexed.ts: -------------------------------------------------------------------------------- 1 | import { Exact, InputMaybe, Scalars } from "web-kit"; 2 | import { UseQuery } from "@reduxjs/toolkit/dist/query/react/buildHooks"; 3 | import { QueryDefinition } from "@reduxjs/toolkit/query"; 4 | import { useEffect, useState } from "react"; 5 | import { useContractWrite, useWaitForTransaction } from "wagmi"; 6 | import useChain from "./useChain"; 7 | import { PrepareWriteContractResult } from "wagmi/actions"; 8 | 9 | const useIndexedTransaction = ( 10 | config: PrepareWriteContractResult, 11 | useQuery: UseQuery< 12 | QueryDefinition< 13 | { 14 | variables: Exact<{ 15 | where?: InputMaybe<{ 16 | transactionHash?: InputMaybe; 17 | }>; 18 | }>; 19 | chainId?: number | undefined; 20 | }, 21 | any, 22 | never, 23 | SubgraphQuery, 24 | "subgraphAPI" 25 | > 26 | >, 27 | selectFromResult: ( 28 | result: SubgraphQuery 29 | ) => { 30 | indexed: boolean; 31 | }, 32 | onSuccessfulIndexing?: () => void 33 | ) => { 34 | const { chainId } = useChain(); 35 | const [polling, setPolling] = useState(false); 36 | 37 | const { data, writeAsync, isLoading: confirmationPending } = useContractWrite( 38 | config 39 | ); 40 | 41 | const { 42 | isSuccess: transactionSucess, 43 | isLoading: transactionPending, 44 | } = useWaitForTransaction({ 45 | hash: data?.hash, 46 | }); 47 | 48 | const { indexed } = useQuery( 49 | { variables: { where: { transactionHash: data?.hash } }, chainId }, 50 | { 51 | pollingInterval: polling ? 2000 : 0, 52 | skip: !polling || !Boolean(data?.hash), 53 | selectFromResult: (result) => { 54 | if (!result.data) { 55 | return { indexed: false }; 56 | } 57 | const { indexed } = selectFromResult(result.data); 58 | return { 59 | indexed, 60 | }; 61 | }, 62 | } 63 | ); 64 | 65 | useEffect(() => { 66 | if (transactionSucess) { 67 | setPolling(true); 68 | } 69 | }, [transactionSucess]); 70 | 71 | useEffect(() => { 72 | if (indexed) { 73 | onSuccessfulIndexing?.(); 74 | setPolling(false); 75 | } 76 | }, [indexed]); 77 | 78 | return { 79 | loading: confirmationPending || transactionPending || polling, 80 | execute: () => { 81 | return writeAsync && writeAsync(); 82 | }, 83 | }; 84 | }; 85 | 86 | export default useIndexedTransaction; 87 | -------------------------------------------------------------------------------- /apps/web/src/hooks/useOwnedNFTs.ts: -------------------------------------------------------------------------------- 1 | import useSWR from "swr"; 2 | import { useAccount } from "wagmi"; 3 | import { getOwnerNFTsPerChainId } from "../services/alchemy"; 4 | 5 | const useOwnedNFTs = (chainId: number) => { 6 | const { address } = useAccount(); 7 | const { data, isLoading, error } = useSWR( 8 | ["/api/owned-nfts", address, chainId], 9 | ([, address, chainId]) => getOwnerNFTsPerChainId(address, chainId), 10 | {} 11 | ); 12 | 13 | return { data, isLoading, error }; 14 | }; 15 | 16 | export default useOwnedNFTs; 17 | -------------------------------------------------------------------------------- /apps/web/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /apps/web/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./App.tsx"; 4 | import "@rainbow-me/rainbowkit/styles.css"; 5 | import "./index.css"; 6 | import Layout from "./components/Layout.tsx"; 7 | import { AppProvider } from "web-kit"; 8 | import { WagmiConfig } from "wagmi"; 9 | import { chains, wagmiConfig } from "wagmi-config"; 10 | import { RainbowKitProvider } from "@rainbow-me/rainbowkit"; 11 | 12 | ReactDOM.createRoot(document.getElementById("root")!).render( 13 | 14 | 15 | 16 | 17 | 18 | {/* TOFIX - DAISYUI BUG: missing/overriden classes */} 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | ); 27 | -------------------------------------------------------------------------------- /apps/web/src/services/alchemy.ts: -------------------------------------------------------------------------------- 1 | import { Address } from "wagmi"; 2 | import { OwnedNft as AlchemyOwnedNFT } from "alchemy-sdk"; 3 | import { getAlchemyNFT } from "shared-config"; 4 | 5 | export interface OwnedNft extends AlchemyOwnedNFT { 6 | chainId: number; 7 | } 8 | 9 | export const getOwnerNFTsPerChainId = async ( 10 | owner: Address | undefined, 11 | chainId: number 12 | ) => { 13 | if (!owner) return; 14 | const nft = getAlchemyNFT(chainId); 15 | 16 | const { ownedNfts: response } = await nft.getNftsForOwner(owner); 17 | 18 | return response.map((nft) => ({ ...nft, chainId })); 19 | }; 20 | -------------------------------------------------------------------------------- /apps/web/src/utils.ts: -------------------------------------------------------------------------------- 1 | export const truncateAddress = (address: string) => { 2 | return `${address.slice(0, 6)}...${address.slice(-4)}`; 3 | }; 4 | -------------------------------------------------------------------------------- /apps/web/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /apps/web/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | import conf from "shared-config/tailwind/tailwind.config.js" 3 | 4 | export default conf -------------------------------------------------------------------------------- /apps/web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true 22 | }, 23 | "include": ["src"], 24 | "references": [{ "path": "./tsconfig.node.json" }] 25 | } 26 | -------------------------------------------------------------------------------- /apps/web/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /apps/web/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react"; 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }); 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-eth-starter", 3 | "author": "@nezz0746", 4 | "license": "MIT", 5 | "version": "1.0.0", 6 | "private": true, 7 | "scripts": { 8 | "build": "turbo run build", 9 | "kit:codegen": "yarn workspace web-kit run codegen", 10 | "chain": "cd apps/contracts && yarn run chain", 11 | "dev": "turbo run dev --parallel", 12 | "deploy:local": "yarn workspace contracts run deploy:local && yarn workspace wagmi-config run generate && yarn workspace subgraph run deploy:local && yarn run kit:codegen", 13 | "deploy:testnets": "yarn workspace contracts run deploy:testnets && yarn workspace wagmi-config run generate && yarn workspace subgraph run deploy:testnets && yarn run kit:codegen", 14 | "lint": "turbo run lint", 15 | "test": "turbo run test", 16 | "test:force": "turbo run test --force", 17 | "add:web": "yarn workspace web add", 18 | "add:kit": "yarn workspace web-kit add", 19 | "add:ui": "yarn workspace web-ui add", 20 | "add:wagmi": "yarn workspace wagmi-config add", 21 | "add:subgraph": "yarn workspace subgraph add", 22 | "add:shared": "yarn workspace shared-config add", 23 | "postinstall": "npx patch-package" 24 | }, 25 | "workspaces": [ 26 | "packages/*", 27 | "apps/*" 28 | ], 29 | "devDependencies": { 30 | "turbo": "^1.10.16" 31 | }, 32 | "engines": { 33 | "npm": ">=7.0.0", 34 | "node": ">=14.0.0" 35 | }, 36 | "packageManager": "yarn@1.22.4" 37 | } 38 | -------------------------------------------------------------------------------- /packages/shared-config/README.md: -------------------------------------------------------------------------------- 1 | # `shared-config` -------------------------------------------------------------------------------- /packages/shared-config/assets.ts: -------------------------------------------------------------------------------- 1 | import op from "./icons/op.svg"; 2 | import polygon from "./icons/polygon.svg"; 3 | import ethereum from "./icons/ethereum.svg"; 4 | import base from "./icons/base.svg"; 5 | import opensea from "./icons/os.svg"; 6 | 7 | export const chainIdToIcon: Record = { 8 | 1: ethereum, 9 | 5: ethereum, 10 | 137: polygon, 11 | 80001: polygon, 12 | 10: op, 13 | 420: op, 14 | 8453: base, 15 | 84531: base, 16 | }; 17 | 18 | export const icons = { 19 | op, 20 | polygon, 21 | ethereum, 22 | base, 23 | opensea, 24 | }; 25 | -------------------------------------------------------------------------------- /packages/shared-config/icons/base.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/shared-config/icons/ethereum.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/shared-config/icons/op.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/shared-config/icons/os.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/shared-config/icons/polygon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/shared-config/index.t.ts: -------------------------------------------------------------------------------- 1 | declare module "*.svg" { 2 | const content: any; 3 | export default content; 4 | } 5 | -------------------------------------------------------------------------------- /packages/shared-config/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./nfts"; 2 | export * from "./variables"; 3 | export * from "./assets"; 4 | -------------------------------------------------------------------------------- /packages/shared-config/nfts.ts: -------------------------------------------------------------------------------- 1 | import { Alchemy, Network } from "alchemy-sdk"; 2 | import { alchemy_key } from "./variables"; 3 | 4 | export const chainIdToOpenseaAssetUrl: Record = { 5 | 1: "https://opensea.io/assets/ethereum", 6 | 5: "https://testnets.opensea.io/assets/goerli", 7 | 11155111: "https://testnets.opensea.io/assets/sepolia", 8 | 10: "https://opensea.io/assets/optimism", 9 | 420: "https://opensea.io/assets/optimism-goerli", 10 | 137: "https://opensea.io/assets/matic", 11 | 80001: "https://testnets.opensea.io/assets/mumbai", 12 | }; 13 | 14 | const chainIdToNetwork: Record = { 15 | 1: Network.ETH_MAINNET, 16 | 5: Network.ETH_GOERLI, 17 | 10: Network.OPT_MAINNET, 18 | 420: Network.OPT_GOERLI, 19 | 137: Network.MATIC_MAINNET, 20 | 80001: Network.MATIC_MUMBAI, 21 | 8453: Network.BASE_MAINNET, 22 | }; 23 | 24 | export const getAlchemyNFT = (chainId: number) => { 25 | return new Alchemy({ 26 | apiKey: alchemy_key, 27 | network: chainIdToNetwork[chainId], 28 | }).nft; 29 | }; 30 | -------------------------------------------------------------------------------- /packages/shared-config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shared-config", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "echo 'Add dev script here'", 7 | "build": "echo 'Add build script here'", 8 | "test": "echo 'Add test script here'", 9 | "lint": "echo 'Add lint script here'" 10 | }, 11 | "dependencies": { 12 | "alchemy-sdk": "3.1.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/shared-config/tailwind/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /packages/shared-config/tailwind/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: [ 3 | "./pages/**/*.{html,js,ts,tsx}", 4 | "./components/**/*.{html,js,ts,tsx}", 5 | "./src/**/*.{html,js,ts,tsx}", 6 | "./lib/**/*.{html,js,ts,tsx}", 7 | ], 8 | theme: { 9 | extend: {}, 10 | }, 11 | plugins: [require("daisyui")], 12 | daisyui: { 13 | themes: ["corporate"] 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /packages/shared-config/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/base.json", 3 | "compilerOptions": { 4 | "module": "ESNext", 5 | "types": ["vite/client"] 6 | }, 7 | "include": ["index.ts", "variables.ts", "assets.ts"], 8 | } -------------------------------------------------------------------------------- /packages/shared-config/variables.ts: -------------------------------------------------------------------------------- 1 | export const appName = "Typescript Starter App"; 2 | 3 | export const alchemy_key = 4 | import.meta.env.VITE_ALCHEMY_KEY || "YOUR_ALCHEMY_KEY"; 5 | 6 | export const projectId = 7 | import.meta.env.VITE_WALLETCONNECT_PROJECT_ID || "YOUR_PROJECT_ID"; 8 | export const localChainEnabled = 9 | import.meta.env.VITE_LOCAL_CHAIN_ENABLED === "true"; 10 | 11 | export const testnetChainEnabled = 12 | import.meta.env.VITE_TESTNET_CHAINS_ENABLED === "true"; 13 | 14 | export const mainnetChainEnabled = 15 | import.meta.env.VITE_MAINNET_CHAINS_ENABLED === "true"; 16 | 17 | console.log({ 18 | localChainEnabled, 19 | testnetChainEnabled, 20 | mainnetChainEnabled, 21 | }); 22 | -------------------------------------------------------------------------------- /packages/tsconfig/base.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Default", 4 | "compilerOptions": { 5 | "composite": false, 6 | "declaration": true, 7 | "declarationMap": true, 8 | "esModuleInterop": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "allowImportingTsExtensions": true, 11 | "noEmit": true, 12 | "inlineSources": false, 13 | "isolatedModules": true, 14 | "moduleResolution": "node", 15 | "noUnusedLocals": false, 16 | "noUnusedParameters": false, 17 | "preserveWatchOutput": true, 18 | "skipLibCheck": true, 19 | "strict": true, 20 | "strictNullChecks": true 21 | }, 22 | "exclude": ["node_modules"] 23 | } 24 | -------------------------------------------------------------------------------- /packages/tsconfig/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tsconfig", 3 | "version": "0.0.0", 4 | "private": true, 5 | "license": "MIT", 6 | "publishConfig": { 7 | "access": "public" 8 | } 9 | } -------------------------------------------------------------------------------- /packages/wagmi-config/README.md: -------------------------------------------------------------------------------- 1 | # `wagmi-config` -------------------------------------------------------------------------------- /packages/wagmi-config/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./wagmi"; 2 | export * from "./generated"; 3 | -------------------------------------------------------------------------------- /packages/wagmi-config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wagmi-config", 3 | "version": "0.0.0", 4 | "type": "module", 5 | "main": "index.ts", 6 | "types": "index.ts", 7 | "private": true, 8 | "scripts": { 9 | "generate": "wagmi generate" 10 | }, 11 | "dependencies": { 12 | "shared-config": "*", 13 | "wagmi": "1.4.8" 14 | }, 15 | "devDependencies": { 16 | "typescript": "^5.2.2", 17 | "@wagmi/cli": "^1.5.2" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/wagmi-config/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "rootDir": ".", 17 | "incremental": true 18 | }, 19 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 20 | "exclude": ["node_modules"] 21 | } 22 | -------------------------------------------------------------------------------- /packages/wagmi-config/wagmi.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "@wagmi/cli"; 2 | import { foundry } from "@wagmi/cli/plugins"; 3 | import { react } from "@wagmi/cli/plugins"; 4 | import fs from "fs/promises"; 5 | import { Address } from "wagmi"; 6 | 7 | export default defineConfig(async () => { 8 | const chainIds = ( 9 | await fs.readdir("../../apps/contracts/deployments", "utf-8") 10 | ).map((chainId) => parseInt(chainId)); 11 | 12 | const deployments: Record> = {}; 13 | 14 | for (const chainId of chainIds) { 15 | const contractNames = await fs.readdir( 16 | `../../apps/contracts/deployments/${chainId}`, 17 | "utf-8" 18 | ); 19 | 20 | for (const contractName of contractNames) { 21 | const name = contractName.split(".")[0]; 22 | if (!deployments[name]) deployments[name] = {}; 23 | deployments[name][chainId] = await ( 24 | await fs 25 | .readFile( 26 | `../../apps/contracts/deployments/${chainId}/${contractName}`, 27 | "utf-8" 28 | ) 29 | .then(JSON.parse) 30 | ).address; 31 | } 32 | } 33 | 34 | console.log({ deployments }); 35 | 36 | return { 37 | out: "generated.ts", 38 | plugins: [ 39 | foundry({ 40 | artifacts: "../../apps/contracts/out", 41 | include: ["UpgradeableCounter.sol/*.json"], 42 | deployments, 43 | }), 44 | react({ 45 | usePrepareContractFunctionWrite: true, 46 | usePrepareContractWrite: true, 47 | }), 48 | ], 49 | }; 50 | }); 51 | -------------------------------------------------------------------------------- /packages/wagmi-config/wagmi.ts: -------------------------------------------------------------------------------- 1 | import { getDefaultWallets } from "@rainbow-me/rainbowkit"; 2 | import { 3 | Chain, 4 | ChainProviderFn, 5 | configureChains, 6 | createConfig, 7 | sepolia, 8 | } from "wagmi"; 9 | import { base, localhost } from "wagmi/chains"; 10 | import { 11 | projectId, 12 | appName, 13 | localChainEnabled, 14 | testnetChainEnabled, 15 | mainnetChainEnabled, 16 | alchemy_key, 17 | } from "shared-config"; 18 | import { alchemyProvider } from "wagmi/providers/alchemy"; 19 | import { jsonRpcProvider } from "wagmi/providers/jsonRpc"; 20 | 21 | let defaultChain: Chain; 22 | let appChains: Chain[] = []; 23 | let providers: ChainProviderFn[] = []; 24 | 25 | if (mainnetChainEnabled) { 26 | defaultChain = base; 27 | appChains = [base]; 28 | } 29 | 30 | if (testnetChainEnabled) { 31 | defaultChain = sepolia; 32 | appChains.push(sepolia); 33 | } 34 | 35 | if (testnetChainEnabled || mainnetChainEnabled) { 36 | providers.push(alchemyProvider({ apiKey: alchemy_key })); 37 | } 38 | 39 | if (localChainEnabled) { 40 | defaultChain = localhost; 41 | appChains.push(localhost); 42 | providers.push( 43 | jsonRpcProvider({ rpc: () => ({ http: "http://localhost:8545" }) }) 44 | ); 45 | } 46 | console.log({ appChains }); 47 | console.log({ providers }); 48 | 49 | const { chains, publicClient } = configureChains(appChains, providers); 50 | 51 | const { connectors } = getDefaultWallets({ 52 | appName, 53 | projectId, 54 | chains, 55 | }); 56 | 57 | const wagmiConfig = createConfig({ 58 | autoConnect: true, 59 | connectors, 60 | publicClient, 61 | }); 62 | 63 | export { wagmiConfig, chains, defaultChain }; 64 | -------------------------------------------------------------------------------- /packages/web-kit/codegen.ts: -------------------------------------------------------------------------------- 1 | import { CodegenConfig } from "@graphql-codegen/cli"; 2 | 3 | const url = "http://localhost:8000/subgraphs/name/local-graph"; 4 | 5 | const config: CodegenConfig = { 6 | overwrite: true, 7 | schema: [url], 8 | documents: ["operations.graphql"], 9 | generates: { 10 | "./src/generated.ts": { 11 | plugins: ["typescript", "typescript-operations", "typescript-rtk-query"], 12 | config: { 13 | importBaseApiFrom: "./subgraph", 14 | importBaseApiAlternateName: "subgraphAPI", 15 | exportHooks: true, 16 | defaultChainId: 1337, 17 | }, 18 | }, 19 | }, 20 | }; 21 | 22 | export default config; 23 | -------------------------------------------------------------------------------- /packages/web-kit/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./src/generated"; 2 | export { AppProvider } from "./src/store"; 3 | -------------------------------------------------------------------------------- /packages/web-kit/operations.graphql: -------------------------------------------------------------------------------- 1 | query account($id: ID!, $block: Block_height) { 2 | account(id: $id, block: $block) { 3 | id 4 | } 5 | }query accounts($skip: Int, $first: Int, $orderBy: Account_orderBy, $orderDirection: OrderDirection, $where: Account_filter, $block: Block_height) { 6 | accounts( 7 | skip: $skip 8 | first: $first 9 | orderBy: $orderBy 10 | orderDirection: $orderDirection 11 | where: $where 12 | block: $block 13 | ) { 14 | id 15 | } 16 | }query number($id: ID!, $block: Block_height) { 17 | number(id: $id, block: $block) { 18 | id 19 | value 20 | } 21 | }query numbers($skip: Int, $first: Int, $orderBy: Number_orderBy, $orderDirection: OrderDirection, $where: Number_filter, $block: Block_height) { 22 | numbers( 23 | skip: $skip 24 | first: $first 25 | orderBy: $orderBy 26 | orderDirection: $orderDirection 27 | where: $where 28 | block: $block 29 | ) { 30 | id 31 | value 32 | } 33 | }query numberSet($id: ID!, $block: Block_height) { 34 | numberSet(id: $id, block: $block) { 35 | id 36 | owner { 37 | id 38 | } 39 | newValue 40 | blockNumber 41 | blockTimestamp 42 | transactionHash 43 | } 44 | }query numberSets($skip: Int, $first: Int, $orderBy: NumberSet_orderBy, $orderDirection: OrderDirection, $where: NumberSet_filter, $block: Block_height) { 45 | numberSets( 46 | skip: $skip 47 | first: $first 48 | orderBy: $orderBy 49 | orderDirection: $orderDirection 50 | where: $where 51 | block: $block 52 | ) { 53 | id 54 | owner { 55 | id 56 | } 57 | newValue 58 | blockNumber 59 | blockTimestamp 60 | transactionHash 61 | } 62 | }query adminChanged($id: ID!, $block: Block_height) { 63 | adminChanged(id: $id, block: $block) { 64 | id 65 | previousAdmin 66 | newAdmin 67 | blockNumber 68 | blockTimestamp 69 | transactionHash 70 | } 71 | }query adminChangeds($skip: Int, $first: Int, $orderBy: AdminChanged_orderBy, $orderDirection: OrderDirection, $where: AdminChanged_filter, $block: Block_height) { 72 | adminChangeds( 73 | skip: $skip 74 | first: $first 75 | orderBy: $orderBy 76 | orderDirection: $orderDirection 77 | where: $where 78 | block: $block 79 | ) { 80 | id 81 | previousAdmin 82 | newAdmin 83 | blockNumber 84 | blockTimestamp 85 | transactionHash 86 | } 87 | }query beaconUpgraded($id: ID!, $block: Block_height) { 88 | beaconUpgraded(id: $id, block: $block) { 89 | id 90 | beacon 91 | blockNumber 92 | blockTimestamp 93 | transactionHash 94 | } 95 | }query beaconUpgradeds($skip: Int, $first: Int, $orderBy: BeaconUpgraded_orderBy, $orderDirection: OrderDirection, $where: BeaconUpgraded_filter, $block: Block_height) { 96 | beaconUpgradeds( 97 | skip: $skip 98 | first: $first 99 | orderBy: $orderBy 100 | orderDirection: $orderDirection 101 | where: $where 102 | block: $block 103 | ) { 104 | id 105 | beacon 106 | blockNumber 107 | blockTimestamp 108 | transactionHash 109 | } 110 | }query initialized($id: ID!, $block: Block_height) { 111 | initialized(id: $id, block: $block) { 112 | id 113 | version 114 | blockNumber 115 | blockTimestamp 116 | transactionHash 117 | } 118 | }query initializeds($skip: Int, $first: Int, $orderBy: Initialized_orderBy, $orderDirection: OrderDirection, $where: Initialized_filter, $block: Block_height) { 119 | initializeds( 120 | skip: $skip 121 | first: $first 122 | orderBy: $orderBy 123 | orderDirection: $orderDirection 124 | where: $where 125 | block: $block 126 | ) { 127 | id 128 | version 129 | blockNumber 130 | blockTimestamp 131 | transactionHash 132 | } 133 | }query ownershipTransferred($id: ID!, $block: Block_height) { 134 | ownershipTransferred(id: $id, block: $block) { 135 | id 136 | previousOwner 137 | newOwner 138 | blockNumber 139 | blockTimestamp 140 | transactionHash 141 | } 142 | }query ownershipTransferreds($skip: Int, $first: Int, $orderBy: OwnershipTransferred_orderBy, $orderDirection: OrderDirection, $where: OwnershipTransferred_filter, $block: Block_height) { 143 | ownershipTransferreds( 144 | skip: $skip 145 | first: $first 146 | orderBy: $orderBy 147 | orderDirection: $orderDirection 148 | where: $where 149 | block: $block 150 | ) { 151 | id 152 | previousOwner 153 | newOwner 154 | blockNumber 155 | blockTimestamp 156 | transactionHash 157 | } 158 | }query upgraded($id: ID!, $block: Block_height) { 159 | upgraded(id: $id, block: $block) { 160 | id 161 | implementation 162 | blockNumber 163 | blockTimestamp 164 | transactionHash 165 | } 166 | }query upgradeds($skip: Int, $first: Int, $orderBy: Upgraded_orderBy, $orderDirection: OrderDirection, $where: Upgraded_filter, $block: Block_height) { 167 | upgradeds( 168 | skip: $skip 169 | first: $first 170 | orderBy: $orderBy 171 | orderDirection: $orderDirection 172 | where: $where 173 | block: $block 174 | ) { 175 | id 176 | implementation 177 | blockNumber 178 | blockTimestamp 179 | transactionHash 180 | } 181 | } -------------------------------------------------------------------------------- /packages/web-kit/ops.cjs: -------------------------------------------------------------------------------- 1 | const { 2 | getIntrospectionQuery, 3 | parse, 4 | buildClientSchema, 5 | print, 6 | } = require("graphql"); 7 | const { buildOperationNodeForField } = require("@graphql-tools/utils"); 8 | const axios = require("axios").default; 9 | const fs = require("fs/promises"); 10 | 11 | async function getSchemaFromUrl(url) { 12 | const response = await axios 13 | .post(url, { query: getIntrospectionQuery().toString() }) 14 | .catch((e) => console.log(e)); 15 | 16 | return buildClientSchema(response.data.data); 17 | } 18 | 19 | const main = async function() { 20 | const schemaUrl = 21 | "http://localhost:8000/subgraphs/name/local-graph"; 22 | 23 | const schema = await getSchemaFromUrl(schemaUrl); 24 | 25 | const operationsDictionary = { 26 | query: { ...(schema.getQueryType()?.getFields() || {}) }, 27 | }; 28 | 29 | let documentString = ""; 30 | for (const operationKind in operationsDictionary) { 31 | // operationsDictionary[operationKind].args = operationsDictionary[operationKind].filter( 32 | // (field) => field.name === 'subgraphError' 33 | // ) 34 | for (const operationName in operationsDictionary[operationKind]) { 35 | // Removing subgraphError argument from the query 36 | operationsDictionary[operationKind][operationName].args.pop() 37 | 38 | // List of queries to remove 39 | const exclude = ["_meta"] 40 | 41 | if(exclude.includes(operationName)) { 42 | continue; 43 | } 44 | 45 | const operationAST = buildOperationNodeForField({ 46 | schema, 47 | kind: operationKind, 48 | field: operationName, 49 | }); 50 | 51 | // Hardcoding naming fixes 52 | operationAST.name.value = operationAST.name.value.replace("_query", ""); 53 | 54 | documentString += print(operationAST); 55 | } 56 | } 57 | 58 | await fs.writeFile("operations.graphql", documentString, (err) => { 59 | if (err) { 60 | console.log(err); 61 | } 62 | }); 63 | 64 | return parse(documentString); 65 | }; 66 | 67 | main(); 68 | -------------------------------------------------------------------------------- /packages/web-kit/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web-kit", 3 | "version": "0.0.1", 4 | "type": "module", 5 | "main": "index.ts", 6 | "types": "inde.ts", 7 | "scripts": { 8 | "codegen": "node ops.cjs && graphql-codegen --config codegen.ts" 9 | }, 10 | "dependencies": { 11 | "@reduxjs/toolkit": "1.9.7", 12 | "@types/react-redux": "^7.1.31", 13 | "graphql-request": "6.1.0", 14 | "react": "18.1.0", 15 | "react-redux": "^8.1.3", 16 | "shared-config": "*", 17 | "wagmi": "1.4.8", 18 | "wagmi-config": "*" 19 | }, 20 | "devDependencies": { 21 | "@graphql-codegen/cli": "5.0.0", 22 | "@graphql-codegen/client-preset": "4.1.0", 23 | "@graphql-codegen/introspection": "4.0.0", 24 | "@graphql-codegen/typescript-graphql-request": "6.0.1", 25 | "@graphql-codegen/typescript-resolvers": "^4.0.1", 26 | "@graphql-codegen/typescript-rtk-query": "^3.1.0", 27 | "codegen-graph-ts": "^0.1.4" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/web-kit/src/generated.ts: -------------------------------------------------------------------------------- 1 | import { subgraphAPI } from './subgraph'; 2 | export type Maybe = T | null; 3 | export type InputMaybe = Maybe; 4 | export type Exact = { [K in keyof T]: T[K] }; 5 | export type MakeOptional = Omit & { [SubKey in K]?: Maybe }; 6 | export type MakeMaybe = Omit & { [SubKey in K]: Maybe }; 7 | export type MakeEmpty = { [_ in K]?: never }; 8 | export type Incremental = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never }; 9 | /** All built-in and custom scalars, mapped to their actual values */ 10 | export type Scalars = { 11 | ID: { input: string; output: string; } 12 | String: { input: string; output: string; } 13 | Boolean: { input: boolean; output: boolean; } 14 | Int: { input: number; output: number; } 15 | Float: { input: number; output: number; } 16 | BigDecimal: { input: any; output: any; } 17 | BigInt: { input: any; output: any; } 18 | Bytes: { input: any; output: any; } 19 | Int8: { input: any; output: any; } 20 | }; 21 | 22 | export type Account = { 23 | __typename?: 'Account'; 24 | id: Scalars['Bytes']['output']; 25 | }; 26 | 27 | export type Account_Filter = { 28 | /** Filter for the block changed event. */ 29 | _change_block?: InputMaybe; 30 | and?: InputMaybe>>; 31 | id?: InputMaybe; 32 | id_contains?: InputMaybe; 33 | id_gt?: InputMaybe; 34 | id_gte?: InputMaybe; 35 | id_in?: InputMaybe>; 36 | id_lt?: InputMaybe; 37 | id_lte?: InputMaybe; 38 | id_not?: InputMaybe; 39 | id_not_contains?: InputMaybe; 40 | id_not_in?: InputMaybe>; 41 | or?: InputMaybe>>; 42 | }; 43 | 44 | export enum Account_OrderBy { 45 | Id = 'id' 46 | } 47 | 48 | export type AdminChanged = { 49 | __typename?: 'AdminChanged'; 50 | blockNumber: Scalars['BigInt']['output']; 51 | blockTimestamp: Scalars['BigInt']['output']; 52 | id: Scalars['Bytes']['output']; 53 | newAdmin: Scalars['Bytes']['output']; 54 | previousAdmin: Scalars['Bytes']['output']; 55 | transactionHash: Scalars['Bytes']['output']; 56 | }; 57 | 58 | export type AdminChanged_Filter = { 59 | /** Filter for the block changed event. */ 60 | _change_block?: InputMaybe; 61 | and?: InputMaybe>>; 62 | blockNumber?: InputMaybe; 63 | blockNumber_gt?: InputMaybe; 64 | blockNumber_gte?: InputMaybe; 65 | blockNumber_in?: InputMaybe>; 66 | blockNumber_lt?: InputMaybe; 67 | blockNumber_lte?: InputMaybe; 68 | blockNumber_not?: InputMaybe; 69 | blockNumber_not_in?: InputMaybe>; 70 | blockTimestamp?: InputMaybe; 71 | blockTimestamp_gt?: InputMaybe; 72 | blockTimestamp_gte?: InputMaybe; 73 | blockTimestamp_in?: InputMaybe>; 74 | blockTimestamp_lt?: InputMaybe; 75 | blockTimestamp_lte?: InputMaybe; 76 | blockTimestamp_not?: InputMaybe; 77 | blockTimestamp_not_in?: InputMaybe>; 78 | id?: InputMaybe; 79 | id_contains?: InputMaybe; 80 | id_gt?: InputMaybe; 81 | id_gte?: InputMaybe; 82 | id_in?: InputMaybe>; 83 | id_lt?: InputMaybe; 84 | id_lte?: InputMaybe; 85 | id_not?: InputMaybe; 86 | id_not_contains?: InputMaybe; 87 | id_not_in?: InputMaybe>; 88 | newAdmin?: InputMaybe; 89 | newAdmin_contains?: InputMaybe; 90 | newAdmin_gt?: InputMaybe; 91 | newAdmin_gte?: InputMaybe; 92 | newAdmin_in?: InputMaybe>; 93 | newAdmin_lt?: InputMaybe; 94 | newAdmin_lte?: InputMaybe; 95 | newAdmin_not?: InputMaybe; 96 | newAdmin_not_contains?: InputMaybe; 97 | newAdmin_not_in?: InputMaybe>; 98 | or?: InputMaybe>>; 99 | previousAdmin?: InputMaybe; 100 | previousAdmin_contains?: InputMaybe; 101 | previousAdmin_gt?: InputMaybe; 102 | previousAdmin_gte?: InputMaybe; 103 | previousAdmin_in?: InputMaybe>; 104 | previousAdmin_lt?: InputMaybe; 105 | previousAdmin_lte?: InputMaybe; 106 | previousAdmin_not?: InputMaybe; 107 | previousAdmin_not_contains?: InputMaybe; 108 | previousAdmin_not_in?: InputMaybe>; 109 | transactionHash?: InputMaybe; 110 | transactionHash_contains?: InputMaybe; 111 | transactionHash_gt?: InputMaybe; 112 | transactionHash_gte?: InputMaybe; 113 | transactionHash_in?: InputMaybe>; 114 | transactionHash_lt?: InputMaybe; 115 | transactionHash_lte?: InputMaybe; 116 | transactionHash_not?: InputMaybe; 117 | transactionHash_not_contains?: InputMaybe; 118 | transactionHash_not_in?: InputMaybe>; 119 | }; 120 | 121 | export enum AdminChanged_OrderBy { 122 | BlockNumber = 'blockNumber', 123 | BlockTimestamp = 'blockTimestamp', 124 | Id = 'id', 125 | NewAdmin = 'newAdmin', 126 | PreviousAdmin = 'previousAdmin', 127 | TransactionHash = 'transactionHash' 128 | } 129 | 130 | export type BeaconUpgraded = { 131 | __typename?: 'BeaconUpgraded'; 132 | beacon: Scalars['Bytes']['output']; 133 | blockNumber: Scalars['BigInt']['output']; 134 | blockTimestamp: Scalars['BigInt']['output']; 135 | id: Scalars['Bytes']['output']; 136 | transactionHash: Scalars['Bytes']['output']; 137 | }; 138 | 139 | export type BeaconUpgraded_Filter = { 140 | /** Filter for the block changed event. */ 141 | _change_block?: InputMaybe; 142 | and?: InputMaybe>>; 143 | beacon?: InputMaybe; 144 | beacon_contains?: InputMaybe; 145 | beacon_gt?: InputMaybe; 146 | beacon_gte?: InputMaybe; 147 | beacon_in?: InputMaybe>; 148 | beacon_lt?: InputMaybe; 149 | beacon_lte?: InputMaybe; 150 | beacon_not?: InputMaybe; 151 | beacon_not_contains?: InputMaybe; 152 | beacon_not_in?: InputMaybe>; 153 | blockNumber?: InputMaybe; 154 | blockNumber_gt?: InputMaybe; 155 | blockNumber_gte?: InputMaybe; 156 | blockNumber_in?: InputMaybe>; 157 | blockNumber_lt?: InputMaybe; 158 | blockNumber_lte?: InputMaybe; 159 | blockNumber_not?: InputMaybe; 160 | blockNumber_not_in?: InputMaybe>; 161 | blockTimestamp?: InputMaybe; 162 | blockTimestamp_gt?: InputMaybe; 163 | blockTimestamp_gte?: InputMaybe; 164 | blockTimestamp_in?: InputMaybe>; 165 | blockTimestamp_lt?: InputMaybe; 166 | blockTimestamp_lte?: InputMaybe; 167 | blockTimestamp_not?: InputMaybe; 168 | blockTimestamp_not_in?: InputMaybe>; 169 | id?: InputMaybe; 170 | id_contains?: InputMaybe; 171 | id_gt?: InputMaybe; 172 | id_gte?: InputMaybe; 173 | id_in?: InputMaybe>; 174 | id_lt?: InputMaybe; 175 | id_lte?: InputMaybe; 176 | id_not?: InputMaybe; 177 | id_not_contains?: InputMaybe; 178 | id_not_in?: InputMaybe>; 179 | or?: InputMaybe>>; 180 | transactionHash?: InputMaybe; 181 | transactionHash_contains?: InputMaybe; 182 | transactionHash_gt?: InputMaybe; 183 | transactionHash_gte?: InputMaybe; 184 | transactionHash_in?: InputMaybe>; 185 | transactionHash_lt?: InputMaybe; 186 | transactionHash_lte?: InputMaybe; 187 | transactionHash_not?: InputMaybe; 188 | transactionHash_not_contains?: InputMaybe; 189 | transactionHash_not_in?: InputMaybe>; 190 | }; 191 | 192 | export enum BeaconUpgraded_OrderBy { 193 | Beacon = 'beacon', 194 | BlockNumber = 'blockNumber', 195 | BlockTimestamp = 'blockTimestamp', 196 | Id = 'id', 197 | TransactionHash = 'transactionHash' 198 | } 199 | 200 | export type BlockChangedFilter = { 201 | number_gte: Scalars['Int']['input']; 202 | }; 203 | 204 | export type Block_Height = { 205 | hash?: InputMaybe; 206 | number?: InputMaybe; 207 | number_gte?: InputMaybe; 208 | }; 209 | 210 | export type Initialized = { 211 | __typename?: 'Initialized'; 212 | blockNumber: Scalars['BigInt']['output']; 213 | blockTimestamp: Scalars['BigInt']['output']; 214 | id: Scalars['Bytes']['output']; 215 | transactionHash: Scalars['Bytes']['output']; 216 | version: Scalars['Int']['output']; 217 | }; 218 | 219 | export type Initialized_Filter = { 220 | /** Filter for the block changed event. */ 221 | _change_block?: InputMaybe; 222 | and?: InputMaybe>>; 223 | blockNumber?: InputMaybe; 224 | blockNumber_gt?: InputMaybe; 225 | blockNumber_gte?: InputMaybe; 226 | blockNumber_in?: InputMaybe>; 227 | blockNumber_lt?: InputMaybe; 228 | blockNumber_lte?: InputMaybe; 229 | blockNumber_not?: InputMaybe; 230 | blockNumber_not_in?: InputMaybe>; 231 | blockTimestamp?: InputMaybe; 232 | blockTimestamp_gt?: InputMaybe; 233 | blockTimestamp_gte?: InputMaybe; 234 | blockTimestamp_in?: InputMaybe>; 235 | blockTimestamp_lt?: InputMaybe; 236 | blockTimestamp_lte?: InputMaybe; 237 | blockTimestamp_not?: InputMaybe; 238 | blockTimestamp_not_in?: InputMaybe>; 239 | id?: InputMaybe; 240 | id_contains?: InputMaybe; 241 | id_gt?: InputMaybe; 242 | id_gte?: InputMaybe; 243 | id_in?: InputMaybe>; 244 | id_lt?: InputMaybe; 245 | id_lte?: InputMaybe; 246 | id_not?: InputMaybe; 247 | id_not_contains?: InputMaybe; 248 | id_not_in?: InputMaybe>; 249 | or?: InputMaybe>>; 250 | transactionHash?: InputMaybe; 251 | transactionHash_contains?: InputMaybe; 252 | transactionHash_gt?: InputMaybe; 253 | transactionHash_gte?: InputMaybe; 254 | transactionHash_in?: InputMaybe>; 255 | transactionHash_lt?: InputMaybe; 256 | transactionHash_lte?: InputMaybe; 257 | transactionHash_not?: InputMaybe; 258 | transactionHash_not_contains?: InputMaybe; 259 | transactionHash_not_in?: InputMaybe>; 260 | version?: InputMaybe; 261 | version_gt?: InputMaybe; 262 | version_gte?: InputMaybe; 263 | version_in?: InputMaybe>; 264 | version_lt?: InputMaybe; 265 | version_lte?: InputMaybe; 266 | version_not?: InputMaybe; 267 | version_not_in?: InputMaybe>; 268 | }; 269 | 270 | export enum Initialized_OrderBy { 271 | BlockNumber = 'blockNumber', 272 | BlockTimestamp = 'blockTimestamp', 273 | Id = 'id', 274 | TransactionHash = 'transactionHash', 275 | Version = 'version' 276 | } 277 | 278 | export type Number = { 279 | __typename?: 'Number'; 280 | id: Scalars['Bytes']['output']; 281 | value: Scalars['BigInt']['output']; 282 | }; 283 | 284 | export type NumberSet = { 285 | __typename?: 'NumberSet'; 286 | blockNumber: Scalars['BigInt']['output']; 287 | blockTimestamp: Scalars['BigInt']['output']; 288 | id: Scalars['Bytes']['output']; 289 | newValue: Scalars['BigInt']['output']; 290 | owner: Account; 291 | transactionHash: Scalars['Bytes']['output']; 292 | }; 293 | 294 | export type NumberSet_Filter = { 295 | /** Filter for the block changed event. */ 296 | _change_block?: InputMaybe; 297 | and?: InputMaybe>>; 298 | blockNumber?: InputMaybe; 299 | blockNumber_gt?: InputMaybe; 300 | blockNumber_gte?: InputMaybe; 301 | blockNumber_in?: InputMaybe>; 302 | blockNumber_lt?: InputMaybe; 303 | blockNumber_lte?: InputMaybe; 304 | blockNumber_not?: InputMaybe; 305 | blockNumber_not_in?: InputMaybe>; 306 | blockTimestamp?: InputMaybe; 307 | blockTimestamp_gt?: InputMaybe; 308 | blockTimestamp_gte?: InputMaybe; 309 | blockTimestamp_in?: InputMaybe>; 310 | blockTimestamp_lt?: InputMaybe; 311 | blockTimestamp_lte?: InputMaybe; 312 | blockTimestamp_not?: InputMaybe; 313 | blockTimestamp_not_in?: InputMaybe>; 314 | id?: InputMaybe; 315 | id_contains?: InputMaybe; 316 | id_gt?: InputMaybe; 317 | id_gte?: InputMaybe; 318 | id_in?: InputMaybe>; 319 | id_lt?: InputMaybe; 320 | id_lte?: InputMaybe; 321 | id_not?: InputMaybe; 322 | id_not_contains?: InputMaybe; 323 | id_not_in?: InputMaybe>; 324 | newValue?: InputMaybe; 325 | newValue_gt?: InputMaybe; 326 | newValue_gte?: InputMaybe; 327 | newValue_in?: InputMaybe>; 328 | newValue_lt?: InputMaybe; 329 | newValue_lte?: InputMaybe; 330 | newValue_not?: InputMaybe; 331 | newValue_not_in?: InputMaybe>; 332 | or?: InputMaybe>>; 333 | owner?: InputMaybe; 334 | owner_?: InputMaybe; 335 | owner_contains?: InputMaybe; 336 | owner_contains_nocase?: InputMaybe; 337 | owner_ends_with?: InputMaybe; 338 | owner_ends_with_nocase?: InputMaybe; 339 | owner_gt?: InputMaybe; 340 | owner_gte?: InputMaybe; 341 | owner_in?: InputMaybe>; 342 | owner_lt?: InputMaybe; 343 | owner_lte?: InputMaybe; 344 | owner_not?: InputMaybe; 345 | owner_not_contains?: InputMaybe; 346 | owner_not_contains_nocase?: InputMaybe; 347 | owner_not_ends_with?: InputMaybe; 348 | owner_not_ends_with_nocase?: InputMaybe; 349 | owner_not_in?: InputMaybe>; 350 | owner_not_starts_with?: InputMaybe; 351 | owner_not_starts_with_nocase?: InputMaybe; 352 | owner_starts_with?: InputMaybe; 353 | owner_starts_with_nocase?: InputMaybe; 354 | transactionHash?: InputMaybe; 355 | transactionHash_contains?: InputMaybe; 356 | transactionHash_gt?: InputMaybe; 357 | transactionHash_gte?: InputMaybe; 358 | transactionHash_in?: InputMaybe>; 359 | transactionHash_lt?: InputMaybe; 360 | transactionHash_lte?: InputMaybe; 361 | transactionHash_not?: InputMaybe; 362 | transactionHash_not_contains?: InputMaybe; 363 | transactionHash_not_in?: InputMaybe>; 364 | }; 365 | 366 | export enum NumberSet_OrderBy { 367 | BlockNumber = 'blockNumber', 368 | BlockTimestamp = 'blockTimestamp', 369 | Id = 'id', 370 | NewValue = 'newValue', 371 | Owner = 'owner', 372 | OwnerId = 'owner__id', 373 | TransactionHash = 'transactionHash' 374 | } 375 | 376 | export type Number_Filter = { 377 | /** Filter for the block changed event. */ 378 | _change_block?: InputMaybe; 379 | and?: InputMaybe>>; 380 | id?: InputMaybe; 381 | id_contains?: InputMaybe; 382 | id_gt?: InputMaybe; 383 | id_gte?: InputMaybe; 384 | id_in?: InputMaybe>; 385 | id_lt?: InputMaybe; 386 | id_lte?: InputMaybe; 387 | id_not?: InputMaybe; 388 | id_not_contains?: InputMaybe; 389 | id_not_in?: InputMaybe>; 390 | or?: InputMaybe>>; 391 | value?: InputMaybe; 392 | value_gt?: InputMaybe; 393 | value_gte?: InputMaybe; 394 | value_in?: InputMaybe>; 395 | value_lt?: InputMaybe; 396 | value_lte?: InputMaybe; 397 | value_not?: InputMaybe; 398 | value_not_in?: InputMaybe>; 399 | }; 400 | 401 | export enum Number_OrderBy { 402 | Id = 'id', 403 | Value = 'value' 404 | } 405 | 406 | /** Defines the order direction, either ascending or descending */ 407 | export enum OrderDirection { 408 | Asc = 'asc', 409 | Desc = 'desc' 410 | } 411 | 412 | export type OwnershipTransferred = { 413 | __typename?: 'OwnershipTransferred'; 414 | blockNumber: Scalars['BigInt']['output']; 415 | blockTimestamp: Scalars['BigInt']['output']; 416 | id: Scalars['Bytes']['output']; 417 | newOwner: Scalars['Bytes']['output']; 418 | previousOwner: Scalars['Bytes']['output']; 419 | transactionHash: Scalars['Bytes']['output']; 420 | }; 421 | 422 | export type OwnershipTransferred_Filter = { 423 | /** Filter for the block changed event. */ 424 | _change_block?: InputMaybe; 425 | and?: InputMaybe>>; 426 | blockNumber?: InputMaybe; 427 | blockNumber_gt?: InputMaybe; 428 | blockNumber_gte?: InputMaybe; 429 | blockNumber_in?: InputMaybe>; 430 | blockNumber_lt?: InputMaybe; 431 | blockNumber_lte?: InputMaybe; 432 | blockNumber_not?: InputMaybe; 433 | blockNumber_not_in?: InputMaybe>; 434 | blockTimestamp?: InputMaybe; 435 | blockTimestamp_gt?: InputMaybe; 436 | blockTimestamp_gte?: InputMaybe; 437 | blockTimestamp_in?: InputMaybe>; 438 | blockTimestamp_lt?: InputMaybe; 439 | blockTimestamp_lte?: InputMaybe; 440 | blockTimestamp_not?: InputMaybe; 441 | blockTimestamp_not_in?: InputMaybe>; 442 | id?: InputMaybe; 443 | id_contains?: InputMaybe; 444 | id_gt?: InputMaybe; 445 | id_gte?: InputMaybe; 446 | id_in?: InputMaybe>; 447 | id_lt?: InputMaybe; 448 | id_lte?: InputMaybe; 449 | id_not?: InputMaybe; 450 | id_not_contains?: InputMaybe; 451 | id_not_in?: InputMaybe>; 452 | newOwner?: InputMaybe; 453 | newOwner_contains?: InputMaybe; 454 | newOwner_gt?: InputMaybe; 455 | newOwner_gte?: InputMaybe; 456 | newOwner_in?: InputMaybe>; 457 | newOwner_lt?: InputMaybe; 458 | newOwner_lte?: InputMaybe; 459 | newOwner_not?: InputMaybe; 460 | newOwner_not_contains?: InputMaybe; 461 | newOwner_not_in?: InputMaybe>; 462 | or?: InputMaybe>>; 463 | previousOwner?: InputMaybe; 464 | previousOwner_contains?: InputMaybe; 465 | previousOwner_gt?: InputMaybe; 466 | previousOwner_gte?: InputMaybe; 467 | previousOwner_in?: InputMaybe>; 468 | previousOwner_lt?: InputMaybe; 469 | previousOwner_lte?: InputMaybe; 470 | previousOwner_not?: InputMaybe; 471 | previousOwner_not_contains?: InputMaybe; 472 | previousOwner_not_in?: InputMaybe>; 473 | transactionHash?: InputMaybe; 474 | transactionHash_contains?: InputMaybe; 475 | transactionHash_gt?: InputMaybe; 476 | transactionHash_gte?: InputMaybe; 477 | transactionHash_in?: InputMaybe>; 478 | transactionHash_lt?: InputMaybe; 479 | transactionHash_lte?: InputMaybe; 480 | transactionHash_not?: InputMaybe; 481 | transactionHash_not_contains?: InputMaybe; 482 | transactionHash_not_in?: InputMaybe>; 483 | }; 484 | 485 | export enum OwnershipTransferred_OrderBy { 486 | BlockNumber = 'blockNumber', 487 | BlockTimestamp = 'blockTimestamp', 488 | Id = 'id', 489 | NewOwner = 'newOwner', 490 | PreviousOwner = 'previousOwner', 491 | TransactionHash = 'transactionHash' 492 | } 493 | 494 | export type Query = { 495 | __typename?: 'Query'; 496 | /** Access to subgraph metadata */ 497 | _meta?: Maybe<_Meta_>; 498 | account?: Maybe; 499 | accounts: Array; 500 | adminChanged?: Maybe; 501 | adminChangeds: Array; 502 | beaconUpgraded?: Maybe; 503 | beaconUpgradeds: Array; 504 | initialized?: Maybe; 505 | initializeds: Array; 506 | number?: Maybe; 507 | numberSet?: Maybe; 508 | numberSets: Array; 509 | numbers: Array; 510 | ownershipTransferred?: Maybe; 511 | ownershipTransferreds: Array; 512 | upgraded?: Maybe; 513 | upgradeds: Array; 514 | }; 515 | 516 | 517 | export type Query_MetaArgs = { 518 | block?: InputMaybe; 519 | }; 520 | 521 | 522 | export type QueryAccountArgs = { 523 | block?: InputMaybe; 524 | id: Scalars['ID']['input']; 525 | subgraphError?: _SubgraphErrorPolicy_; 526 | }; 527 | 528 | 529 | export type QueryAccountsArgs = { 530 | block?: InputMaybe; 531 | first?: InputMaybe; 532 | orderBy?: InputMaybe; 533 | orderDirection?: InputMaybe; 534 | skip?: InputMaybe; 535 | subgraphError?: _SubgraphErrorPolicy_; 536 | where?: InputMaybe; 537 | }; 538 | 539 | 540 | export type QueryAdminChangedArgs = { 541 | block?: InputMaybe; 542 | id: Scalars['ID']['input']; 543 | subgraphError?: _SubgraphErrorPolicy_; 544 | }; 545 | 546 | 547 | export type QueryAdminChangedsArgs = { 548 | block?: InputMaybe; 549 | first?: InputMaybe; 550 | orderBy?: InputMaybe; 551 | orderDirection?: InputMaybe; 552 | skip?: InputMaybe; 553 | subgraphError?: _SubgraphErrorPolicy_; 554 | where?: InputMaybe; 555 | }; 556 | 557 | 558 | export type QueryBeaconUpgradedArgs = { 559 | block?: InputMaybe; 560 | id: Scalars['ID']['input']; 561 | subgraphError?: _SubgraphErrorPolicy_; 562 | }; 563 | 564 | 565 | export type QueryBeaconUpgradedsArgs = { 566 | block?: InputMaybe; 567 | first?: InputMaybe; 568 | orderBy?: InputMaybe; 569 | orderDirection?: InputMaybe; 570 | skip?: InputMaybe; 571 | subgraphError?: _SubgraphErrorPolicy_; 572 | where?: InputMaybe; 573 | }; 574 | 575 | 576 | export type QueryInitializedArgs = { 577 | block?: InputMaybe; 578 | id: Scalars['ID']['input']; 579 | subgraphError?: _SubgraphErrorPolicy_; 580 | }; 581 | 582 | 583 | export type QueryInitializedsArgs = { 584 | block?: InputMaybe; 585 | first?: InputMaybe; 586 | orderBy?: InputMaybe; 587 | orderDirection?: InputMaybe; 588 | skip?: InputMaybe; 589 | subgraphError?: _SubgraphErrorPolicy_; 590 | where?: InputMaybe; 591 | }; 592 | 593 | 594 | export type QueryNumberArgs = { 595 | block?: InputMaybe; 596 | id: Scalars['ID']['input']; 597 | subgraphError?: _SubgraphErrorPolicy_; 598 | }; 599 | 600 | 601 | export type QueryNumberSetArgs = { 602 | block?: InputMaybe; 603 | id: Scalars['ID']['input']; 604 | subgraphError?: _SubgraphErrorPolicy_; 605 | }; 606 | 607 | 608 | export type QueryNumberSetsArgs = { 609 | block?: InputMaybe; 610 | first?: InputMaybe; 611 | orderBy?: InputMaybe; 612 | orderDirection?: InputMaybe; 613 | skip?: InputMaybe; 614 | subgraphError?: _SubgraphErrorPolicy_; 615 | where?: InputMaybe; 616 | }; 617 | 618 | 619 | export type QueryNumbersArgs = { 620 | block?: InputMaybe; 621 | first?: InputMaybe; 622 | orderBy?: InputMaybe; 623 | orderDirection?: InputMaybe; 624 | skip?: InputMaybe; 625 | subgraphError?: _SubgraphErrorPolicy_; 626 | where?: InputMaybe; 627 | }; 628 | 629 | 630 | export type QueryOwnershipTransferredArgs = { 631 | block?: InputMaybe; 632 | id: Scalars['ID']['input']; 633 | subgraphError?: _SubgraphErrorPolicy_; 634 | }; 635 | 636 | 637 | export type QueryOwnershipTransferredsArgs = { 638 | block?: InputMaybe; 639 | first?: InputMaybe; 640 | orderBy?: InputMaybe; 641 | orderDirection?: InputMaybe; 642 | skip?: InputMaybe; 643 | subgraphError?: _SubgraphErrorPolicy_; 644 | where?: InputMaybe; 645 | }; 646 | 647 | 648 | export type QueryUpgradedArgs = { 649 | block?: InputMaybe; 650 | id: Scalars['ID']['input']; 651 | subgraphError?: _SubgraphErrorPolicy_; 652 | }; 653 | 654 | 655 | export type QueryUpgradedsArgs = { 656 | block?: InputMaybe; 657 | first?: InputMaybe; 658 | orderBy?: InputMaybe; 659 | orderDirection?: InputMaybe; 660 | skip?: InputMaybe; 661 | subgraphError?: _SubgraphErrorPolicy_; 662 | where?: InputMaybe; 663 | }; 664 | 665 | export type Subscription = { 666 | __typename?: 'Subscription'; 667 | /** Access to subgraph metadata */ 668 | _meta?: Maybe<_Meta_>; 669 | account?: Maybe; 670 | accounts: Array; 671 | adminChanged?: Maybe; 672 | adminChangeds: Array; 673 | beaconUpgraded?: Maybe; 674 | beaconUpgradeds: Array; 675 | initialized?: Maybe; 676 | initializeds: Array; 677 | number?: Maybe; 678 | numberSet?: Maybe; 679 | numberSets: Array; 680 | numbers: Array; 681 | ownershipTransferred?: Maybe; 682 | ownershipTransferreds: Array; 683 | upgraded?: Maybe; 684 | upgradeds: Array; 685 | }; 686 | 687 | 688 | export type Subscription_MetaArgs = { 689 | block?: InputMaybe; 690 | }; 691 | 692 | 693 | export type SubscriptionAccountArgs = { 694 | block?: InputMaybe; 695 | id: Scalars['ID']['input']; 696 | subgraphError?: _SubgraphErrorPolicy_; 697 | }; 698 | 699 | 700 | export type SubscriptionAccountsArgs = { 701 | block?: InputMaybe; 702 | first?: InputMaybe; 703 | orderBy?: InputMaybe; 704 | orderDirection?: InputMaybe; 705 | skip?: InputMaybe; 706 | subgraphError?: _SubgraphErrorPolicy_; 707 | where?: InputMaybe; 708 | }; 709 | 710 | 711 | export type SubscriptionAdminChangedArgs = { 712 | block?: InputMaybe; 713 | id: Scalars['ID']['input']; 714 | subgraphError?: _SubgraphErrorPolicy_; 715 | }; 716 | 717 | 718 | export type SubscriptionAdminChangedsArgs = { 719 | block?: InputMaybe; 720 | first?: InputMaybe; 721 | orderBy?: InputMaybe; 722 | orderDirection?: InputMaybe; 723 | skip?: InputMaybe; 724 | subgraphError?: _SubgraphErrorPolicy_; 725 | where?: InputMaybe; 726 | }; 727 | 728 | 729 | export type SubscriptionBeaconUpgradedArgs = { 730 | block?: InputMaybe; 731 | id: Scalars['ID']['input']; 732 | subgraphError?: _SubgraphErrorPolicy_; 733 | }; 734 | 735 | 736 | export type SubscriptionBeaconUpgradedsArgs = { 737 | block?: InputMaybe; 738 | first?: InputMaybe; 739 | orderBy?: InputMaybe; 740 | orderDirection?: InputMaybe; 741 | skip?: InputMaybe; 742 | subgraphError?: _SubgraphErrorPolicy_; 743 | where?: InputMaybe; 744 | }; 745 | 746 | 747 | export type SubscriptionInitializedArgs = { 748 | block?: InputMaybe; 749 | id: Scalars['ID']['input']; 750 | subgraphError?: _SubgraphErrorPolicy_; 751 | }; 752 | 753 | 754 | export type SubscriptionInitializedsArgs = { 755 | block?: InputMaybe; 756 | first?: InputMaybe; 757 | orderBy?: InputMaybe; 758 | orderDirection?: InputMaybe; 759 | skip?: InputMaybe; 760 | subgraphError?: _SubgraphErrorPolicy_; 761 | where?: InputMaybe; 762 | }; 763 | 764 | 765 | export type SubscriptionNumberArgs = { 766 | block?: InputMaybe; 767 | id: Scalars['ID']['input']; 768 | subgraphError?: _SubgraphErrorPolicy_; 769 | }; 770 | 771 | 772 | export type SubscriptionNumberSetArgs = { 773 | block?: InputMaybe; 774 | id: Scalars['ID']['input']; 775 | subgraphError?: _SubgraphErrorPolicy_; 776 | }; 777 | 778 | 779 | export type SubscriptionNumberSetsArgs = { 780 | block?: InputMaybe; 781 | first?: InputMaybe; 782 | orderBy?: InputMaybe; 783 | orderDirection?: InputMaybe; 784 | skip?: InputMaybe; 785 | subgraphError?: _SubgraphErrorPolicy_; 786 | where?: InputMaybe; 787 | }; 788 | 789 | 790 | export type SubscriptionNumbersArgs = { 791 | block?: InputMaybe; 792 | first?: InputMaybe; 793 | orderBy?: InputMaybe; 794 | orderDirection?: InputMaybe; 795 | skip?: InputMaybe; 796 | subgraphError?: _SubgraphErrorPolicy_; 797 | where?: InputMaybe; 798 | }; 799 | 800 | 801 | export type SubscriptionOwnershipTransferredArgs = { 802 | block?: InputMaybe; 803 | id: Scalars['ID']['input']; 804 | subgraphError?: _SubgraphErrorPolicy_; 805 | }; 806 | 807 | 808 | export type SubscriptionOwnershipTransferredsArgs = { 809 | block?: InputMaybe; 810 | first?: InputMaybe; 811 | orderBy?: InputMaybe; 812 | orderDirection?: InputMaybe; 813 | skip?: InputMaybe; 814 | subgraphError?: _SubgraphErrorPolicy_; 815 | where?: InputMaybe; 816 | }; 817 | 818 | 819 | export type SubscriptionUpgradedArgs = { 820 | block?: InputMaybe; 821 | id: Scalars['ID']['input']; 822 | subgraphError?: _SubgraphErrorPolicy_; 823 | }; 824 | 825 | 826 | export type SubscriptionUpgradedsArgs = { 827 | block?: InputMaybe; 828 | first?: InputMaybe; 829 | orderBy?: InputMaybe; 830 | orderDirection?: InputMaybe; 831 | skip?: InputMaybe; 832 | subgraphError?: _SubgraphErrorPolicy_; 833 | where?: InputMaybe; 834 | }; 835 | 836 | export type Upgraded = { 837 | __typename?: 'Upgraded'; 838 | blockNumber: Scalars['BigInt']['output']; 839 | blockTimestamp: Scalars['BigInt']['output']; 840 | id: Scalars['Bytes']['output']; 841 | implementation: Scalars['Bytes']['output']; 842 | transactionHash: Scalars['Bytes']['output']; 843 | }; 844 | 845 | export type Upgraded_Filter = { 846 | /** Filter for the block changed event. */ 847 | _change_block?: InputMaybe; 848 | and?: InputMaybe>>; 849 | blockNumber?: InputMaybe; 850 | blockNumber_gt?: InputMaybe; 851 | blockNumber_gte?: InputMaybe; 852 | blockNumber_in?: InputMaybe>; 853 | blockNumber_lt?: InputMaybe; 854 | blockNumber_lte?: InputMaybe; 855 | blockNumber_not?: InputMaybe; 856 | blockNumber_not_in?: InputMaybe>; 857 | blockTimestamp?: InputMaybe; 858 | blockTimestamp_gt?: InputMaybe; 859 | blockTimestamp_gte?: InputMaybe; 860 | blockTimestamp_in?: InputMaybe>; 861 | blockTimestamp_lt?: InputMaybe; 862 | blockTimestamp_lte?: InputMaybe; 863 | blockTimestamp_not?: InputMaybe; 864 | blockTimestamp_not_in?: InputMaybe>; 865 | id?: InputMaybe; 866 | id_contains?: InputMaybe; 867 | id_gt?: InputMaybe; 868 | id_gte?: InputMaybe; 869 | id_in?: InputMaybe>; 870 | id_lt?: InputMaybe; 871 | id_lte?: InputMaybe; 872 | id_not?: InputMaybe; 873 | id_not_contains?: InputMaybe; 874 | id_not_in?: InputMaybe>; 875 | implementation?: InputMaybe; 876 | implementation_contains?: InputMaybe; 877 | implementation_gt?: InputMaybe; 878 | implementation_gte?: InputMaybe; 879 | implementation_in?: InputMaybe>; 880 | implementation_lt?: InputMaybe; 881 | implementation_lte?: InputMaybe; 882 | implementation_not?: InputMaybe; 883 | implementation_not_contains?: InputMaybe; 884 | implementation_not_in?: InputMaybe>; 885 | or?: InputMaybe>>; 886 | transactionHash?: InputMaybe; 887 | transactionHash_contains?: InputMaybe; 888 | transactionHash_gt?: InputMaybe; 889 | transactionHash_gte?: InputMaybe; 890 | transactionHash_in?: InputMaybe>; 891 | transactionHash_lt?: InputMaybe; 892 | transactionHash_lte?: InputMaybe; 893 | transactionHash_not?: InputMaybe; 894 | transactionHash_not_contains?: InputMaybe; 895 | transactionHash_not_in?: InputMaybe>; 896 | }; 897 | 898 | export enum Upgraded_OrderBy { 899 | BlockNumber = 'blockNumber', 900 | BlockTimestamp = 'blockTimestamp', 901 | Id = 'id', 902 | Implementation = 'implementation', 903 | TransactionHash = 'transactionHash' 904 | } 905 | 906 | export type _Block_ = { 907 | __typename?: '_Block_'; 908 | /** The hash of the block */ 909 | hash?: Maybe; 910 | /** The block number */ 911 | number: Scalars['Int']['output']; 912 | /** Integer representation of the timestamp stored in blocks for the chain */ 913 | timestamp?: Maybe; 914 | }; 915 | 916 | /** The type for the top-level _meta field */ 917 | export type _Meta_ = { 918 | __typename?: '_Meta_'; 919 | /** 920 | * Information about a specific subgraph block. The hash of the block 921 | * will be null if the _meta field has a block constraint that asks for 922 | * a block number. It will be filled if the _meta field has no block constraint 923 | * and therefore asks for the latest block 924 | * 925 | */ 926 | block: _Block_; 927 | /** The deployment ID */ 928 | deployment: Scalars['String']['output']; 929 | /** If `true`, the subgraph encountered indexing errors at some past block */ 930 | hasIndexingErrors: Scalars['Boolean']['output']; 931 | }; 932 | 933 | export enum _SubgraphErrorPolicy_ { 934 | /** Data will be returned even if the subgraph has indexing errors */ 935 | Allow = 'allow', 936 | /** If the subgraph has indexing errors, data will be omitted. The default. */ 937 | Deny = 'deny' 938 | } 939 | 940 | export type AccountQueryVariables = Exact<{ 941 | id: Scalars['ID']['input']; 942 | block?: InputMaybe; 943 | }>; 944 | 945 | 946 | export type AccountQuery = { __typename?: 'Query', account?: { __typename?: 'Account', id: any } | null }; 947 | 948 | export type AccountsQueryVariables = Exact<{ 949 | skip?: InputMaybe; 950 | first?: InputMaybe; 951 | orderBy?: InputMaybe; 952 | orderDirection?: InputMaybe; 953 | where?: InputMaybe; 954 | block?: InputMaybe; 955 | }>; 956 | 957 | 958 | export type AccountsQuery = { __typename?: 'Query', accounts: Array<{ __typename?: 'Account', id: any }> }; 959 | 960 | export type NumberQueryVariables = Exact<{ 961 | id: Scalars['ID']['input']; 962 | block?: InputMaybe; 963 | }>; 964 | 965 | 966 | export type NumberQuery = { __typename?: 'Query', number?: { __typename?: 'Number', id: any, value: any } | null }; 967 | 968 | export type NumbersQueryVariables = Exact<{ 969 | skip?: InputMaybe; 970 | first?: InputMaybe; 971 | orderBy?: InputMaybe; 972 | orderDirection?: InputMaybe; 973 | where?: InputMaybe; 974 | block?: InputMaybe; 975 | }>; 976 | 977 | 978 | export type NumbersQuery = { __typename?: 'Query', numbers: Array<{ __typename?: 'Number', id: any, value: any }> }; 979 | 980 | export type NumberSetQueryVariables = Exact<{ 981 | id: Scalars['ID']['input']; 982 | block?: InputMaybe; 983 | }>; 984 | 985 | 986 | export type NumberSetQuery = { __typename?: 'Query', numberSet?: { __typename?: 'NumberSet', id: any, newValue: any, blockNumber: any, blockTimestamp: any, transactionHash: any, owner: { __typename?: 'Account', id: any } } | null }; 987 | 988 | export type NumberSetsQueryVariables = Exact<{ 989 | skip?: InputMaybe; 990 | first?: InputMaybe; 991 | orderBy?: InputMaybe; 992 | orderDirection?: InputMaybe; 993 | where?: InputMaybe; 994 | block?: InputMaybe; 995 | }>; 996 | 997 | 998 | export type NumberSetsQuery = { __typename?: 'Query', numberSets: Array<{ __typename?: 'NumberSet', id: any, newValue: any, blockNumber: any, blockTimestamp: any, transactionHash: any, owner: { __typename?: 'Account', id: any } }> }; 999 | 1000 | export type AdminChangedQueryVariables = Exact<{ 1001 | id: Scalars['ID']['input']; 1002 | block?: InputMaybe; 1003 | }>; 1004 | 1005 | 1006 | export type AdminChangedQuery = { __typename?: 'Query', adminChanged?: { __typename?: 'AdminChanged', id: any, previousAdmin: any, newAdmin: any, blockNumber: any, blockTimestamp: any, transactionHash: any } | null }; 1007 | 1008 | export type AdminChangedsQueryVariables = Exact<{ 1009 | skip?: InputMaybe; 1010 | first?: InputMaybe; 1011 | orderBy?: InputMaybe; 1012 | orderDirection?: InputMaybe; 1013 | where?: InputMaybe; 1014 | block?: InputMaybe; 1015 | }>; 1016 | 1017 | 1018 | export type AdminChangedsQuery = { __typename?: 'Query', adminChangeds: Array<{ __typename?: 'AdminChanged', id: any, previousAdmin: any, newAdmin: any, blockNumber: any, blockTimestamp: any, transactionHash: any }> }; 1019 | 1020 | export type BeaconUpgradedQueryVariables = Exact<{ 1021 | id: Scalars['ID']['input']; 1022 | block?: InputMaybe; 1023 | }>; 1024 | 1025 | 1026 | export type BeaconUpgradedQuery = { __typename?: 'Query', beaconUpgraded?: { __typename?: 'BeaconUpgraded', id: any, beacon: any, blockNumber: any, blockTimestamp: any, transactionHash: any } | null }; 1027 | 1028 | export type BeaconUpgradedsQueryVariables = Exact<{ 1029 | skip?: InputMaybe; 1030 | first?: InputMaybe; 1031 | orderBy?: InputMaybe; 1032 | orderDirection?: InputMaybe; 1033 | where?: InputMaybe; 1034 | block?: InputMaybe; 1035 | }>; 1036 | 1037 | 1038 | export type BeaconUpgradedsQuery = { __typename?: 'Query', beaconUpgradeds: Array<{ __typename?: 'BeaconUpgraded', id: any, beacon: any, blockNumber: any, blockTimestamp: any, transactionHash: any }> }; 1039 | 1040 | export type InitializedQueryVariables = Exact<{ 1041 | id: Scalars['ID']['input']; 1042 | block?: InputMaybe; 1043 | }>; 1044 | 1045 | 1046 | export type InitializedQuery = { __typename?: 'Query', initialized?: { __typename?: 'Initialized', id: any, version: number, blockNumber: any, blockTimestamp: any, transactionHash: any } | null }; 1047 | 1048 | export type InitializedsQueryVariables = Exact<{ 1049 | skip?: InputMaybe; 1050 | first?: InputMaybe; 1051 | orderBy?: InputMaybe; 1052 | orderDirection?: InputMaybe; 1053 | where?: InputMaybe; 1054 | block?: InputMaybe; 1055 | }>; 1056 | 1057 | 1058 | export type InitializedsQuery = { __typename?: 'Query', initializeds: Array<{ __typename?: 'Initialized', id: any, version: number, blockNumber: any, blockTimestamp: any, transactionHash: any }> }; 1059 | 1060 | export type OwnershipTransferredQueryVariables = Exact<{ 1061 | id: Scalars['ID']['input']; 1062 | block?: InputMaybe; 1063 | }>; 1064 | 1065 | 1066 | export type OwnershipTransferredQuery = { __typename?: 'Query', ownershipTransferred?: { __typename?: 'OwnershipTransferred', id: any, previousOwner: any, newOwner: any, blockNumber: any, blockTimestamp: any, transactionHash: any } | null }; 1067 | 1068 | export type OwnershipTransferredsQueryVariables = Exact<{ 1069 | skip?: InputMaybe; 1070 | first?: InputMaybe; 1071 | orderBy?: InputMaybe; 1072 | orderDirection?: InputMaybe; 1073 | where?: InputMaybe; 1074 | block?: InputMaybe; 1075 | }>; 1076 | 1077 | 1078 | export type OwnershipTransferredsQuery = { __typename?: 'Query', ownershipTransferreds: Array<{ __typename?: 'OwnershipTransferred', id: any, previousOwner: any, newOwner: any, blockNumber: any, blockTimestamp: any, transactionHash: any }> }; 1079 | 1080 | export type UpgradedQueryVariables = Exact<{ 1081 | id: Scalars['ID']['input']; 1082 | block?: InputMaybe; 1083 | }>; 1084 | 1085 | 1086 | export type UpgradedQuery = { __typename?: 'Query', upgraded?: { __typename?: 'Upgraded', id: any, implementation: any, blockNumber: any, blockTimestamp: any, transactionHash: any } | null }; 1087 | 1088 | export type UpgradedsQueryVariables = Exact<{ 1089 | skip?: InputMaybe; 1090 | first?: InputMaybe; 1091 | orderBy?: InputMaybe; 1092 | orderDirection?: InputMaybe; 1093 | where?: InputMaybe; 1094 | block?: InputMaybe; 1095 | }>; 1096 | 1097 | 1098 | export type UpgradedsQuery = { __typename?: 'Query', upgradeds: Array<{ __typename?: 'Upgraded', id: any, implementation: any, blockNumber: any, blockTimestamp: any, transactionHash: any }> }; 1099 | 1100 | 1101 | export const AccountDocument = ` 1102 | query account($id: ID!, $block: Block_height) { 1103 | account(id: $id, block: $block) { 1104 | id 1105 | } 1106 | } 1107 | `; 1108 | export const AccountsDocument = ` 1109 | query accounts($skip: Int, $first: Int, $orderBy: Account_orderBy, $orderDirection: OrderDirection, $where: Account_filter, $block: Block_height) { 1110 | accounts( 1111 | skip: $skip 1112 | first: $first 1113 | orderBy: $orderBy 1114 | orderDirection: $orderDirection 1115 | where: $where 1116 | block: $block 1117 | ) { 1118 | id 1119 | } 1120 | } 1121 | `; 1122 | export const NumberDocument = ` 1123 | query number($id: ID!, $block: Block_height) { 1124 | number(id: $id, block: $block) { 1125 | id 1126 | value 1127 | } 1128 | } 1129 | `; 1130 | export const NumbersDocument = ` 1131 | query numbers($skip: Int, $first: Int, $orderBy: Number_orderBy, $orderDirection: OrderDirection, $where: Number_filter, $block: Block_height) { 1132 | numbers( 1133 | skip: $skip 1134 | first: $first 1135 | orderBy: $orderBy 1136 | orderDirection: $orderDirection 1137 | where: $where 1138 | block: $block 1139 | ) { 1140 | id 1141 | value 1142 | } 1143 | } 1144 | `; 1145 | export const NumberSetDocument = ` 1146 | query numberSet($id: ID!, $block: Block_height) { 1147 | numberSet(id: $id, block: $block) { 1148 | id 1149 | owner { 1150 | id 1151 | } 1152 | newValue 1153 | blockNumber 1154 | blockTimestamp 1155 | transactionHash 1156 | } 1157 | } 1158 | `; 1159 | export const NumberSetsDocument = ` 1160 | query numberSets($skip: Int, $first: Int, $orderBy: NumberSet_orderBy, $orderDirection: OrderDirection, $where: NumberSet_filter, $block: Block_height) { 1161 | numberSets( 1162 | skip: $skip 1163 | first: $first 1164 | orderBy: $orderBy 1165 | orderDirection: $orderDirection 1166 | where: $where 1167 | block: $block 1168 | ) { 1169 | id 1170 | owner { 1171 | id 1172 | } 1173 | newValue 1174 | blockNumber 1175 | blockTimestamp 1176 | transactionHash 1177 | } 1178 | } 1179 | `; 1180 | export const AdminChangedDocument = ` 1181 | query adminChanged($id: ID!, $block: Block_height) { 1182 | adminChanged(id: $id, block: $block) { 1183 | id 1184 | previousAdmin 1185 | newAdmin 1186 | blockNumber 1187 | blockTimestamp 1188 | transactionHash 1189 | } 1190 | } 1191 | `; 1192 | export const AdminChangedsDocument = ` 1193 | query adminChangeds($skip: Int, $first: Int, $orderBy: AdminChanged_orderBy, $orderDirection: OrderDirection, $where: AdminChanged_filter, $block: Block_height) { 1194 | adminChangeds( 1195 | skip: $skip 1196 | first: $first 1197 | orderBy: $orderBy 1198 | orderDirection: $orderDirection 1199 | where: $where 1200 | block: $block 1201 | ) { 1202 | id 1203 | previousAdmin 1204 | newAdmin 1205 | blockNumber 1206 | blockTimestamp 1207 | transactionHash 1208 | } 1209 | } 1210 | `; 1211 | export const BeaconUpgradedDocument = ` 1212 | query beaconUpgraded($id: ID!, $block: Block_height) { 1213 | beaconUpgraded(id: $id, block: $block) { 1214 | id 1215 | beacon 1216 | blockNumber 1217 | blockTimestamp 1218 | transactionHash 1219 | } 1220 | } 1221 | `; 1222 | export const BeaconUpgradedsDocument = ` 1223 | query beaconUpgradeds($skip: Int, $first: Int, $orderBy: BeaconUpgraded_orderBy, $orderDirection: OrderDirection, $where: BeaconUpgraded_filter, $block: Block_height) { 1224 | beaconUpgradeds( 1225 | skip: $skip 1226 | first: $first 1227 | orderBy: $orderBy 1228 | orderDirection: $orderDirection 1229 | where: $where 1230 | block: $block 1231 | ) { 1232 | id 1233 | beacon 1234 | blockNumber 1235 | blockTimestamp 1236 | transactionHash 1237 | } 1238 | } 1239 | `; 1240 | export const InitializedDocument = ` 1241 | query initialized($id: ID!, $block: Block_height) { 1242 | initialized(id: $id, block: $block) { 1243 | id 1244 | version 1245 | blockNumber 1246 | blockTimestamp 1247 | transactionHash 1248 | } 1249 | } 1250 | `; 1251 | export const InitializedsDocument = ` 1252 | query initializeds($skip: Int, $first: Int, $orderBy: Initialized_orderBy, $orderDirection: OrderDirection, $where: Initialized_filter, $block: Block_height) { 1253 | initializeds( 1254 | skip: $skip 1255 | first: $first 1256 | orderBy: $orderBy 1257 | orderDirection: $orderDirection 1258 | where: $where 1259 | block: $block 1260 | ) { 1261 | id 1262 | version 1263 | blockNumber 1264 | blockTimestamp 1265 | transactionHash 1266 | } 1267 | } 1268 | `; 1269 | export const OwnershipTransferredDocument = ` 1270 | query ownershipTransferred($id: ID!, $block: Block_height) { 1271 | ownershipTransferred(id: $id, block: $block) { 1272 | id 1273 | previousOwner 1274 | newOwner 1275 | blockNumber 1276 | blockTimestamp 1277 | transactionHash 1278 | } 1279 | } 1280 | `; 1281 | export const OwnershipTransferredsDocument = ` 1282 | query ownershipTransferreds($skip: Int, $first: Int, $orderBy: OwnershipTransferred_orderBy, $orderDirection: OrderDirection, $where: OwnershipTransferred_filter, $block: Block_height) { 1283 | ownershipTransferreds( 1284 | skip: $skip 1285 | first: $first 1286 | orderBy: $orderBy 1287 | orderDirection: $orderDirection 1288 | where: $where 1289 | block: $block 1290 | ) { 1291 | id 1292 | previousOwner 1293 | newOwner 1294 | blockNumber 1295 | blockTimestamp 1296 | transactionHash 1297 | } 1298 | } 1299 | `; 1300 | export const UpgradedDocument = ` 1301 | query upgraded($id: ID!, $block: Block_height) { 1302 | upgraded(id: $id, block: $block) { 1303 | id 1304 | implementation 1305 | blockNumber 1306 | blockTimestamp 1307 | transactionHash 1308 | } 1309 | } 1310 | `; 1311 | export const UpgradedsDocument = ` 1312 | query upgradeds($skip: Int, $first: Int, $orderBy: Upgraded_orderBy, $orderDirection: OrderDirection, $where: Upgraded_filter, $block: Block_height) { 1313 | upgradeds( 1314 | skip: $skip 1315 | first: $first 1316 | orderBy: $orderBy 1317 | orderDirection: $orderDirection 1318 | where: $where 1319 | block: $block 1320 | ) { 1321 | id 1322 | implementation 1323 | blockNumber 1324 | blockTimestamp 1325 | transactionHash 1326 | } 1327 | } 1328 | `; 1329 | 1330 | const injectedRtkApi = subgraphAPI.injectEndpoints({ 1331 | endpoints: (build) => ({ 1332 | account: build.query({ 1333 | query: ({variables, chainId = 1337}) => ({ document: AccountDocument, variables, chainId }) 1334 | }), 1335 | accounts: build.query({ 1336 | query: ({variables, chainId = 1337}) => ({ document: AccountsDocument, variables, chainId }) 1337 | }), 1338 | number: build.query({ 1339 | query: ({variables, chainId = 1337}) => ({ document: NumberDocument, variables, chainId }) 1340 | }), 1341 | numbers: build.query({ 1342 | query: ({variables, chainId = 1337}) => ({ document: NumbersDocument, variables, chainId }) 1343 | }), 1344 | numberSet: build.query({ 1345 | query: ({variables, chainId = 1337}) => ({ document: NumberSetDocument, variables, chainId }) 1346 | }), 1347 | numberSets: build.query({ 1348 | query: ({variables, chainId = 1337}) => ({ document: NumberSetsDocument, variables, chainId }) 1349 | }), 1350 | adminChanged: build.query({ 1351 | query: ({variables, chainId = 1337}) => ({ document: AdminChangedDocument, variables, chainId }) 1352 | }), 1353 | adminChangeds: build.query({ 1354 | query: ({variables, chainId = 1337}) => ({ document: AdminChangedsDocument, variables, chainId }) 1355 | }), 1356 | beaconUpgraded: build.query({ 1357 | query: ({variables, chainId = 1337}) => ({ document: BeaconUpgradedDocument, variables, chainId }) 1358 | }), 1359 | beaconUpgradeds: build.query({ 1360 | query: ({variables, chainId = 1337}) => ({ document: BeaconUpgradedsDocument, variables, chainId }) 1361 | }), 1362 | initialized: build.query({ 1363 | query: ({variables, chainId = 1337}) => ({ document: InitializedDocument, variables, chainId }) 1364 | }), 1365 | initializeds: build.query({ 1366 | query: ({variables, chainId = 1337}) => ({ document: InitializedsDocument, variables, chainId }) 1367 | }), 1368 | ownershipTransferred: build.query({ 1369 | query: ({variables, chainId = 1337}) => ({ document: OwnershipTransferredDocument, variables, chainId }) 1370 | }), 1371 | ownershipTransferreds: build.query({ 1372 | query: ({variables, chainId = 1337}) => ({ document: OwnershipTransferredsDocument, variables, chainId }) 1373 | }), 1374 | upgraded: build.query({ 1375 | query: ({variables, chainId = 1337}) => ({ document: UpgradedDocument, variables, chainId }) 1376 | }), 1377 | upgradeds: build.query({ 1378 | query: ({variables, chainId = 1337}) => ({ document: UpgradedsDocument, variables, chainId }) 1379 | }), 1380 | }), 1381 | }); 1382 | 1383 | export { injectedRtkApi as api }; 1384 | export const { useAccountQuery, useLazyAccountQuery, useAccountsQuery, useLazyAccountsQuery, useNumberQuery, useLazyNumberQuery, useNumbersQuery, useLazyNumbersQuery, useNumberSetQuery, useLazyNumberSetQuery, useNumberSetsQuery, useLazyNumberSetsQuery, useAdminChangedQuery, useLazyAdminChangedQuery, useAdminChangedsQuery, useLazyAdminChangedsQuery, useBeaconUpgradedQuery, useLazyBeaconUpgradedQuery, useBeaconUpgradedsQuery, useLazyBeaconUpgradedsQuery, useInitializedQuery, useLazyInitializedQuery, useInitializedsQuery, useLazyInitializedsQuery, useOwnershipTransferredQuery, useLazyOwnershipTransferredQuery, useOwnershipTransferredsQuery, useLazyOwnershipTransferredsQuery, useUpgradedQuery, useLazyUpgradedQuery, useUpgradedsQuery, useLazyUpgradedsQuery } = injectedRtkApi; 1385 | 1386 | -------------------------------------------------------------------------------- /packages/web-kit/src/store.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { configureStore } from "@reduxjs/toolkit"; 3 | import { subgraphAPI } from "./subgraph"; 4 | import { Provider } from "react-redux"; 5 | export const store = configureStore({ 6 | reducer: { 7 | // Add the generated reducer as a specific top-level slice 8 | [subgraphAPI.reducerPath]: subgraphAPI.reducer, 9 | }, 10 | // Adding the api middleware enables caching, invalidation, polling, 11 | // and other useful features of `rtk-query`. 12 | middleware: (getDefaultMiddleware) => 13 | getDefaultMiddleware().concat(subgraphAPI.middleware), 14 | }); 15 | 16 | type AppProviderProps = { 17 | children: React.ReactNode; 18 | }; 19 | 20 | export const AppProvider = ({ children }: AppProviderProps) => { 21 | return {children}; 22 | }; 23 | 24 | // optional, but required for refetchOnFocus/refetchOnReconnect behaviors 25 | // see `setupListeners` docs - takes an optional callback as the 2nd arg for customization 26 | // setupListeners(store.dispatch) 27 | -------------------------------------------------------------------------------- /packages/web-kit/src/subgraph.ts: -------------------------------------------------------------------------------- 1 | import { createApi } from "@reduxjs/toolkit/query/react"; 2 | import request, { ClientError } from "graphql-request"; 3 | 4 | const subgraphUrls: Record = { 5 | 1337: "http://localhost:8000/subgraphs/name/local-graph", 6 | 80001: "https://api.thegraph.com/subgraphs/name/nezz0746/starter-counter-mumbai", 7 | 11155111: "https://api.thegraph.com/subgraphs/name/nezz0746/starter-counter-sepolia", 8 | }; 9 | 10 | type SubgraphGraphQLBaseQueryParams = { 11 | document: string; 12 | variables: Record; 13 | chainId: number; 14 | }; 15 | 16 | export const subgraphQuery = () => async ({ 17 | document, 18 | variables, 19 | chainId, 20 | }: SubgraphGraphQLBaseQueryParams): Promise<{ data: T } | { error: any }> => { 21 | try { 22 | const baseUrl = subgraphUrls[chainId]; 23 | 24 | const result = await request(baseUrl, document, variables); 25 | 26 | return { data: result } as { data: T }; 27 | } catch (error) { 28 | if (error instanceof ClientError) { 29 | return { error: { status: error.response.status, data: error } }; 30 | } 31 | return { error: { status: 500, data: error } }; 32 | } 33 | }; 34 | 35 | export const subgraphAPI = createApi({ 36 | reducerPath: "subgraphAPI", 37 | baseQuery: subgraphQuery(), 38 | endpoints: () => ({}), 39 | }); 40 | -------------------------------------------------------------------------------- /packages/web-kit/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES6", 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "node", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "rootDir": ".", 16 | "incremental": true 17 | }, 18 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 19 | "exclude": ["node_modules"] 20 | } 21 | -------------------------------------------------------------------------------- /packages/web-ui/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:@typescript-eslint/recommended', 7 | 'plugin:react-hooks/recommended', 8 | ], 9 | ignorePatterns: ['dist', '.eslintrc.cjs'], 10 | parser: '@typescript-eslint/parser', 11 | plugins: ['react-refresh'], 12 | rules: { 13 | 'react-refresh/only-export-components': [ 14 | 'warn', 15 | { allowConstantExport: true }, 16 | ], 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /packages/web-ui/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /packages/web-ui/README.md: -------------------------------------------------------------------------------- 1 | # React + TypeScript + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | 10 | ## Expanding the ESLint configuration 11 | 12 | If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: 13 | 14 | - Configure the top-level `parserOptions` property like this: 15 | 16 | ```js 17 | export default { 18 | // other rules... 19 | parserOptions: { 20 | ecmaVersion: 'latest', 21 | sourceType: 'module', 22 | project: ['./tsconfig.json', './tsconfig.node.json'], 23 | tsconfigRootDir: __dirname, 24 | }, 25 | } 26 | ``` 27 | 28 | - Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` 29 | - Optionally add `plugin:@typescript-eslint/stylistic-type-checked` 30 | - Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list 31 | -------------------------------------------------------------------------------- /packages/web-ui/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/web-ui/lib/components/Button.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | 3 | type ButtonVariant = "base" | "primary" | "secondary"; 4 | 5 | type ButtonSize = "xs" | "sm" | "md" | "lg" | "xl"; 6 | 7 | export function Button({ 8 | className, 9 | loading = false, 10 | variant = "base", 11 | disabled = false, 12 | size = "md", 13 | children, 14 | ...rest 15 | }: React.ButtonHTMLAttributes & { 16 | loading?: boolean; 17 | variant?: ButtonVariant; 18 | size?: ButtonSize; 19 | }) { 20 | return ( 21 | 42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /packages/web-ui/lib/components/Card.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | 3 | export function Card({ 4 | className, 5 | ...rest 6 | }: React.HTMLAttributes) { 7 | return ( 8 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /packages/web-ui/lib/components/Table.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | 3 | type TableDataProps = { 4 | pending?: boolean; 5 | id?: string; 6 | } & Record; 7 | 8 | export function Table>({ 9 | children, 10 | className, 11 | head, 12 | data, 13 | ...rest 14 | }: React.HTMLAttributes & { 15 | head: { 16 | label: string; 17 | key?: keyof T; 18 | render?: (row: T) => React.ReactNode; 19 | }[]; 20 | data: T[]; 21 | }) { 22 | return ( 23 | 24 | 25 | 26 | {head.map((head) => ( 27 | 28 | ))} 29 | 30 | 31 | 32 | {data.map((row) => { 33 | const rowKey = String(row.id); 34 | 35 | return ( 36 | 42 | {head.map(({ key, label, render }, _i) => { 43 | const cellKey = label + "_" + rowKey; 44 | 45 | return ( 46 | 49 | ); 50 | })} 51 | 52 | ); 53 | })} 54 | 55 |
{head.label}
47 | {render ? render(row) : ((row as unknown) as any)[key]} 48 |
56 | ); 57 | } 58 | -------------------------------------------------------------------------------- /packages/web-ui/lib/components/WalletButton.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import { Web3Account, Web3AccountProps } from "./Web3Account"; 3 | 4 | type WalletButtonProps = Web3AccountProps; 5 | 6 | export function WalletButton({ 7 | className, 8 | address, 9 | ...rest 10 | }: React.ButtonHTMLAttributes & WalletButtonProps) { 11 | return ( 12 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /packages/web-ui/lib/components/Web3Account.tsx: -------------------------------------------------------------------------------- 1 | import * as blockies from "blockies-ts"; 2 | 3 | export type Web3AccountProps = { 4 | address: string; 5 | }; 6 | 7 | function truncateAddress(address: string) { 8 | return `${address.slice(0, 6)}...${address.slice(-4)}`; 9 | } 10 | 11 | export function Web3Account({ 12 | address, 13 | }: React.HTMLAttributes & Web3AccountProps) { 14 | return ( 15 |
16 | 19 | {truncateAddress(address)} 20 |
21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /packages/web-ui/lib/main.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /packages/web-ui/lib/main.ts: -------------------------------------------------------------------------------- 1 | import "./main.css"; 2 | 3 | export * from "./components/Button"; 4 | export * from "./components/Card"; 5 | export * from "./components/WalletButton"; 6 | export * from "./components/Table"; 7 | export * from "./components/Web3Account"; 8 | -------------------------------------------------------------------------------- /packages/web-ui/lib/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /packages/web-ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web-ui", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "main": "dist/main.js", 7 | "types": "dist/main.d.ts", 8 | "scripts": { 9 | "demo": "vite", 10 | "watch": "tsc --p ./tsconfig.build.json && vite build -w", 11 | "build": "tsc --p ./tsconfig.build.json && vite build", 12 | "dev": "concurrently \"npm:watch\" \"npm:demo\"", 13 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 14 | "preview": "vite preview" 15 | }, 16 | "dependencies": { 17 | "blockies-ts": "^1.0.0", 18 | "daisyui": "4.4.19", 19 | "react": "^18.2.0", 20 | "react-dom": "^18.2.0" 21 | }, 22 | "devDependencies": { 23 | "@types/react": "^18.2.37", 24 | "@types/react-dom": "^18.2.15", 25 | "@typescript-eslint/eslint-plugin": "^6.10.0", 26 | "@typescript-eslint/parser": "^6.10.0", 27 | "@vitejs/plugin-react": "^4.2.0", 28 | "autoprefixer": "10.4.16", 29 | "classnames": "^2.3.2", 30 | "concurrently": "^8.2.2", 31 | "eslint": "^8.53.0", 32 | "eslint-plugin-react-hooks": "^4.6.0", 33 | "eslint-plugin-react-refresh": "^0.4.4", 34 | "postcss": "^8.4.32", 35 | "shared-config": "*", 36 | "tailwindcss": "3.3.6", 37 | "typescript": "^5.2.2", 38 | "vite": "^5.0.0", 39 | "vite-plugin-dts": "^3.6.4", 40 | "vite-plugin-lib-inject-css": "^1.3.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/web-ui/postcss.config.js: -------------------------------------------------------------------------------- 1 | import config from "shared-config/tailwind/postcss.config.js" 2 | 3 | export default config -------------------------------------------------------------------------------- /packages/web-ui/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/web-ui/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { WalletButton } from "../lib/components/WalletButton"; 2 | import { Button } from "../lib/main"; 3 | 4 | function App() { 5 | return ( 6 |
7 |
8 | 9 | 12 | 15 | 18 | 21 |
22 |
23 | 24 | 25 | 26 | 27 | 28 |
29 |
30 | 31 |
32 |
33 | ); 34 | } 35 | 36 | export default App; 37 | -------------------------------------------------------------------------------- /packages/web-ui/src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/web-ui/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./App.tsx"; 4 | 5 | ReactDOM.createRoot(document.getElementById("root")!).render( 6 | 7 | 8 | 9 | ); 10 | -------------------------------------------------------------------------------- /packages/web-ui/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /packages/web-ui/tailwind.config.js: -------------------------------------------------------------------------------- 1 | import config from "shared-config/tailwind/tailwind.config.js" 2 | 3 | export default config -------------------------------------------------------------------------------- /packages/web-ui/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "paths": { 6 | "src/": ["src/*"], 7 | } 8 | }, 9 | "include": ["lib"] 10 | } -------------------------------------------------------------------------------- /packages/web-ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true, 22 | 23 | 24 | }, 25 | "include": ["src", "lib"], 26 | "references": [{ "path": "./tsconfig.node.json" }] 27 | } 28 | -------------------------------------------------------------------------------- /packages/web-ui/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /packages/web-ui/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import { resolve } from "path"; 3 | import react from "@vitejs/plugin-react"; 4 | import dts from "vite-plugin-dts"; 5 | import { libInjectCss } from "vite-plugin-lib-inject-css"; 6 | 7 | // https://vitejs.dev/config/ 8 | export default defineConfig({ 9 | plugins: [ 10 | react(), 11 | libInjectCss(), 12 | dts({ 13 | include: ["lib"], 14 | }), 15 | ], 16 | build: { 17 | copyPublicDir: false, 18 | lib: { 19 | entry: resolve(__dirname, "lib/main.ts"), 20 | formats: ["es"], 21 | fileName: "main", 22 | }, 23 | rollupOptions: { 24 | external: ["react", "react/jsx-runtime"], 25 | }, 26 | }, 27 | }); 28 | -------------------------------------------------------------------------------- /patches/@graphql-codegen+typescript-rtk-query+3.1.0.patch: -------------------------------------------------------------------------------- 1 | diff --git a/node_modules/@graphql-codegen/typescript-rtk-query/cjs/visitor.js b/node_modules/@graphql-codegen/typescript-rtk-query/cjs/visitor.js 2 | index 980d3c9..af25e63 100644 3 | --- a/node_modules/@graphql-codegen/typescript-rtk-query/cjs/visitor.js 4 | +++ b/node_modules/@graphql-codegen/typescript-rtk-query/cjs/visitor.js 5 | @@ -14,6 +14,7 @@ class RTKQueryVisitor extends visitor_plugin_common_1.ClientSideBaseVisitor { 6 | addTransformResponse: (0, visitor_plugin_common_1.getConfigValue)(rawConfig.addTransformResponse, false), 7 | exportHooks: (0, visitor_plugin_common_1.getConfigValue)(rawConfig.exportHooks, false), 8 | overrideExisting: (0, visitor_plugin_common_1.getConfigValue)(rawConfig.overrideExisting, ''), 9 | + defaultChainId: (0, visitor_plugin_common_1.getConfigValue)(rawConfig.defaultChainId, 1), 10 | }); 11 | this.rawConfig = rawConfig; 12 | this._endpoints = []; 13 | @@ -79,9 +80,9 @@ export { injectedRtkApi as api }; 14 | console.warn(`Plugin "typescript-rtk-query" does not support GraphQL Subscriptions at the moment! Skipping "${(_b = node.name) === null || _b === void 0 ? void 0 : _b.value}"...`); 15 | return ''; 16 | } 17 | - const Generics = `${operationResultType}, ${operationVariablesTypes}${hasRequiredVariables ? '' : ' | void'}`; 18 | + const Generics = `${operationResultType}, {variables: ${operationVariablesTypes}; chainId?: number;}`; 19 | const operationTypeString = operationType.toLowerCase(); 20 | - const functionsString = `query: (variables) => ({ document: ${documentVariableName}, variables }) 21 | + const functionsString = `query: ({variables, chainId = ${this.rawConfig.defaultChainId}}) => ({ document: ${documentVariableName}, variables, chainId }) 22 | ${this.injectTransformResponse(Generics)}`.trim(); 23 | const endpointString = ` 24 | ${operationName}: build.${operationTypeString}<${Generics}>({ 25 | diff --git a/node_modules/@graphql-codegen/typescript-rtk-query/typings/config.d.cts b/node_modules/@graphql-codegen/typescript-rtk-query/typings/config.d.cts 26 | index 9b605df..2d241fe 100644 27 | --- a/node_modules/@graphql-codegen/typescript-rtk-query/typings/config.d.cts 28 | +++ b/node_modules/@graphql-codegen/typescript-rtk-query/typings/config.d.cts 29 | @@ -102,6 +102,7 @@ export interface RTKConfig { 30 | * ``` 31 | */ 32 | overrideExisting?: string; 33 | + defaultChainId?: string; 34 | /** 35 | * @name addTransformResponse 36 | * @description Sets the `addTransformResponse` option, which will automatically add a types transformResponse for query 37 | diff --git a/node_modules/@graphql-codegen/typescript-rtk-query/typings/config.d.ts b/node_modules/@graphql-codegen/typescript-rtk-query/typings/config.d.ts 38 | index 9b605df..2d241fe 100644 39 | --- a/node_modules/@graphql-codegen/typescript-rtk-query/typings/config.d.ts 40 | +++ b/node_modules/@graphql-codegen/typescript-rtk-query/typings/config.d.ts 41 | @@ -102,6 +102,7 @@ export interface RTKConfig { 42 | * ``` 43 | */ 44 | overrideExisting?: string; 45 | + defaultChainId?: string; 46 | /** 47 | * @name addTransformResponse 48 | * @description Sets the `addTransformResponse` option, which will automatically add a types transformResponse for query 49 | -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turborepo.org/schema.json", 3 | "pipeline": { 4 | "build": { 5 | "dependsOn": ["^build"], 6 | "outputs": ["dist/**", ".next/**", "artifacts/**", "cache/**"] 7 | }, 8 | "test": { 9 | "dependsOn": ["^build"] 10 | }, 11 | "lint": {}, 12 | "dev": { 13 | "cache": false 14 | } 15 | } 16 | } 17 | --------------------------------------------------------------------------------