├── .env.example ├── .eslintrc.cjs ├── .gitignore ├── .prettierrc ├── README.md ├── package.json ├── pnpm-lock.yaml ├── postcss.config.cjs ├── src ├── app.css ├── app.html ├── global.d.ts ├── hooks.ts ├── lib │ ├── components │ │ ├── AddFacet.svelte │ │ ├── Bookmark.svelte │ │ ├── Comments.svelte │ │ ├── ConnectToOrbis.svelte │ │ ├── FacetCard.svelte │ │ ├── Featured.svelte │ │ ├── Header.svelte │ │ ├── History.svelte │ │ ├── LatestDiamonds.svelte │ │ ├── Loading.svelte │ │ ├── ProfileDropdown.svelte │ │ ├── ReadContract.svelte │ │ ├── RemoveFacet.svelte │ │ ├── Search.svelte │ │ ├── Sponsor.svelte │ │ ├── Stats.svelte │ │ ├── TopDiamonds.svelte │ │ ├── TransactionNotification.svelte │ │ ├── Transactions.svelte │ │ └── WriteContract.svelte │ ├── config.ts │ ├── services │ │ ├── contractReader.ts │ │ └── diamond.ts │ ├── stores │ │ ├── navigationState.ts │ │ ├── orbis.ts │ │ └── user.ts │ └── utils.ts ├── routes │ ├── +error.svelte │ ├── +layout.svelte │ ├── +page.svelte │ ├── api │ │ ├── contract │ │ │ └── +server.ts │ │ ├── events │ │ │ └── +server.ts │ │ ├── facets │ │ │ └── +server.ts │ │ ├── leaderboard │ │ │ └── +server.ts │ │ ├── readContract │ │ │ └── +server.ts │ │ ├── stats │ │ │ └── +server.ts │ │ └── transactions │ │ │ └── +server.ts │ ├── bookmarks │ │ ├── +page.js │ │ └── +page.svelte │ ├── diamond │ │ └── [address] │ │ │ ├── +page.svelte │ │ │ └── +page.ts │ └── sponsor │ │ └── +page.svelte └── types │ └── entities.ts ├── static ├── favicon.ico ├── img │ ├── aavegotchi-mainnet-logo.png │ ├── aavegotchi-polygon-logo.jpg │ ├── barnbridge-logo.jpg │ ├── beanstalk-logo.png │ ├── connext-logo.png │ ├── escabro-logo.png │ ├── gelato-logo.png │ ├── lifi.png │ ├── mark3labslogo.png │ ├── piedao-logo.png │ └── quicknode-logo.svg ├── louper-logo-transparent.png ├── louper-logo.png ├── louper-logo.svg ├── manifest.json └── thumbnail.png ├── supabase ├── config.toml └── migrations │ ├── 20220601172756_add_contracts_table.sql │ └── 20220805092229_add_leaderboard.sql ├── svelte.config.js ├── tailwind.config.cjs ├── tsconfig.json └── vite.config.ts /.env.example: -------------------------------------------------------------------------------- 1 | BINANCE_ETHERSCAN_API_KEY= 2 | POLYGON_ETHERSCAN_API_KEY= 3 | ETHERSCAN_API_KEY= 4 | FUJI_ETHERSCAN_API_KEY= 5 | SUPABASE_URL=http://localhost:54321 6 | SUPABASE_KEY= 7 | INFURA_API_KEY= -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: '@typescript-eslint/parser', 4 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'], 5 | plugins: ['svelte3', '@typescript-eslint'], 6 | ignorePatterns: ['*.cjs'], 7 | overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }], 8 | settings: { 9 | 'svelte3/typescript': () => require('typescript'), 10 | }, 11 | parserOptions: { 12 | sourceType: 'module', 13 | ecmaVersion: 2019, 14 | }, 15 | env: { 16 | browser: true, 17 | es2017: true, 18 | node: true, 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .vercel_build_output 8 | # Supabase 9 | **/supabase/.branches 10 | **/supabase/.temp 11 | .vercel 12 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": false, 3 | "singleQuote": true, 4 | "trailingComma": "all", 5 | "printWidth": 100, 6 | "semi": false 7 | } 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Louper - The Ethereum Diamond Inspector 2 | 3 | A simple UI for viewing details about EVM smart contracts using EIP-2535 (Diamond Standard) 4 | 5 | **Features:** 6 | 7 | - View diamond details include all facets and their methods 8 | - Read from facet methods 9 | - Write to facet methods 10 | - **MIT License** completely open source to do with as you please 11 | 12 | ## Development 13 | 14 | ### Prerequisites 15 | 16 | You will need a Subabase local db instance. Follow the [instructions](https://supabase.com/docs/guides/local-development) to get set up. 17 | 18 | Run: 19 | 20 | ```sh 21 | supabase init 22 | supabase start 23 | supabase db reset 24 | ``` 25 | 26 | You should now have a working DB. 27 | 28 | Run: 29 | 30 | ```sh 31 | cp .env.example .env 32 | supabase status 33 | ``` 34 | 35 | Copy the `service_role key` and paste that as the value of `SUPABASE_KEY` in `.env` 36 | 37 | Ensure you have [pnpm](https://pnpm.io/installation) installed. 38 | 39 | Run: 40 | 41 | ```sh 42 | pnpm install 43 | pnpm run dev 44 | ``` 45 | 46 | You should now have a working dev environment. 47 | 48 | ## License 49 | 50 | MIT License 51 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "louper-v2", 3 | "version": "0.0.1", 4 | "scripts": { 5 | "dev": "vite dev", 6 | "build": "vite build", 7 | "preview": "vite preview", 8 | "check": "svelte-check --tsconfig ./tsconfig.json", 9 | "check:watch": "svelte-check --tsconfig ./tsconfig.json --watch", 10 | "lint": "prettier --ignore-path .gitignore --check --plugin-search-dir=. . && eslint --ignore-path .gitignore .", 11 | "format": "prettier --ignore-path .gitignore --write --plugin-search-dir=. .", 12 | "postinstall": "svelte-kit sync" 13 | }, 14 | "devDependencies": { 15 | "@esbuild-plugins/node-globals-polyfill": "^0.2.3", 16 | "@esbuild-plugins/node-modules-polyfill": "^0.2.2", 17 | "@sveltejs/kit": "1.24.1", 18 | "@types/node": "^20.6.0", 19 | "@typescript-eslint/eslint-plugin": "^6.6.0", 20 | "@typescript-eslint/parser": "^6.6.0", 21 | "autoprefixer": "^10.4.15", 22 | "buffer": "^6.0.3", 23 | "cssnano": "^6.0.1", 24 | "esbuild": "^0.19.2", 25 | "eslint": "^8.49.0", 26 | "eslint-config-prettier": "^9.0.0", 27 | "postcss": "^8.4.29", 28 | "postcss-load-config": "^4.0.1", 29 | "prettier": "^3.0.3", 30 | "prettier-plugin-svelte": "^3.0.3", 31 | "rollup": "^3.29.1", 32 | "rollup-plugin-node-polyfills": "^0.2.1", 33 | "rollup-plugin-polyfill-node": "^0.12.0", 34 | "svelte": "^4.2.0", 35 | "svelte-check": "^3.5.1", 36 | "svelte-preprocess": "^5.0.4", 37 | "tailwindcss": "^3.3.3", 38 | "tslib": "^2.6.2", 39 | "typescript": "^5.2.2", 40 | "vite": "^4.4.9", 41 | "vite-plugin-node-polyfills": "^0.12.0" 42 | }, 43 | "type": "module", 44 | "dependencies": { 45 | "@ceramicnetwork/stream-tile": "^2.29.0", 46 | "@ethersproject/abi": "^5.7.0", 47 | "@ethersproject/bignumber": "^5.7.0", 48 | "@ethersproject/contracts": "^5.7.0", 49 | "@ethersproject/providers": "^5.7.2", 50 | "@opentelemetry/api": "^1.4.1", 51 | "@orbisclub/orbis-sdk": "^0.4.87", 52 | "@supabase/supabase-js": "^2.33.1", 53 | "@sveltejs/adapter-vercel": "3.0.3", 54 | "@tailwindcss/typography": "^0.5.10", 55 | "axios": "^1.5.0", 56 | "daisyui": "^3.7.3", 57 | "dotenv": "^16.3.1", 58 | "eslint-plugin-svelte": "^2.33.1", 59 | "ethers": "^6.7.1", 60 | "lodash": "^4.17.21", 61 | "redaxios": "^0.5.1", 62 | "svelte-notifications": "^0.9.98", 63 | "svelte-select": "^5.7.0", 64 | "svelte-tags-input": "^5.0.0", 65 | "web3w": "0.3.2-beta.22", 66 | "web3w-walletconnect-loader": "^0.4.3" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /postcss.config.cjs: -------------------------------------------------------------------------------- 1 | const tailwindcss = require('tailwindcss') 2 | const autoprefixer = require('autoprefixer') 3 | const cssnano = require('cssnano') 4 | 5 | const mode = process.env.NODE_ENV 6 | const dev = mode === 'development' 7 | 8 | const config = { 9 | plugins: [ 10 | //Some plugins, like tailwindcss/nesting, need to run before Tailwind, 11 | tailwindcss(), 12 | //But others, like autoprefixer, need to run after, 13 | autoprefixer(), 14 | !dev && 15 | cssnano({ 16 | preset: 'default', 17 | }), 18 | ], 19 | } 20 | 21 | module.exports = config 22 | -------------------------------------------------------------------------------- /src/app.css: -------------------------------------------------------------------------------- 1 | /* Write your global styles here, in PostCSS syntax */ 2 | @tailwind base; 3 | @tailwind components; 4 | @tailwind utilities; 5 | 6 | .svelte-tags-input-layout { 7 | @apply border-2 border-primary rounded m-2 bg-base-100 !important; 8 | } 9 | 10 | .svelte-tags-input { 11 | @apply bg-base-100; 12 | } 13 | 14 | .svelte-tags-input-tag { 15 | @apply badge-primary !important; 16 | } -------------------------------------------------------------------------------- /src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | Louper - The Ethereum Diamond Inspector 12 | 13 | 14 | 18 | 19 | 20 | 21 | 22 | 23 | 32 | %sveltekit.head% 33 | 34 | 35 |
%sveltekit.body%
36 | 37 | 38 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/global.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/hooks.ts: -------------------------------------------------------------------------------- 1 | import type { Handle } from "@sveltejs/kit"; 2 | 3 | export const handle: Handle = async ({ resolve, event }) => { 4 | const response = await resolve(event); 5 | 6 | // Apply CORS header for API routes 7 | if (event.url.pathname.startsWith('/api')) { 8 | // Required for CORS to work 9 | if (event.request.method === 'OPTIONS') { 10 | return new Response(null, { 11 | headers: { 12 | 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS, DELETE', 13 | 'Access-Control-Allow-Origin': '*', 14 | } 15 | }); 16 | } 17 | 18 | response.headers.append('Access-Control-Allow-Origin', `*`); 19 | } 20 | 21 | return response; 22 | } 23 | -------------------------------------------------------------------------------- /src/lib/components/AddFacet.svelte: -------------------------------------------------------------------------------- 1 | 148 | 149 | {#if showModal} 150 |
151 |
152 |
153 |

Add New Facet

154 | 155 | {#if fetchFacetError} 156 |
157 |
158 | 🛑 159 | 162 |
163 |
164 | {/if} 165 |
166 | {#if $wallet.state !== 'Ready'} 167 |
168 | {#if $wallet.state !== 'Ready'} 169 | {#if $builtin.available} 170 | 173 | {/if} 174 | 177 | {/if} 178 |
179 | {:else} 180 | {#if !facet} 181 |
182 |
183 | 186 | 191 |
192 | 195 |
196 | {/if} 197 | 198 | {#if facet} 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | {#each methods as method} 208 | 209 | 210 | 211 | 214 | 215 | {/each} 216 |
AddMethodSelector
{method.signature} 212 | {method.selector} 213 |
217 |
218 |
219 | 222 | 227 |
228 |
229 | 232 | 237 |
238 | 239 |
240 |

241 | {#if $wallet.pendingUserConfirmation} 242 | Please check and approve the transaction in your wallet. 243 | {/if} 244 | 245 | {#if $flow.inProgress} 246 |

247 | 248 |
249 | {/if} 250 | 251 | {#if error} 252 |
253 |

254 | {error.message} 255 |

256 |
257 | {/if} 258 |

259 |
260 | 261 |
262 | 287 |
288 | {/if} 289 | {/if} 290 |
291 |
292 | 293 |
294 |
295 | 296 |
297 |
298 | {/if} 299 | -------------------------------------------------------------------------------- /src/lib/components/Bookmark.svelte: -------------------------------------------------------------------------------- 1 | 53 | 54 | 70 | -------------------------------------------------------------------------------- /src/lib/components/Comments.svelte: -------------------------------------------------------------------------------- 1 | 56 | 57 |
58 | 59 |
60 | 67 | 73 | 74 | Comments 75 | {#if comments.length} 76 |
{comments.length}
77 | {/if} 78 |
79 |
80 | {#each comments as comment} 81 |
82 | {comment.creator_details.profile 83 | ? comment.creator_details.profile.username 84 | : shortProfile(comment.creator_details.metadata.address)} 85 |
86 |
87 | {comment.content.body} 88 |
89 | {/each} 90 | {#if $user} 91 |
92 |