restructure (#345)

* restructure

* udpate readme links
This commit is contained in:
Glitch
2023-12-13 15:36:25 -03:00
committed by GitHub
parent 599743deb1
commit deb123f283
573 changed files with 578 additions and 578 deletions
@@ -0,0 +1 @@
FROM node:21-bullseye
@@ -0,0 +1,31 @@
{
// These tasks will run in order when initializing your CodeSandbox project.
"setupTasks": [
{
"name": "Install Dependencies",
"command": "yarn install"
}
],
// These tasks can be run from CodeSandbox. Running one will open a log in the app.
"tasks": {
"dev": {
"name": "dev",
"command": "yarn dev",
"runAtStart": true,
"preview": {
"port": 5173
}
},
"build": {
"name": "build",
"command": "yarn build",
"runAtStart": false
},
"preview": {
"name": "preview",
"command": "yarn preview",
"runAtStart": false
}
}
}
+1
View File
@@ -0,0 +1 @@
VITE_PROJECT_ID=YOUR_PROJECT_ID
+24
View File
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
+22
View File
@@ -0,0 +1,22 @@
# Vite + React
This is a [Vite](https://vitejs.dev) project together with React.
[![Edit in CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/github/codesandbox/codesandbox-template-vite-react/main)
[Configuration](https://codesandbox.io/docs/projects/learn/setting-up/tasks) has been added to optimize it for [CodeSandbox](https://codesandbox.io/dashboard).
## Usage
1. Go to [WalletConnect Cloud](https://cloud.walletconnect.com) and create a new project.
2. Copy your `Project ID`
3. Rename `.env.example` to `.env` and paste your `Project ID` as the value for `VITE_PROJECT_ID`
4. Run `yarn` to install dependencies
5. Run `yarn dev` to start the development server
## Resources
- [CodeSandbox — Docs](https://codesandbox.io/docs/projects)
- [CodeSandbox — Discord](https://discord.gg/Ggarp3pX5H)
- [Vite — GitHub](https://github.com/vitejs/vite)
- [Vite — Docs](https://vitejs.dev/guide/)
+13
View File
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>WalletConnect Ethereum Provider</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
+25
View File
@@ -0,0 +1,25 @@
{
"name": "codesandbox-template-vite",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
},
"dependencies": {
"@walletconnect/ethereum-provider": "^2.9.1",
"@walletconnect/modal": "^2.6.1",
"ethers": "^5.7.1",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/react": "^18.0.15",
"@types/react-dom": "^18.0.6",
"@vitejs/plugin-react": "^2.0.0",
"typescript": "^4.6.4",
"vite": "^4.3.0"
}
}
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

+87
View File
@@ -0,0 +1,87 @@
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);
}
@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@media (prefers-reduced-motion: no-preference) {
a:nth-of-type(1) .logo {
animation: logo-spin infinite 20s linear;
}
}
.card {
padding: 2em;
}
.read-the-docs {
color: #888;
}
#root {
max-width: 100vw;
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
gap: 1rem;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);
}
@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@media (prefers-reduced-motion: no-preference) {
a:nth-of-type(1) .logo {
animation: logo-spin infinite 20s linear;
}
}
.card {
padding: 2em;
}
.read-the-docs {
color: #888;
}
+68
View File
@@ -0,0 +1,68 @@
import "./App.css";
import { EthereumProvider } from "@walletconnect/ethereum-provider";
import { ethers } from "ethers";
import React, { useState } from "react";
const projectId = import.meta.env.VITE_PROJECT_ID as string;
// 1. Create a new EthereumProvider instance
const provider = await EthereumProvider.init({
projectId,
chains: [1],
methods: ["personal_sign", "eth_sendTransaction"],
showQrModal: true,
qrModalOptions: {
themeMode: "light",
},
});
provider.on("display_uri", (uri) => {
console.log("display_uri", uri);
});
// 2. Pass the provider to ethers.js
const ethersWeb3Provider = new ethers.providers.Web3Provider(provider);
function App() {
// 3. Handle Connect
const connect = () => {
provider.connect().then(() => {
setConnected(true);
});
};
const [connected, setConnected] = useState(false);
const [balance, setBalance] = useState<string | null>(null);
// 4. Fetch Balance on click with ethers.js
const getBalance = async () => {
const balanceFromEthers = await ethersWeb3Provider
.getSigner(provider.accounts[0])
.getBalance();
const remainder = balanceFromEthers.mod(1e14);
setBalance(ethers.utils.formatEther(balanceFromEthers.sub(remainder)));
};
// 5. Handle Disconnect
const refresh = () => {
provider.disconnect();
window.localStorage.clear();
setConnected(false);
};
if (connected) {
return (
<>
<button onClick={getBalance}>Balance</button>
<button onClick={refresh}>Refresh</button>
<p>
balance: {balance ? `${balance} ETH` : `click "Balance" to fetch`}
</p>
</>
);
}
return <button onClick={connect}>Connect with ethereum-provider</button>;
}
export default App;
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

+70
View File
@@ -0,0 +1,70 @@
:root {
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
font-size: 16px;
line-height: 24px;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}
+10
View File
@@ -0,0 +1,10 @@
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import "./index.css";
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
<React.StrictMode>
<App />
</React.StrictMode>
);
+1
View File
@@ -0,0 +1 @@
/// <reference types="vite/client" />
+31
View File
@@ -0,0 +1,31 @@
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": [
"DOM",
"DOM.Iterable",
"ESNext"
],
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": [
"src"
],
"references": [
{
"path": "./tsconfig.node.json"
}
]
}
@@ -0,0 +1,11 @@
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true
},
"include": [
"vite.config.ts"
]
}
+7
View File
@@ -0,0 +1,7 @@
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()]
});
File diff suppressed because it is too large Load Diff
-1
View File
@@ -1 +0,0 @@
NEXT_PUBLIC_PROJECT_ID=...
-3
View File
@@ -1,3 +0,0 @@
{
"extends": "next/core-web-vitals"
}
-36
View File
@@ -1,36 +0,0 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
-39
View File
@@ -1,39 +0,0 @@
# React Auth dApp (React, Typescript, Next.js, Ethers)
🔗 Live dapp demo - https://react-auth-dapp.walletconnect.com/ <br />
🔗 Live wallet demo - https://react-auth-wallet.walletconnect.com/ <br />
📚 WalletConnect v2 Docs - https://docs.walletconnect.com/2.0
## Overview
This example aims to demonstrate dapp-facing use cases enabled by WalletConnect Auth Alpha.
## Running locally
Install the app's dependencies:
```bash
npm i
```
Set up your local environment variables by copying the example into your own `.env.local` file:
```bash
cp .env.local.example .env.local
```
Your `.env.local` now contains the following environment variables:
- `NEXT_PUBLIC_PROJECT_ID` (placeholder) - You can generate your own ProjectId at https://cloud.walletconnect.com
## Develop
```bash
npm run dev
```
## Build
```bash
npm run build
```
@@ -1,64 +0,0 @@
import { Flex, Switch } from "@chakra-ui/react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import moon from "../public/moon.svg";
import sun from "../public/sun.svg";
const ThemeSwitcher: React.FC = () => {
const [darkMode, setDarkMode] = useState(true);
const switchRef = useRef(null);
const darkTheme = useMemo(
() => ({
"--primary-bg": "#1e1e1e",
"--secondary-bg": "#272a2a",
"--qr-bg": "#141414",
"--text-color": "white",
"--wc-btn-bg": "#19324d",
"--wc-btn-brdr": "#0f4b8a",
"--wc-btn-clr": "#66b1ff",
}),
[]
);
const lightTheme = useMemo(
() => ({
"--primary-bg": "#F1F3F3",
"--secondary-bg": "white",
"--qr-bg": "#c8d0d0",
"--text-color": "black",
"--wc-btn-bg": "#E8F2FC",
"--wc-btn-brdr": "#CDE5FE",
"--wc-btn-clr": "#3396FF",
}),
[]
);
const setTheme = useCallback((theme: Record<string, string>) => {
Object.entries(theme).forEach(([key, value]) => {
document.documentElement.style.setProperty(key, value);
});
}, []);
useEffect(() => {
if (darkMode) {
setTheme(darkTheme);
} else {
setTheme(lightTheme);
}
}, [darkMode, setTheme, darkTheme, lightTheme]);
return (
<Switch
size="lg"
ref={switchRef}
isChecked={darkMode}
colorScheme="blackAlpha"
className={`theme-switcher theme-switcher-${darkMode ? "dark" : "light"}`}
onChange={({ target }) => {
setDarkMode(target.checked);
}}
/>
);
};
export default ThemeSwitcher;
-7
View File
@@ -1,7 +0,0 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
}
module.exports = nextConfig
File diff suppressed because it is too large Load Diff
-35
View File
@@ -1,35 +0,0 @@
{
"name": "auth-sample-app",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@chakra-ui/react": "^2.2.6",
"@emotion/react": "^11.10.0",
"@emotion/styled": "^11.10.0",
"@walletconnect/auth-client": "2.1.1",
"@web3modal/standalone": "^2.2.2",
"better-sqlite3": "^7.6.2",
"ethers": "^5.7.0",
"events": "^3.3.0",
"framer-motion": "^7.0.0",
"next": "12.2.4",
"react": "18.2.0",
"react-dom": "18.2.0",
"smart-truncate": "^1.0.1"
},
"devDependencies": {
"@types/node": "18.6.4",
"@types/react": "18.0.15",
"@types/react-dom": "18.0.6",
"@types/smart-truncate": "^1.0.2",
"eslint": "8.21.0",
"eslint-config-next": "12.2.4",
"typescript": "^4.9.5"
}
}
-41
View File
@@ -1,41 +0,0 @@
import { Box, Heading, Text, Flex, Button } from "@chakra-ui/react";
import Link from "next/link";
const Page404 = () => {
return (
<Flex minHeight="100vh" direction="column" justifyContent="center">
<Box marginY={4}>
<Heading textAlign="center" size="lg">
Page not Found.
</Heading>
<Flex
flexDirection="column"
justifyContent="center"
textAlign="center"
marginTop={4}
gap={2}
>
<Text fontSize="sm" color="gray">
It&apos;s Okay!
</Text>
<Link href="/">
<Button
padding={2}
backgroundColor="#3396FF"
rounded="xl"
_hover={{
border: "solid 1px #3396FF",
backgroundColor: "black",
}}
>
Let&apos;s Head Back
</Button>
</Link>
</Flex>
</Box>
</Flex>
);
};
export default Page404;
-73
View File
@@ -1,73 +0,0 @@
import "../styles/globals.css";
import type { AppProps } from "next/app";
import { version } from "@walletconnect/auth-client/package.json";
import {
ChakraProvider,
Box,
Flex,
Grid,
GridItem,
Image,
} from "@chakra-ui/react";
import ThemeSwitcher from "../components/ThemeSwitcher";
function MyApp({ Component, pageProps }: AppProps) {
return (
<ChakraProvider>
<Box
width="100vw"
style={{ width: "100vw", height: "100vh" }}
className="bg-primary"
>
<Grid
templateAreas={`
"header"
"main"
"footer"
`}
style={{ height: "100%", width: "100%" }}
gridTemplateRows={"50px 3f 20px"}
gridTemplateColumns={"1fr"}
paddingY="2em"
>
<GridItem area={"header"}>
<Flex
alignItems="center"
justifyContent="center"
gap="5"
fontSize={"1.25em"}
>
<div>Example App</div>
<Flex
padding="0.5em"
borderRadius="16px"
className="bg-secondary"
gap="2"
>
<Image
width="1.5em"
height="1.5em"
src="/wc-bg.png"
alt="WC"
></Image>
<span>V{version}</span>
</Flex>
</Flex>
</GridItem>
<Flex justifyContent="center">
<GridItem area={"main"} justifyContent="center">
<Component {...pageProps} />
</GridItem>
</Flex>
<GridItem area={"footer"} alignSelf="flex-end">
<Flex justifyContent="flex-end">
<ThemeSwitcher />
</Flex>
</GridItem>
</Grid>
</Box>
</ChakraProvider>
);
}
export default MyApp;
-122
View File
@@ -1,122 +0,0 @@
import { Box, useToast } from "@chakra-ui/react";
import AuthClient, { generateNonce } from "@walletconnect/auth-client";
import { Web3Modal } from "@web3modal/standalone";
import type { NextPage } from "next";
import { useCallback, useEffect, useState } from "react";
import DefaultView from "../views/DefaultView";
import SignedInView from "../views/SignedInView";
// 1. Get projectID at https://cloud.walletconnect.com
const projectId = process.env.NEXT_PUBLIC_PROJECT_ID;
if (!projectId) {
throw new Error("You need to provide NEXT_PUBLIC_PROJECT_ID env variable");
}
// 2. Configure web3Modal
const web3Modal = new Web3Modal({
projectId,
walletConnectVersion: 2,
});
const Home: NextPage = () => {
const [client, setClient] = useState<AuthClient | null>();
const [hasInitialized, setHasInitialized] = useState(false);
const [uri, setUri] = useState<string>("");
const [address, setAddress] = useState<string>("");
const toast = useToast();
const onSignIn = useCallback(() => {
if (!client) return;
client
.request({
aud: window.location.href,
domain: window.location.hostname.split(".").slice(-2).join("."),
chainId: "eip155:1",
type: "eip4361",
nonce: generateNonce(),
statement: "Sign in with wallet.",
})
.then(({ uri }) => {
if (uri) {
setUri(uri);
}
});
}, [client, setUri]);
useEffect(() => {
AuthClient.init({
relayUrl:
process.env.NEXT_PUBLIC_RELAY_URL || "wss://relay.walletconnect.com",
projectId: process.env.NEXT_PUBLIC_PROJECT_ID!,
metadata: {
name: "react-dapp-auth",
description: "React Example Dapp for Auth",
url: window.location.host,
icons: [],
},
})
.then((authClient) => {
setClient(authClient);
setHasInitialized(true);
})
.catch(console.error);
}, []);
useEffect(() => {
if (!client) return;
client.on("auth_response", ({ params }) => {
if ("code" in params) {
console.error(params);
return web3Modal.closeModal();
}
if ("error" in params) {
console.error(params.error);
if ("message" in params.error) {
toast({
status: "error",
title: params.error.message,
});
}
return web3Modal.closeModal();
}
toast({
status: "success",
title: "Auth request successfully approved",
colorScheme: "whatsapp",
});
setAddress(params.result.p.iss.split(":")[4]);
});
}, [client, toast]);
const [view, changeView] = useState<"default" | "qr" | "signedIn">("default");
useEffect(() => {
async function handleOpenModal() {
if (uri) {
await web3Modal.openModal({
uri,
standaloneChains: ["eip155:1"],
});
}
}
handleOpenModal();
}, [uri]);
useEffect(() => {
if (address) {
web3Modal.closeModal();
changeView("signedIn");
}
}, [address, changeView]);
return (
<Box width="100%" height="100%">
{view === "default" && (
<DefaultView onClick={onSignIn} hasInitialized={hasInitialized} />
)}
{view === "signedIn" && <SignedInView address={address} />}
</Box>
);
};
export default Home;
Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

-3
View File
@@ -1,3 +0,0 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.00298 4.00529C4.01518 2.77994 4.07734 2.06898 4.39411 1.51362C4.6603 1.04694 5.04694 0.660299 5.51362 0.394105C6.20456 0 7.13638 0 9 0C10.8636 0 11.7954 0 12.4864 0.394105C12.9531 0.660299 13.3397 1.04694 13.6059 1.51362C14 2.20456 14 3.13638 14 5C14 6.86362 14 7.79544 13.6059 8.48638C13.3397 8.95306 12.9531 9.3397 12.4864 9.60589C11.931 9.92266 11.2201 9.98482 9.99473 9.99702L9.99697 10C9.98464 11.2221 9.92221 11.9318 9.60589 12.4864C9.3397 12.9531 8.95306 13.3397 8.48638 13.6059C7.79544 14 6.86362 14 5 14C3.13638 14 2.20456 14 1.51362 13.6059C1.04694 13.3397 0.660299 12.9531 0.394105 12.4864C0 11.7955 0 10.8636 0 9.00002C0 7.1364 0 6.20459 0.394105 5.51364C0.660299 5.04696 1.04694 4.66032 1.51362 4.39413C2.06818 4.07782 2.77789 4.01538 4 4.00305L4.00298 4.00529ZM9 8.5C8.04098 8.5 7.42048 8.49861 6.95033 8.45667C6.49953 8.41646 6.3366 8.34846 6.25681 8.30295C6.02347 8.16985 5.83015 7.97653 5.69705 7.74319C5.65154 7.6634 5.58354 7.50047 5.54333 7.04967C5.50139 6.57952 5.5 5.95902 5.5 5C5.5 4.04098 5.50139 3.42048 5.54333 2.95033C5.58354 2.49953 5.65154 2.3366 5.69705 2.25681C5.83015 2.02347 6.02347 1.83015 6.25681 1.69705C6.3366 1.65154 6.49953 1.58354 6.95033 1.54333C7.42048 1.50139 8.04098 1.5 9 1.5C9.95902 1.5 10.5795 1.50139 11.0497 1.54333C11.5005 1.58354 11.6634 1.65154 11.7432 1.69705C11.9765 1.83015 12.1699 2.02347 12.3029 2.25681C12.3485 2.3366 12.4165 2.49953 12.4567 2.95033C12.4986 3.42048 12.5 4.04098 12.5 5C12.5 5.95902 12.4986 6.57952 12.4567 7.04967C12.4165 7.50047 12.3485 7.6634 12.3029 7.74319C12.1699 7.97653 11.9765 8.16985 11.7432 8.30295C11.6634 8.34846 11.5005 8.41646 11.0497 8.45667C10.5795 8.49861 9.95902 8.5 9 8.5ZM4 5.50319C3.56883 5.50762 3.2325 5.51818 2.95033 5.54335C2.49953 5.58356 2.3366 5.65156 2.25681 5.69707C2.02347 5.83017 1.83015 6.02349 1.69705 6.25683C1.65154 6.33662 1.58354 6.49955 1.54333 6.95035C1.50139 7.4205 1.5 8.041 1.5 9.00002C1.5 9.95904 1.50139 10.5795 1.54333 11.0497C1.58354 11.5005 1.65154 11.6634 1.69705 11.7432C1.83015 11.9766 2.02347 12.1699 2.25681 12.303C2.3366 12.3485 2.49953 12.4165 2.95033 12.4567C3.42048 12.4986 4.04098 12.5 5 12.5C5.95902 12.5 6.57952 12.4986 7.04967 12.4567C7.50047 12.4165 7.6634 12.3485 7.74319 12.303C7.97653 12.1699 8.16985 11.9766 8.30295 11.7432C8.34846 11.6634 8.41646 11.5005 8.45667 11.0497C8.48184 10.7675 8.4924 10.4312 8.49683 10C6.96254 9.99683 6.13941 9.96284 5.51362 9.60589C5.04694 9.3397 4.6603 8.95306 4.39411 8.48638C4.03716 7.86059 4.00317 7.03747 4 5.50319Z" fill="#66B1FF"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 378 B

-3
View File
@@ -1,3 +0,0 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.4886 8.58186C15.5877 9.45941 14.357 9.99999 13 9.99999C10.2386 9.99999 8 7.76142 8 4.99999C8 3.64301 8.54058 2.41228 9.41813 1.51141C9.57056 1.35494 9.73311 1.20844 9.90478 1.07295C10.2898 0.769107 10.2101 0.0672333 9.72122 0.0284691C9.43254 0.00557958 9.1383 -0.00365545 8.84492 0.00130131C3.9459 0.0840722 0 4.08123 0 8.99999C0 13.9706 4.02944 18 9 18C13.9188 18 17.9159 14.0541 17.9987 9.15507C18.0036 8.86165 17.9944 8.56746 17.9715 8.27878C17.9328 7.78988 17.2309 7.71024 16.927 8.09521C16.7915 8.26688 16.6451 8.42945 16.4886 8.58186ZM15.2991 11.0814C15.7079 10.9267 16.1623 11.2747 16.0059 11.6828C14.9265 14.4999 12.1968 16.5 9 16.5C4.85786 16.5 1.5 13.1421 1.5 8.99999C1.5 5.80319 3.50007 3.07353 6.31715 1.99409C6.72532 1.83769 7.0733 2.2921 6.91862 2.70092C6.64821 3.41562 6.5 4.19056 6.5 4.99999C6.5 8.58984 9.41015 11.5 13 11.5C13.8094 11.5 14.5844 11.3518 15.2991 11.0814Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

-13
View File
@@ -1,13 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M23.2479 8.99997C23.6627 8.99997 23.9992 8.66248 23.9962 8.24768C23.9778 5.65192 23.8678 4.17733 23.2118 3.02721C22.6794 2.09384 21.9061 1.32056 20.9727 0.788174C19.8226 0.132152 18.348 0.0221324 15.7522 0.00368134C15.3374 0.000732908 14.9999 0.337238 14.9999 0.752048C14.9999 1.16571 15.3349 1.50083 15.7485 1.50378C16.763 1.51101 17.5672 1.53195 18.2418 1.59249C19.2368 1.68178 19.8031 1.84788 20.2295 2.09112C20.9296 2.49041 21.5095 3.07037 21.9088 3.7704C22.1521 4.19685 22.3181 4.76317 22.4074 5.75815C22.468 6.4327 22.4889 7.23688 22.4961 8.25134C22.4991 8.665 22.8342 8.99997 23.2479 8.99997Z" fill="#9EA9A9"/>
<path d="M3.02721 0.788174C4.17732 0.132161 5.6519 0.0221355 8.24762 0.00368196C8.66242 0.000733053 8.99991 0.337238 8.99991 0.752048C8.99991 1.16571 8.66494 1.50083 8.25129 1.50378C7.23685 1.51101 6.43269 1.53195 5.75815 1.59249C4.76317 1.68178 4.19685 1.84788 3.7704 2.09112C3.07037 2.49041 2.49041 3.07037 2.09112 3.7704C1.84788 4.19685 1.68178 4.76317 1.59249 5.75815C1.53195 6.4327 1.51101 7.23688 1.50378 8.25134C1.50083 8.665 1.16571 8.99997 0.752048 8.99997C0.337238 8.99997 0.000732695 8.66247 0.00368134 8.24767C0.0221335 5.65192 0.132156 4.17733 0.788174 3.02721C1.32056 2.09384 2.09384 1.32056 3.02721 0.788174Z" fill="#9EA9A9"/>
<path d="M0.752048 15C0.337238 15 0.000732716 15.3375 0.00368139 15.7523C0.0221337 18.348 0.132157 19.8226 0.788174 20.9727C1.32056 21.9061 2.09384 22.6794 3.02721 23.2118C4.17732 23.8678 5.6519 23.9778 8.24762 23.9962C8.66242 23.9992 8.99991 23.6627 8.99991 23.2479C8.99991 22.8342 8.66494 22.4991 8.25129 22.4961C7.23685 22.4889 6.43269 22.468 5.75815 22.4074C4.76317 22.3181 4.19685 22.1521 3.7704 21.9088C3.07037 21.5095 2.49041 20.9296 2.09112 20.2295C1.84788 19.8031 1.68178 19.2368 1.59249 18.2418C1.53195 17.5672 1.51101 16.7631 1.50378 15.7486C1.50083 15.3349 1.16571 15 0.752048 15Z" fill="#9EA9A9"/>
<path d="M22.4961 15.7486C22.4991 15.3349 22.8342 15 23.2479 15C23.6627 15 23.9992 15.3375 23.9962 15.7523C23.9778 18.348 23.8678 19.8226 23.2118 20.9727C22.6794 21.9061 21.9061 22.6794 20.9727 23.2118C19.8226 23.8678 18.348 23.9778 15.7522 23.9962C15.3374 23.9992 14.9999 23.6627 14.9999 23.2479C14.9999 22.8342 15.3349 22.4991 15.7485 22.4962C16.763 22.4889 17.5672 22.468 18.2418 22.4074C19.2368 22.3181 19.8031 22.1521 20.2295 21.9088C20.9296 21.5095 21.5095 20.9296 21.9088 20.2295C22.1521 19.8031 22.3181 19.2368 22.4074 18.2418C22.468 17.5672 22.4889 16.7631 22.4961 15.7486Z" fill="#9EA9A9"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.49994 3.99997C5.11922 3.99997 3.99994 5.11926 3.99994 6.49997V8.49997C3.99994 9.88068 5.11922 11 6.49994 11H8.49994C9.88065 11 10.9999 9.88068 10.9999 8.49997V6.49997C10.9999 5.11926 9.88065 3.99997 8.49994 3.99997H6.49994ZM8.49994 5.49997H6.49994C5.94765 5.49997 5.49994 5.94768 5.49994 6.49997V8.49997C5.49994 9.05225 5.94765 9.49997 6.49994 9.49997H8.49994C9.05222 9.49997 9.49994 9.05225 9.49994 8.49997V6.49997C9.49994 5.94768 9.05222 5.49997 8.49994 5.49997Z" fill="#9EA9A9"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.9999 6.49997C12.9999 5.11926 14.1192 3.99997 15.4999 3.99997H17.4999C18.8806 3.99997 19.9999 5.11926 19.9999 6.49997V8.49997C19.9999 9.88068 18.8806 11 17.4999 11H15.4999C14.1192 11 12.9999 9.88068 12.9999 8.49997V6.49997ZM15.4999 5.49997H17.4999C18.0522 5.49997 18.4999 5.94768 18.4999 6.49997V8.49997C18.4999 9.05225 18.0522 9.49997 17.4999 9.49997H15.4999C14.9477 9.49997 14.4999 9.05225 14.4999 8.49997V6.49997C14.4999 5.94768 14.9477 5.49997 15.4999 5.49997Z" fill="#9EA9A9"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.49994 13C5.11922 13 3.99994 14.1193 3.99994 15.5V17.5C3.99994 18.8807 5.11922 20 6.49994 20H8.49994C9.88065 20 10.9999 18.8807 10.9999 17.5V15.5C10.9999 14.1193 9.88065 13 8.49994 13H6.49994ZM8.49994 14.5H6.49994C5.94765 14.5 5.49994 14.9477 5.49994 15.5V17.5C5.49994 18.0523 5.94765 18.5 6.49994 18.5H8.49994C9.05222 18.5 9.49994 18.0523 9.49994 17.5V15.5C9.49994 14.9477 9.05222 14.5 8.49994 14.5Z" fill="#9EA9A9"/>
<path d="M12.9999 15.5C12.9999 15.0353 12.9999 14.803 13.0384 14.6098C13.1962 13.8164 13.8164 13.1962 14.6098 13.0384C14.803 13 15.0353 13 15.4999 13V15.5H12.9999Z" fill="#9EA9A9"/>
<path d="M17.4999 13C17.9646 13 18.1969 13 18.3901 13.0384C19.1835 13.1962 19.8037 13.8164 19.9615 14.6098C19.9999 14.803 19.9999 15.0353 19.9999 15.5H17.4999V13Z" fill="#9EA9A9"/>
<path d="M17.4999 17.5H19.9999C19.9999 17.9646 19.9999 18.197 19.9615 18.3901C19.8037 19.1835 19.1835 19.8037 18.3901 19.9615C18.1969 20 17.9646 20 17.4999 20V17.5Z" fill="#9EA9A9"/>
<path d="M12.9999 17.5H15.4999V20C15.0353 20 14.803 20 14.6098 19.9615C13.8164 19.8037 13.1962 19.1835 13.0384 18.3901C12.9999 18.197 12.9999 17.9646 12.9999 17.5Z" fill="#9EA9A9"/>
</svg>

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 681 B

-11
View File
@@ -1,11 +0,0 @@
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11 0.75C11.4142 0.75 11.75 1.08579 11.75 1.5V3.5C11.75 3.91421 11.4142 4.25 11 4.25C10.5858 4.25 10.25 3.91421 10.25 3.5V1.5C10.25 1.08579 10.5858 0.75 11 0.75Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.75 11C6.75 8.65279 8.65279 6.75 11 6.75C13.3472 6.75 15.25 8.65279 15.25 11C15.25 13.3472 13.3472 15.25 11 15.25C8.65279 15.25 6.75 13.3472 6.75 11ZM11 8.25C9.48122 8.25 8.25 9.48122 8.25 11C8.25 12.5188 9.48122 13.75 11 13.75C12.5188 13.75 13.75 12.5188 13.75 11C13.75 9.48122 12.5188 8.25 11 8.25Z" fill="white"/>
<path d="M11.75 18.5C11.75 18.0858 11.4142 17.75 11 17.75C10.5858 17.75 10.25 18.0858 10.25 18.5V20.5C10.25 20.9142 10.5858 21.25 11 21.25C11.4142 21.25 11.75 20.9142 11.75 20.5V18.5Z" fill="white"/>
<path d="M3.75225 3.75216C4.04515 3.45926 4.52002 3.45926 4.81291 3.75216L6.22713 5.16637C6.52002 5.45926 6.52002 5.93414 6.22713 6.22703C5.93423 6.51992 5.45936 6.51992 5.16647 6.22703L3.75225 4.81282C3.45936 4.51992 3.45936 4.04505 3.75225 3.75216Z" fill="white"/>
<path d="M16.8337 15.773C16.5408 15.4801 16.0659 15.4801 15.773 15.773C15.4801 16.0659 15.4801 16.5407 15.773 16.8336L17.1872 18.2478C17.4801 18.5407 17.955 18.5407 18.2479 18.2478C18.5408 17.9549 18.5408 17.4801 18.2479 17.1872L16.8337 15.773Z" fill="white"/>
<path d="M21.25 11C21.25 11.4142 20.9142 11.75 20.5 11.75H18.5C18.0858 11.75 17.75 11.4142 17.75 11C17.75 10.5858 18.0858 10.25 18.5 10.25H20.5C20.9142 10.25 21.25 10.5858 21.25 11Z" fill="white"/>
<path d="M3.5 11.75C3.91421 11.75 4.25 11.4142 4.25 11C4.25 10.5858 3.91421 10.25 3.5 10.25H1.5C1.08579 10.25 0.75 10.5858 0.75 11C0.75 11.4142 1.08579 11.75 1.5 11.75H3.5Z" fill="white"/>
<path d="M18.2479 3.75216C18.5408 4.04506 18.5408 4.51993 18.2479 4.81282L16.8336 6.22704C16.5408 6.51993 16.0659 6.51993 15.773 6.22704C15.4801 5.93414 15.4801 5.45927 15.773 5.16638L17.1872 3.75216C17.4801 3.45927 17.955 3.45927 18.2479 3.75216Z" fill="white"/>
<path d="M6.22711 16.8336C6.52 16.5407 6.52 16.0659 6.22711 15.773C5.93421 15.4801 5.45934 15.4801 5.16645 15.773L3.75223 17.1872C3.45934 17.4801 3.45934 17.955 3.75223 18.2478C4.04513 18.5407 4.52 18.5407 4.81289 18.2478L6.22711 16.8336Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 368 B

-88
View File
@@ -1,88 +0,0 @@
:root {
--primary-bg: #1e1e1e;
--secondary-bg: #272a2a;
--qr-bg: #141414;
--text-color: white;
--wc-btn-bg: #19324d;
--wc-btn-brdr: #0f4b8a;
--wc-btn-clr: #66b1ff;
}
* {
transition: background 0.25s ease-in-out;
}
html,
body {
color: var(--text-color) !important;
padding: 0;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
}
a {
color: inherit;
text-decoration: none;
}
* {
box-sizing: border-box;
}
@media (prefers-color-scheme: dark) {
html {
color-scheme: dark;
}
body {
color: white;
background: black;
}
}
.bg-primary {
background: var(--primary-bg);
}
.bg-qr {
background: var(--qr-bg);
}
.bg-secondary {
background: var(--secondary-bg);
}
.wc-button {
background: var(--wc-btn-bg) !important;
border: solid 1px var(--wc-btn-brdr);
color: var(--wc-btn-clr) !important;
}
.theme-switcher-dark > span > span {
background: url("/moon.svg") no-repeat #66b1ff !important;
}
.theme-switcher-light > span > span {
background: url("/sun.svg") no-repeat #66b1ff !important;
}
.theme-switcher > span {
border: solid 2px white;
}
.theme-switcher-light > span {
background: url("/moon.svg") no-repeat #3b4040 !important;
background-repeat: no-repeat !important;
background-position: 90% center !important;
}
.theme-switcher-dark > span {
background: url("/sun.svg") no-repeat #3b4040 !important;
background-repeat: no-repeat !important;
background-position: 10% center !important;
}
.theme-switcher > span > span {
background-repeat: no-repeat !important;
background-position: center center !important;
}
-20
View File
@@ -1,20 +0,0 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}
@@ -1,57 +0,0 @@
import { Box, Button, Flex, Heading, Image, position } from "@chakra-ui/react";
const DefaultView: React.FC<{
onClick: () => void;
hasInitialized: boolean;
}> = ({ onClick, hasInitialized }) => {
return (
<Box>
<Box position={{ position: "relative" }}>
<Flex
style={{ position: "relative", top: "1.5em" }}
justifyContent="center"
>
<Flex
style={{ position: "absolute", top: "5%" }}
justifyContent="center"
>
<Image
style={{ filter: "blur(10px)" }}
src="/auth.png"
alt="auth"
/>
</Flex>
<Image style={{ filter: "blur(1px)" }} src="/auth.png" alt="auth" />
</Flex>
</Box>
<Flex
flexDir={"column"}
gap="5"
padding="2em 3em"
borderRadius={"24px"}
className="bg-secondary"
alignItems={"center"}
>
<Heading>Sign in</Heading>
<Button
minW="80%"
paddingY="1.25em"
fontSize={"1.25em"}
onClick={onClick}
borderRadius={"16px"}
background="#3396FF"
disabled={!hasInitialized}
>
<Flex gap="1em">
<Image src="/wc.png" fit="scale-down" alt="WC" />
<span style={{ color: "white" }}>
{hasInitialized ? "WalletConnect" : "Initializing..."}
</span>
</Flex>
</Button>
</Flex>
</Box>
);
};
export default DefaultView;
@@ -1,116 +0,0 @@
import {
Box,
Button,
Divider,
Flex,
Grid,
Image,
Spinner,
Text,
} from "@chakra-ui/react";
import truncate from "smart-truncate";
import { providers } from "ethers";
import { useCallback, useEffect, useState } from "react";
const SignedInView: React.FC<{ address: string }> = ({ address }) => {
const [balance, setBalance] = useState<string>();
const [avatar, setAvatar] = useState<string | null>();
const [isLoading, setLoading] = useState<boolean>(false);
useEffect(() => {
const innerEffect = async (address: string) => {
setLoading(true);
const provider = new providers.JsonRpcProvider(
`https://rpc.walletconnect.com/v1/?chainId=eip155:1&projectId=${process.env.NEXT_PUBLIC_PROJECT_ID}`
);
const avatar = await provider.getAvatar(address);
const bigNumBalance = await provider.getBalance(address);
setAvatar(avatar);
setBalance(bigNumBalance.toString());
setLoading(false);
};
if (address) {
innerEffect(address);
}
}, [setBalance, address]);
const onSignOut = useCallback(() => {
window.location.reload();
}, []);
return (
<Box className="bg-secondary" borderRadius={"24px"} padding="2em">
<Flex flexDir={"column"} gap="5">
<Flex justifyContent="space-between" gap="10">
<Grid
borderRadius={"100%"}
width="6em"
height="6em"
className="bg-qr"
border="solid 1px #585F5F"
placeItems="center"
>
{isLoading ? (
<Spinner />
) : (
avatar && <Image src={avatar} alt="avatar" />
)}
</Grid>
<Flex
padding="0.5em"
border="solid 1px #585F5F"
borderRadius={"16px"}
height={"min-content"}
className="bg-qr"
alignItems="center"
gap="0.5em"
>
<Box
width="0.75em"
height="0.75em"
backgroundColor="#2BEE6C"
borderRadius={"100%"}
style={{ filter: "blur(1px)" }}
/>
<span>Connected</span>
</Flex>
</Flex>
<Flex>
<Text fontWeight="800" fontSize={"1.5em"}>
{truncate(address, 12, { position: 7 })}
</Text>
</Flex>
<Divider />
<Flex
justifyContent="space-between"
fontSize={"1.5em"}
alignItems="center"
>
<Text>Balance</Text>
{isLoading ? (
<Spinner />
) : (
<Flex alignItems="center" gap="2">
<Image src="/eth.png" alt="ETH" width="1.5em" height="1.5em" />
<Text>{balance} ETH</Text>
</Flex>
)}
</Flex>
<Flex width={"100%"} justifyContent="center">
<Button
width={"100%"}
className="wc-button"
padding={"1.5em"}
borderRadius={"16px"}
onClick={onSignOut}
>
Sign Out
</Button>
</Flex>
</Flex>
</Box>
);
};
export default SignedInView;
@@ -1,4 +0,0 @@
REACT_APP_PROJECT_ID=39bc93c...
REACT_APP_INFURA_ID=5dc0df...
REACT_APP_RELAY_URL=wss://relay.walletconnect.com
@@ -1,25 +0,0 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.eslintcache
@@ -1,7 +0,0 @@
{
"tabWidth": 2,
"useTabs": false,
"trailingComma": "all",
"printWidth": 100,
"arrowParens": "avoid"
}
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2021 WalletConnect, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -1,55 +0,0 @@
# React dApp (with v2 CosmosProvider)
> **⚠️ This provider-based example is currently reliant on the deprecated `2.0.0-beta.26` SDK. ⚠️**
>
> This example will be updated to be compatible with the latest v2 SDK (`2.0.0-beta.100+`) in due time.
> In the meantime, please use the up-to-date [standalone client example](../react-dapp-v2/).
🔗 Live dapp demo - https://react-dapp-v2-cosmos-provider.vercel.app/ <br />
🔗 Live wallet demo - https://react-wallet-v2.vercel.app <br />
📚 WalletConnect v2 Docs - https://docs.walletconnect.com/2.0
## Overview
This is an example implementation of a React dApp (generated via `create-react-app`) using the v2 [`CosmosProvider`](https://docs.walletconnect.com/2.0/quick-start/dapps/cosmos-provider) to:
- handle pairings
- manage sessions
- send JSON-RPC requests to a paired wallet
## Running locally
Install the app's dependencies:
```bash
yarn
```
Set up your local environment variables by copying the example into your own `.env.local` file:
```bash
cp .env.local.example .env.local
```
Your `.env.local` now contains the following environment variables:
- `REACT_APP_PROJECT_ID` (placeholder) - You can generate your own ProjectId at https://cloud.walletconnect.com
- `REACT_APP_RELAY_URL` (already set)
## Develop
```bash
yarn start
```
## Test
```bash
yarn test
```
## Build
```bash
yarn build
```
-7
View File
@@ -1,7 +0,0 @@
declare module "*.svg";
declare module "*.png";
declare module "*.jpg";
declare module "*.jpeg";
declare module "*.gif";
declare module "*.bmp";
declare module "*.tiff";
-5
View File
@@ -1,5 +0,0 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
@@ -1,16 +0,0 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
distDir: "build",
webpack(config) {
config.resolve.fallback = {
...config.resolve.fallback,
fs: false,
};
return config;
},
};
module.exports = nextConfig;
@@ -1,89 +0,0 @@
{
"name": "react-dapp-v2-cosmos",
"version": "2.0.0-beta.26",
"private": true,
"keywords": [
"walletconnect",
"ethereum",
"web3",
"crypto"
],
"author": "WalletConnect, Inc. <walletconnect.com>",
"license": "MIT",
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"prettier": "prettier --check '**/*.{js,ts,jsx,tsx}'"
},
"repository": {
"type": "git",
"url": "git+https://github.com/walletconnect/walletconnect-monorepo.git"
},
"bugs": {
"url": "https://github.com/walletconnect/walletconnect-monorepo/issues"
},
"resolutions": {
"react-error-overlay": "6.0.11"
},
"dependencies": {
"@walletconnect/types": "2.10.0",
"@walletconnect/utils": "2.10.0",
"axios": "^1.0.0",
"blockies-ts": "^1.0.0",
"caip-api": "^2.0.0-beta.1",
"cosmos-wallet": "^1.2.0",
"eth-sig-util": "^2.5.3",
"ethereumjs-util": "^7.0.6",
"ethers": "^5.3.0",
"prop-types": "^15.7.2",
"qr-image": "^3.2.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-scripts": "^4.0.3",
"styled-components": "^5.2.0",
"typescript": "^4.3.2",
"web-vitals": "^0.2.4",
"next": "12.2.4",
"@ethereumjs/tx": "^3.5.0",
"@walletconnect/encoding": "^1.0.1",
"@walletconnect/universal-provider": "2.10.0",
"@web3modal/standalone": "^2.3.7"
},
"devDependencies": {
"@testing-library/jest-dom": "^5.16.1",
"@testing-library/react": "^12.1.2",
"@testing-library/user-event": "^13.5.0",
"@types/bn.js": "^5.1.0",
"@types/eth-sig-util": "^2.1.1",
"@types/jest": "^27.4.0",
"@types/node": "^17.0.14",
"@types/pino": "^7.0.5",
"@types/prop-types": "^15.7.4",
"@types/qr-image": "^3.2.5",
"@types/react": "18.0.15",
"@types/react-dom": "18.0.6",
"@types/styled-components": "^5.1.21",
"prettier": "^2.5.1",
"eslint-config-next": "12.2.4"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.8 KiB

@@ -1,7 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="26" viewBox="0 0 16 26">
<path
fill="#0C0C0D"
fillRule="nonzero"
d="M8 19.096l7.998-4.733L8 25.637 0 14.363l8 4.733zM8 0l8 12.6-8 4.733L0 12.6 8 0z"
/>
</svg>

Before

Width:  |  Height:  |  Size: 234 B

@@ -1 +0,0 @@
<svg id="Logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2000 2000"><defs><style>.cls-1{fill:#e6007a;}</style></defs><ellipse class="cls-1" cx="1000" cy="441.78" rx="254.27" ry="147.95"/><ellipse class="cls-1" cx="1000" cy="1556.15" rx="254.27" ry="147.95"/><ellipse class="cls-1" cx="517.47" cy="720.38" rx="254.27" ry="147.95" transform="translate(-365.13 808.33) rotate(-60)"/><ellipse class="cls-1" cx="1482.53" cy="1277.56" rx="254.27" ry="147.95" transform="translate(-365.13 1922.69) rotate(-60)"/><ellipse class="cls-1" cx="517.47" cy="1277.56" rx="147.95" ry="254.27" transform="translate(-569.45 429.89) rotate(-30)"/><ellipse class="cls-1" cx="1482.53" cy="720.38" rx="147.95" ry="254.27" transform="translate(-161.57 837.78) rotate(-30)"/></svg>

Before

Width:  |  Height:  |  Size: 763 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

@@ -1,20 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta name="theme-color" content="#000000" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>React App</title>
<meta name="description" content="React App for WalletConnect" />
<style>
@import url("https://fonts.googleapis.com/css?family=Open+Sans:400,500,600,700,800");
</style>
</head>
<body>
<noscript> You need to enable JavaScript to run this app. </noscript>
<div id="root"></div>
</body>
</html>
@@ -1,15 +0,0 @@
{
"short_name": "WalletConnect",
"name": "WalletConnect React App",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": "./index.html",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
@@ -1,33 +0,0 @@
import { JsonRpcRequest } from "@walletconnect/jsonrpc-utils";
import { BLOCKCHAIN_LOGO_BASE_URL } from "../constants";
import { NamespaceMetadata, ChainMetadata, ChainRequestRender } from "../helpers";
export const CosmosMetadata: NamespaceMetadata = {
"cosmoshub-4": {
logo: BLOCKCHAIN_LOGO_BASE_URL + "cosmos:cosmoshub-4.png",
rgb: "27, 31, 53",
},
};
export function getChainMetadata(chainId: string): ChainMetadata {
const reference = chainId.split(":")[1];
const metadata = CosmosMetadata[reference];
if (typeof metadata === "undefined") {
throw new Error(`No chain metadata found for chainId: ${chainId}`);
}
return metadata;
}
export function getChainRequestRender(
request: JsonRpcRequest
): ChainRequestRender[] {
return [
{ label: "Method", value: request.method },
{
label: "params",
value: JSON.stringify(request.params, null, "\t"),
},
];
}
@@ -1,148 +0,0 @@
import { JsonRpcRequest } from "@walletconnect/jsonrpc-utils";
import {
NamespaceMetadata,
ChainMetadata,
ChainRequestRender,
convertHexToNumber,
convertHexToUtf8,
} from "../helpers";
import { BLOCKCHAIN_LOGO_BASE_URL } from "../constants";
export const EIP155Colors = {
ethereum: "99, 125, 234",
optimism: "233, 1, 1",
goerli: "189, 174, 155",
xdai: "73, 169, 166",
polygon: "130, 71, 229",
celo: "60, 203, 132",
arbitrum: "44, 55, 75",
};
export const EIP155Metadata: NamespaceMetadata = {
"1": {
name: "Ethereum",
logo: BLOCKCHAIN_LOGO_BASE_URL + "eip155:1.png",
rgb: EIP155Colors.ethereum,
},
"5": {
logo: BLOCKCHAIN_LOGO_BASE_URL + "eip155:5.png",
rgb: EIP155Colors.goerli,
},
"10": {
name: "Optimism",
logo: BLOCKCHAIN_LOGO_BASE_URL + "eip155:10.png",
rgb: EIP155Colors.optimism,
},
"42": {
logo: BLOCKCHAIN_LOGO_BASE_URL + "eip155:42.png",
rgb: EIP155Colors.ethereum,
},
"69": {
logo: BLOCKCHAIN_LOGO_BASE_URL + "eip155:69.png",
rgb: EIP155Colors.optimism,
},
"100": {
logo: BLOCKCHAIN_LOGO_BASE_URL + "eip155:100.png",
rgb: EIP155Colors.xdai,
},
"137": {
name: "Polygon",
logo: BLOCKCHAIN_LOGO_BASE_URL + "eip155:137.png",
rgb: EIP155Colors.polygon,
},
"80001": {
logo: BLOCKCHAIN_LOGO_BASE_URL + "eip155:80001.png",
rgb: EIP155Colors.polygon,
},
"42161": {
name: "Arbitrum",
logo: BLOCKCHAIN_LOGO_BASE_URL + "eip155:42161.png",
rgb: EIP155Colors.arbitrum,
},
"42220": {
name: "Celo",
logo: BLOCKCHAIN_LOGO_BASE_URL + "eip155:42220.png",
rgb: EIP155Colors.celo,
},
"44787": {
logo: BLOCKCHAIN_LOGO_BASE_URL + "eip155:44787.png",
rgb: EIP155Colors.celo,
},
"421611": {
logo: BLOCKCHAIN_LOGO_BASE_URL + "eip155:421611.png",
rgb: EIP155Colors.arbitrum,
},
};
export function getChainMetadata(chainId: string): ChainMetadata {
const reference = chainId.split(":")[1];
const metadata = EIP155Metadata[reference];
if (typeof metadata === "undefined") {
throw new Error(`No chain metadata found for chainId: ${chainId}`);
}
return metadata;
}
export function getChainRequestRender(request: JsonRpcRequest): ChainRequestRender[] {
let params = [{ label: "Method", value: request.method }];
switch (request.method) {
case "eth_sendTransaction":
case "eth_signTransaction":
params = [
...params,
{ label: "From", value: request.params[0].from },
{ label: "To", value: request.params[0].to },
{
label: "Gas Limit",
value: request.params[0].gas
? convertHexToNumber(request.params[0].gas)
: request.params[0].gasLimit
? convertHexToNumber(request.params[0].gasLimit)
: "",
},
{
label: "Gas Price",
value: convertHexToNumber(request.params[0].gasPrice),
},
{
label: "Nonce",
value: convertHexToNumber(request.params[0].nonce),
},
{
label: "Value",
value: request.params[0].value ? convertHexToNumber(request.params[0].value) : "",
},
{ label: "Data", value: request.params[0].data },
];
break;
case "eth_sign":
params = [
...params,
{ label: "Address", value: request.params[0] },
{ label: "Message", value: request.params[1] },
];
break;
case "personal_sign":
params = [
...params,
{ label: "Address", value: request.params[1] },
{
label: "Message",
value: convertHexToUtf8(request.params[0]),
},
];
break;
default:
params = [
...params,
{
label: "params",
value: JSON.stringify(request.params, null, "\t"),
},
];
break;
}
return params;
}
@@ -1,38 +0,0 @@
import { JsonRpcRequest } from "@walletconnect/jsonrpc-utils";
import * as eip155 from "./eip155";
import * as cosmos from "./cosmos";
import * as polkadot from "./polkadot";
import { ChainMetadata, ChainRequestRender } from "../helpers";
export function getChainMetadata(chainId: string): ChainMetadata {
const namespace = chainId.split(":")[0];
switch (namespace) {
case "eip155":
return eip155.getChainMetadata(chainId);
case "cosmos":
return cosmos.getChainMetadata(chainId);
case "polkadot":
return polkadot.getChainMetadata(chainId);
default:
throw new Error(`No metadata handler for namespace ${namespace}`);
}
}
export function getChainRequestRender(
request: JsonRpcRequest,
chainId: string,
): ChainRequestRender[] {
const namespace = chainId.split(":")[0];
switch (namespace) {
case "eip155":
return eip155.getChainRequestRender(request);
case "cosmos":
return cosmos.getChainRequestRender(request);
case "polkadot":
return polkadot.getChainRequestRender(request);
default:
throw new Error(`No render handler for namespace ${namespace}`);
}
}
@@ -1,38 +0,0 @@
import { JsonRpcRequest } from "@walletconnect/jsonrpc-utils";
import { BLOCKCHAIN_LOGO_BASE_URL } from "../constants";
import { NamespaceMetadata, ChainMetadata, ChainRequestRender } from "../helpers";
export const PolkadotMetadata: NamespaceMetadata = {
// eslint-disable-next-line no-useless-computed-key
["91b171bb158e2d3848fa23a9f1c25182"]: {
logo: "/assets/polkadot.svg",
rgb: "230, 1, 122",
},
};
export function getChainMetadata(chainId: string): ChainMetadata {
const reference = chainId.split(":")[1];
const metadata = PolkadotMetadata[reference];
if (typeof metadata === "undefined") {
throw new Error(`No chain metadata found for chainId: ${chainId}`);
}
return metadata;
}
export function getChainRequestRender(request: JsonRpcRequest): ChainRequestRender[] {
let params = [{ label: "Method", value: request.method }];
switch (request.method) {
default:
params = [
...params,
{
label: "params",
value: JSON.stringify(request.params, null, "\t"),
},
];
break;
}
return params;
}
@@ -1,73 +0,0 @@
import * as React from "react";
import styled from "styled-components";
import Icon from "./Icon";
import { AssetData } from "../helpers";
import { getChainMetadata } from "../chains";
const xdai = getChainMetadata("eip155:100").logo;
const matic = getChainMetadata("eip155:137").logo;
const SAsset = styled.div`
width: 100%;
padding: 20px;
display: flex;
justify-content: space-between;
`;
const SAssetLeft = styled.div`
display: flex;
`;
const SAssetName = styled.div`
display: flex;
margin-left: 10px;
`;
const SAssetRight = styled.div`
display: flex;
`;
const SAssetBalance = styled.div`
display: flex;
`;
// eslint-disable-next-line
function getAssetIcon(asset: AssetData): JSX.Element {
if (!!asset.contractAddress) {
const src = `https://raw.githubusercontent.com/TrustWallet/tokens/master/tokens/${asset.contractAddress.toLowerCase()}.png`;
return <Icon src={src} fallback={"/assets/erc20.svg"} />;
}
switch (asset.symbol.toLowerCase()) {
case "eth":
return <Icon src={"assets/eth.svg"} />;
case "xdai":
return <Icon src={xdai} />;
case "matic":
return <Icon src={matic} />;
default:
return <Icon src={"/assets/erc20.svg"} />;
}
}
interface AssetProps {
asset: { symbol: string; balance: string };
}
const Asset = (props: AssetProps) => {
const { asset } = props;
return (
<SAsset {...props}>
<SAssetLeft>
{/* {getAssetIcon(asset)} */}
<SAssetName>{asset.symbol}</SAssetName>
</SAssetLeft>
<SAssetRight>
<SAssetBalance>{`${asset.balance}`}</SAssetBalance>
</SAssetRight>
</SAsset>
);
};
export default Asset;
@@ -1,24 +0,0 @@
import * as React from "react";
import styled from "styled-components";
const SBannerWrapper = styled.div`
display: flex;
align-items: center;
position: relative;
`;
const SBanner = styled.div`
width: 275px;
height: 45px;
background: url(/assets/walletconnect.png) no-repeat;
background-size: cover;
background-position: center;
`;
const Banner = () => (
<SBannerWrapper>
<SBanner />
</SBannerWrapper>
);
export default Banner;
@@ -1,180 +0,0 @@
import React, { PropsWithChildren, FC } from "react";
import styled from "styled-components";
import { ChainData } from "caip-api";
import Asset from "./Asset";
import Button from "./Button";
import Column from "./Column";
import Loader from "./Loader";
import { getChainMetadata } from "../chains";
import {
AccountAction,
ellipseAddress,
ChainMetadata,
ChainNamespaces,
AccountBalances,
} from "../helpers";
import { fonts } from "../styles";
interface AccountStyleProps {
rgb: string;
}
const SAccount = styled.div<AccountStyleProps>`
width: 100%;
display: flex;
flex-direction: column;
align-items: flex-start;
border-radius: 8px;
padding: 8px;
margin: 5px 0;
border: ${({ rgb }) => `2px solid rgb(${rgb})`};
&.active {
box-shadow: ${({ rgb }) => `0 0 8px rgb(${rgb})`};
}
`;
const SChain = styled.div`
width: 100%;
display: flex;
align-items: center;
& p {
font-weight: 600;
}
& img {
border-radius: 50%;
width: 35px;
height: 35px;
margin-right: 10px;
}
`;
const SContainer = styled.div`
height: 100%;
min-height: 200px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
word-break: break-word;
`;
const SFullWidthContainer = styled.div`
width: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
`;
const SAction = styled(Button as any)`
border-radius: 8px;
font-size: ${fonts.size.medium};
height: 44px;
width: 100%;
margin: 12px 0;
background-color: ${({ rgb }) => `rgb(${rgb})`};
`;
const SBlockchainChildrenContainer = styled(SFullWidthContainer)`
flex-direction: column;
`;
interface BlockchainProps {
chainData: ChainNamespaces;
fetching?: boolean;
active?: boolean;
chainId: string;
address?: string;
onClick?: (chain: string) => void;
balances?: AccountBalances;
actions?: AccountAction[];
}
interface BlockchainDisplayData {
data: ChainData;
meta: ChainMetadata;
}
function getBlockchainDisplayData(
chainId: string,
chainData: ChainNamespaces,
): BlockchainDisplayData | undefined {
const [namespace, reference] = chainId.split(":");
let meta: ChainMetadata;
try {
meta = getChainMetadata(chainId);
} catch (e) {
return undefined;
}
const data: ChainData = chainData[namespace][reference];
if (typeof data === "undefined") return undefined;
return { data, meta };
}
const Blockchain: FC<PropsWithChildren<BlockchainProps>> = (
props: PropsWithChildren<BlockchainProps>,
) => {
const { chainData, fetching, chainId, address, onClick, balances, active, actions } = props;
if (!Object.keys(chainData).length) return null;
const chain = getBlockchainDisplayData(chainId, chainData);
if (typeof chain === "undefined") {
return null;
}
const name = chain.meta.name || chain.data.name;
return (
<React.Fragment>
<SAccount
rgb={chain.meta.rgb}
onClick={() => onClick && onClick(props.chainId)}
className={active ? "active" : ""}
>
<SChain>
<img src={chain.meta.logo} alt={name} />
<p>{name}</p>
</SChain>
{!!address && <p>{ellipseAddress(address)}</p>}
<SBlockchainChildrenContainer>
{fetching ? (
<Column center>
<SContainer>
<Loader rgb={`rgb(${chain.meta.rgb})`} />
</SContainer>
</Column>
) : (
<>
{!!address && !!balances && balances[address] ? (
<SFullWidthContainer>
<h6>Balances</h6>
<Column center>
<Asset key={balances[address].symbol} asset={balances[address]} />
</Column>
</SFullWidthContainer>
) : null}
{address && !!actions && actions.length ? (
<SFullWidthContainer>
<h6>Methods</h6>
{actions.map(action => (
<SAction
key={action.method}
left
rgb={chain.meta.rgb}
onClick={() => action.callback(address)}
>
{action.method}
</SAction>
))}
</SFullWidthContainer>
) : null}
</>
)}
</SBlockchainChildrenContainer>
</SAccount>
</React.Fragment>
);
};
export default Blockchain;
@@ -1,128 +0,0 @@
import * as React from "react";
import styled from "styled-components";
import Loader from "./Loader";
import { colors, fonts, shadows, transitions } from "../styles";
interface ButtonStyleProps {
fetching: boolean;
outline: boolean;
type: "button" | "submit" | "reset";
color: string;
disabled: boolean;
icon: any;
left: boolean;
}
interface ButtonProps extends ButtonStyleProps {
children: React.ReactNode;
onClick?: any;
}
const SIcon = styled.div`
position: absolute;
height: 15px;
width: 15px;
margin: 0 8px;
top: calc((100% - 15px) / 2);
`;
const SHoverLayer = styled.div`
transition: ${transitions.button};
position: absolute;
height: 100%;
width: 100%;
background-color: rgb(${colors.white}, 0.1);
top: 0;
bottom: 0;
right: 0;
left: 0;
pointer-events: none;
opacity: 0;
visibility: hidden;
`;
const SButton = styled.button<ButtonStyleProps>`
transition: ${transitions.button};
position: relative;
border: none;
border-style: none;
box-sizing: border-box;
background-color: ${({ outline, color }) => (outline ? "transparent" : `rgb(${colors[color]})`)};
border: ${({ outline, color }) => (outline ? `1px solid rgb(${colors[color]})` : "none")};
color: ${({ outline, color }) => (outline ? `rgb(${colors[color]})` : `rgb(${colors.white})`)};
box-shadow: ${({ outline }) => (outline ? "none" : `${shadows.soft}`)};
border-radius: 8px;
font-size: ${fonts.size.medium};
font-weight: ${fonts.weight.semibold};
padding: ${({ icon, left }) =>
icon ? (left ? "7px 12px 8px 28px" : "7px 28px 8px 12px") : "8px 12px"};
cursor: ${({ disabled }) => (disabled ? "auto" : "pointer")};
will-change: transform;
&:disabled {
opacity: 0.6;
box-shadow: ${({ outline }) => (outline ? "none" : `${shadows.soft}`)};
}
@media (hover: hover) {
&:hover {
transform: ${({ disabled }) => (!disabled ? "translateY(-1px)" : "none")};
box-shadow: ${({ disabled, outline }) =>
!disabled ? (outline ? "none" : `${shadows.hover}`) : `${shadows.soft}`};
}
&:hover ${SHoverLayer} {
opacity: 1;
visibility: visible;
}
}
&:active {
transform: ${({ disabled }) => (!disabled ? "translateY(1px)" : "none")};
box-shadow: ${({ outline }) => (outline ? "none" : `${shadows.soft}`)};
color: ${({ outline, color }) =>
outline ? `rgb(${colors[color]})` : `rgba(${colors.white}, 0.24)`};
& ${SIcon} {
opacity: 0.8;
}
}
& ${SIcon} {
right: ${({ left }) => (left ? "auto" : "0")};
left: ${({ left }) => (left ? "0" : "auto")};
display: ${({ icon }) => (icon ? "block" : "none")};
mask: ${({ icon }) => (icon ? `url(${icon}) center no-repeat` : "none")};
background-color: ${({ outline, color }) =>
outline ? `rgb(${colors[color]})` : `rgb(${colors.white})`};
transition: 0.15s ease;
}
`;
const Button = (props: ButtonProps) => (
<SButton
{...props}
type={props.type}
outline={props.outline}
color={props.color}
disabled={props.disabled}
icon={props.icon}
left={props.left}
>
<SHoverLayer />
<SIcon />
{props.fetching ? <Loader size={20} color="white" /> : props.children}
</SButton>
);
Button.defaultProps = {
fetching: false,
outline: false,
type: "button",
color: "lightBlue",
disabled: false,
icon: null,
left: false,
};
export default Button;
@@ -1,49 +0,0 @@
import * as React from "react";
import * as PropTypes from "prop-types";
import styled from "styled-components";
interface ColumnStyleProps {
spanHeight: boolean;
maxWidth: number;
center: boolean;
}
interface ColumnProps extends ColumnStyleProps {
children: React.ReactNode;
}
const SColumn = styled.div<ColumnStyleProps>`
position: relative;
width: 100%;
height: ${({ spanHeight }) => (spanHeight ? "100%" : "auto")};
max-width: ${({ maxWidth }) => `${maxWidth}px`};
margin: 0 auto;
display: flex;
flex-direction: column;
align-items: center;
justify-content: ${({ center }) => (center ? "center" : "flex-start")};
`;
const Column = (props: ColumnProps) => {
const { children, spanHeight, maxWidth, center } = props;
return (
<SColumn {...props} spanHeight={spanHeight} maxWidth={maxWidth} center={center}>
{children}
</SColumn>
);
};
Column.propTypes = {
children: PropTypes.node.isRequired,
spanHeight: PropTypes.bool,
maxWidth: PropTypes.number,
center: PropTypes.bool,
};
Column.defaultProps = {
spanHeight: false,
maxWidth: 600,
center: false,
};
export default Column;
@@ -1,80 +0,0 @@
import { SessionTypes } from "@walletconnect/types";
import * as React from "react";
import styled from "styled-components";
import { fonts, responsive } from "../styles";
import Button from "./Button";
const SHeader = styled.div`
margin-top: -1px;
margin-bottom: 1px;
width: 100%;
height: 100px;
display: flex;
align-items: flex-start;
justify-content: space-between;
padding: 16px;
@media screen and (${responsive.sm.max}) {
font-size: ${fonts.size.small};
}
`;
const SHeaderActions = styled.div`
display: flex;
& > button:first-child {
margin-right: 10px !important;
}
`;
const SActiveAccount = styled.div`
display: flex;
align-items: center;
position: relative;
font-weight: 500;
`;
const SActiveSession = styled(SActiveAccount as any)`
flex-direction: column;
text-align: left;
align-items: flex-start;
& p {
font-size: 0.8em;
margin: 0;
padding: 0;
}
& p:nth-child(n + 2) {
font-weight: bold;
}
`;
interface HeaderProps {
ping: () => Promise<void>;
disconnect: () => Promise<void>;
session: SessionTypes.Struct | undefined;
}
const Header = (props: HeaderProps) => {
const { ping, disconnect, session } = props;
return (
<SHeader {...props}>
{session ? (
<>
<SActiveSession>
<p>{`Connected to`}</p>
<p>{session.peer.metadata.name}</p>
</SActiveSession>
<SHeaderActions>
<Button outline color="black" onClick={ping}>
{"Ping"}
</Button>
<Button outline color="red" onClick={disconnect}>
{"Disconnect"}
</Button>
</SHeaderActions>
</>
) : null}
</SHeader>
);
};
export default Header;
@@ -1,42 +0,0 @@
import * as React from "react";
import * as PropTypes from "prop-types";
import styled from "styled-components";
interface IconStyleProps {
size: number;
}
const SIcon = styled.img<IconStyleProps>`
width: ${({ size }) => `${size}px`};
height: ${({ size }) => `${size}px`};
`;
const Icon = (props: any) => {
const { src, fallback, size } = props;
return (
<SIcon
{...props}
src={src}
size={size}
onError={(event: any) => {
if (fallback) {
event.target.src = fallback;
}
}}
/>
);
};
Icon.propTypes = {
src: PropTypes.string,
fallback: PropTypes.string,
size: PropTypes.number,
};
Icon.defaultProps = {
src: null,
fallback: "",
size: 20,
};
export default Icon;
@@ -1,68 +0,0 @@
import * as React from "react";
import * as PropTypes from "prop-types";
import styled, { keyframes } from "styled-components";
import { colors } from "../styles";
const load = keyframes`
0% {
transform: scale(1.0);
}
5% {
transform: scale(1.0);
}
50% {
transform: scale(0.8);
}
95% {
transform: scale(1.0);
}
100% {
transform: scale(1.0);
}
`;
interface LoaderStyleProps {
size: number;
}
interface LoaderProps extends LoaderStyleProps {
color: string;
rgb?: string;
}
const SLoader = styled.svg<LoaderStyleProps>`
width: ${({ size }) => `${size}px`};
height: ${({ size }) => `${size}px`};
animation: ${load} 1s infinite cubic-bezier(0.25, 0, 0.75, 1);
transform: translateZ(0);
`;
const Loader = (props: LoaderProps) => {
const { size, color } = props;
const rgb = props.rgb || `rgb(${colors[color]})`;
return (
<SLoader viewBox="0 0 186 187" size={size}>
<g stroke="none" strokeWidth="1" fill="none" fillRule="evenodd">
<path
d="M60,10.34375 C32.3857625,10.34375 10,32.7295125 10,60.34375 L10,126.34375 C10,153.957987 32.3857625,176.34375 60,176.34375 L126,176.34375 C153.614237,176.34375 176,153.957987 176,126.34375 L176,60.34375 C176,32.7295125 153.614237,10.34375 126,10.34375 L60,10.34375 Z M60,0.34375 L126,0.34375 C159.137085,0.34375 186,27.206665 186,60.34375 L186,126.34375 C186,159.480835 159.137085,186.34375 126,186.34375 L60,186.34375 C26.862915,186.34375 0,159.480835 0,126.34375 L0,60.34375 C0,27.206665 26.862915,0.34375 60,0.34375 Z"
id="Rectangle-Copy"
fill={rgb}
fillRule="nonzero"
/>
<rect id="Rectangle" fill={rgb} x="44" y="44.34375" width="98" height="98" rx="35" />
</g>
</SLoader>
);
};
Loader.propTypes = {
size: PropTypes.number,
color: PropTypes.string,
};
Loader.defaultProps = {
size: 40,
color: "lightBlue",
};
export default Loader;
@@ -1,17 +0,0 @@
import Head from "next/head";
import * as React from "react";
import { DEFAULT_APP_METADATA } from "../constants";
const Metadata = () => (
<Head>
<title>{DEFAULT_APP_METADATA.name}</title>
<meta name="description" content={DEFAULT_APP_METADATA.description} />
<meta name="url" content={DEFAULT_APP_METADATA.url} />
{DEFAULT_APP_METADATA.icons.map((icon, index) => (
<link key={index} rel="icon" href={icon} />
))}
</Head>
);
export default Metadata;
@@ -1,141 +0,0 @@
import React, { useEffect, useRef, useState } from "react";
import styled from "styled-components";
import { colors, transitions } from "../styles";
const SLightbox = styled.div<{
show: boolean;
offset: number;
opacity?: number;
}>`
transition: opacity 0.1s ease-in-out;
text-align: center;
position: absolute;
width: 100vw;
height: 100%;
margin-left: -50vw;
top: ${({ offset }) => (offset ? `-${offset}px` : 0)};
left: 50%;
z-index: 2;
will-change: opacity;
background-color: ${({ opacity }) => {
let alpha = 0.4;
if (typeof opacity === "number") {
alpha = opacity;
}
return `rgba(0, 0, 0, ${alpha})`;
}};
opacity: ${({ show }) => (show ? 1 : 0)};
visibility: ${({ show }) => (show ? "visible" : "hidden")};
pointer-events: ${({ show }) => (show ? "auto" : "none")};
display: flex;
justify-content: center;
align-items: center;
`;
const SModalContainer = styled.div`
position: relative;
width: 100%;
height: 100%;
padding: 15px;
display: flex;
align-items: center;
justify-content: center;
`;
const SHitbox = styled.div`
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
`;
interface CloseButtonStyleProps {
size: number;
color: string;
onClick?: any;
}
const SCloseButton = styled.div<CloseButtonStyleProps>`
transition: ${transitions.short};
position: absolute;
width: ${({ size }) => `${size}px`};
height: ${({ size }) => `${size}px`};
right: ${({ size }) => `${size / 1.6667}px`};
top: ${({ size }) => `${size / 1.6667}px`};
opacity: 0.5;
cursor: pointer;
&:hover {
opacity: 1;
}
&:before,
&:after {
position: absolute;
content: " ";
height: ${({ size }) => `${size}px`};
width: 2px;
background: ${({ color }) => `rgb(${colors[color]})`};
}
&:before {
transform: rotate(45deg);
}
&:after {
transform: rotate(-45deg);
}
`;
const SCard = styled.div`
position: relative;
width: 100%;
max-width: 500px;
padding: 25px;
background-color: rgb(${colors.white});
border-radius: 6px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
`;
const SModalContent = styled.div`
position: relative;
width: 100%;
position: relative;
word-wrap: break-word;
`;
interface IProps {
children: React.ReactNode;
show: boolean;
closeModal: () => void;
opacity?: number;
}
export default function Modal({ children, show, opacity, closeModal }: IProps) {
const [offset, setOffset] = useState(0);
const lightboxRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (lightboxRef.current) {
const lightboxRect = lightboxRef.current.getBoundingClientRect();
const nextOffset = lightboxRect.top > 0 ? lightboxRect.top : 0;
if (nextOffset !== 0 && nextOffset !== offset) {
setOffset(nextOffset);
}
}
}, [offset]);
return (
<SLightbox show={show} offset={offset} opacity={opacity} ref={lightboxRef}>
<SModalContainer>
<SHitbox onClick={closeModal} />
<SCard>
<SCloseButton size={25} color={"dark"} onClick={closeModal} />
<SModalContent>{children}</SModalContent>
</SCard>
</SModalContainer>
</SLightbox>
);
}
@@ -1,33 +0,0 @@
import * as React from "react";
import styled from "styled-components";
import { PairingTypes } from "@walletconnect/types";
import Peer from "./Peer";
interface PairingProps {
pairing: PairingTypes.Struct;
onClick?: any;
}
const SPairingContainer = styled.div`
width: 100%;
cursor: pointer;
`;
const Pairing = (props: PairingProps) => {
const { peerMetadata } = props.pairing;
return (
<SPairingContainer onClick={props.onClick}>
<div>
{typeof peerMetadata !== "undefined" ? (
<Peer oneLiner metadata={peerMetadata} />
) : (
<div>{`Unknown`}</div>
)}
</div>
</SPairingContainer>
);
};
export default Pairing;
@@ -1,74 +0,0 @@
import * as React from "react";
import styled from "styled-components";
import { colors, fonts } from "../styles";
import { Metadata } from "@walletconnect/universal-provider";
const SPeerOneLiner = styled.div`
display: flex;
align-items: center;
border-radius: 8px;
border: 2px solid rgb(${colors.darkGrey});
padding: 5px;
& img {
width: 40px;
height: 40px;
}
& > div {
margin-left: 10px;
}
`;
const SPeerCard = styled.div`
align-items: center;
justify-content: center;
display: flex;
width: 100%;
flex-direction: column;
border-radius: 8px;
border: 2px solid rgb(${colors.darkGrey});
padding: 5px;
& > div {
margin: 4px auto;
}
`;
const SIcon = styled.img`
width: 100px;
margin: 0 auto;
`;
const SCenter = styled.div`
text-align: center;
`;
const SUrl = styled(SCenter as any)`
font-size: ${fonts.size.small};
opacity: 0.8;
`;
const SName = styled(SCenter as any)`
font-weight: bold;
`;
interface PeerProps {
oneLiner?: boolean;
metadata: Metadata;
}
const Peer = (props: PeerProps) =>
props.oneLiner ? (
<SPeerOneLiner>
<img src={props.metadata.icons[0]} alt={props.metadata.name} />
<div>{props.metadata.name}</div>
</SPeerOneLiner>
) : (
<SPeerCard>
<SIcon src={props.metadata.icons[0]} alt={props.metadata.name} />
<SName>{props.metadata.name}</SName>
<SCenter>{props.metadata.description}</SCenter>
<SUrl>{props.metadata.url}</SUrl>
</SPeerCard>
);
export default Peer;
@@ -1,74 +0,0 @@
import * as React from "react";
import * as PropTypes from "prop-types";
import styled from "styled-components";
import { colors, transitions } from "../styles";
interface IToggleStyleProps {
color: string;
active: boolean;
}
const SToggle = styled.div<IToggleStyleProps>`
position: relative;
margin: 0;
padding: 0;
box-sizing: border-box;
transition: ${transitions.base};
& div {
transition: ${transitions.base};
appearance: none;
transition: all 0.3s ease;
box-shadow: ${({ active, color }) =>
active
? `inset 0px 0px 0px 20px rgb(${colors[color]})`
: `inset 0px 0px 0px 1px rgb(${colors.grey})`};
border-radius: 1rem;
background-color: rgb(${colors.white});
padding: 1px;
display: inline-block;
width: 46px;
height: 26px;
position: relative;
cursor: pointer;
margin: 0px;
vertical-align: bottom;
outline: none;
border: none;
}
& div:after {
transition: ${transitions.base};
box-shadow: inset 0 1px 0 rgb(${colors.grey}), 0px 2px 2px 1px rgba(${colors.black}, 0.2);
border-radius: 1rem;
left: ${({ active }) => (active ? `20px` : `0`)};
content: "";
position: absolute;
width: 24px;
height: 24px;
cursor: pointer;
background-color: rgb(${colors.white});
}
`;
interface IToggleProps extends IToggleStyleProps {
onClick?: any;
}
const Toggle = (props: IToggleProps) => (
<SToggle color={props.color} active={props.active} onClick={props.onClick}>
<div />
</SToggle>
);
Toggle.propTypes = {
active: PropTypes.bool,
color: PropTypes.string,
onClick: PropTypes.func,
};
Toggle.defaultProps = {
active: false,
color: "green",
};
export default Toggle;
@@ -1,50 +0,0 @@
import * as React from "react";
import * as PropTypes from "prop-types";
import styled, { keyframes } from "styled-components";
const fadeIn = keyframes`
0% {
opacity: 0;
}
100% {
opacity: 1;
}
`;
interface WrapperStyleProps {
center: boolean;
}
const SWrapper = styled.div<WrapperStyleProps>`
will-change: transform, opacity;
animation: ${fadeIn} 0.7s ease 0s normal 1;
min-height: 200px;
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: ${({ center }) => (center ? `center` : `flex-start`)};
`;
interface WrapperProps extends WrapperStyleProps {
children: React.ReactNode;
}
const Wrapper = (props: WrapperProps) => {
const { children, center } = props;
return (
<SWrapper {...props} center={center}>
{children}
</SWrapper>
);
};
Wrapper.propTypes = {
children: PropTypes.node.isRequired,
center: PropTypes.bool,
};
Wrapper.defaultProps = {
center: false,
};
export default Wrapper;
@@ -1,73 +0,0 @@
import styled from "styled-components";
import { fonts } from "../../styles";
import Button from "../Button";
import Column from "../Column";
import Wrapper from "../Wrapper";
export const SLayout = styled.div`
position: relative;
width: 100%;
min-height: 100vh;
text-align: center;
`;
export const SContent = styled(Wrapper as any)`
width: 100%;
height: 100%;
padding: 0 16px;
`;
export const SLanding = styled(Column as any)`
/* height: 600px; */
`;
export const SButtonContainer = styled(Column as any)`
width: 250px;
margin: 50px 0;
`;
export const SConnectButton = styled(Button as any)`
border-radius: 8px;
font-size: ${fonts.size.medium};
height: 44px;
width: 100%;
margin: 12px 0;
`;
export const SAccountsContainer = styled(SLanding as any)`
height: 100%;
padding-bottom: 30px;
& h3 {
padding-top: 30px;
}
`;
export const SToggleContainer = styled.div`
width: 100%;
display: flex;
justify-content: center;
align-items: center;
margin: 10px auto;
& > p {
margin-right: 10px;
}
`;
export const SFullWidthContainer = styled.div`
width: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
`;
export const SAccounts = styled(SFullWidthContainer)`
justify-content: space-between;
& > div {
margin: 12px 0;
flex: 1 0 100%;
@media (min-width: 648px) {
flex: 0 1 48%;
}
}
`;
@@ -1,32 +0,0 @@
import styled from "styled-components";
export const SContainer = styled.div`
height: 100%;
min-height: 200px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
word-break: break-word;
`;
export const STable = styled(SContainer as any)`
flex-direction: column;
text-align: left;
`;
export const SRow = styled.div`
width: 100%;
display: flex;
margin: 6px 0;
`;
export const SKey = styled.div`
width: 30%;
font-weight: 700;
`;
export const SValue = styled.div`
width: 70%;
font-family: monospace;
`;
@@ -1,29 +0,0 @@
export const DEFAULT_MAIN_CHAINS = [
// mainnets
"cosmos:cosmoshub-4",
];
export const DEFAULT_TEST_CHAINS = [
// testnets
];
export const DEFAULT_CHAINS = [...DEFAULT_MAIN_CHAINS, ...DEFAULT_TEST_CHAINS];
export const DEFAULT_PROJECT_ID = process.env.NEXT_PUBLIC_PROJECT_ID;
export const DEFAULT_INFURA_ID = process.env.NEXT_PUBLIC_INFURA_ID;
export const DEFAULT_RELAY_URL = process.env.NEXT_PUBLIC_RELAY_URL;
export const DEFAULT_EIP155_METHODS = ["eth_sendTransaction", "personal_sign", "eth_signTypedData"];
export const DEFAULT_COSMOS_METHODS = ["cosmos_signDirect", "cosmos_signAmino"];
export const DEFAULT_LOGGER = "debug";
export const DEFAULT_APP_METADATA = {
name: "React App",
description: "React App for WalletConnect",
url: "https://walletconnect.com/",
icons: ["https://avatars.githubusercontent.com/u/37784886"],
};
@@ -1,2 +0,0 @@
export * from "./default";
export * from "./logo";
@@ -1 +0,0 @@
export const BLOCKCHAIN_LOGO_BASE_URL = "https://blockchain-api.xyz/logos/";
@@ -1,276 +0,0 @@
import SignClient from "@walletconnect/sign-client";
import { ISignClient, PairingTypes, SessionTypes } from "@walletconnect/types";
import UniversalProvider, { IUniversalProvider } from "@walletconnect/universal-provider";
import { Web3Modal } from "@web3modal/standalone";
import {
createContext,
ReactNode,
useCallback,
useContext,
useEffect,
useMemo,
useState,
} from "react";
import {
DEFAULT_COSMOS_METHODS,
DEFAULT_LOGGER,
DEFAULT_PROJECT_ID,
DEFAULT_RELAY_URL,
} from "../constants";
import { AccountBalances, ChainNamespaces, getAllChainNamespaces } from "../helpers";
import { apiGetChainNamespace, ChainsMap } from "caip-api";
/**
* Types
*/
interface IContext {
client: ISignClient | undefined;
session: SessionTypes.Struct | undefined;
disconnect: () => Promise<void>;
isInitializing: boolean;
chain: string;
pairings: PairingTypes.Struct[];
accounts: string[];
balances: AccountBalances;
chainData: ChainNamespaces;
onEnable: (chainId: string) => Promise<void>;
cosmosProvider?: IUniversalProvider;
}
/**
* Context
*/
export const ClientContext = createContext<IContext>({} as IContext);
/**
* Provider
*/
export function ClientContextProvider({ children }: { children: ReactNode | ReactNode[] }) {
const [client, setClient] = useState<ISignClient>();
const [pairings, setPairings] = useState<PairingTypes.Struct[]>([]);
const [session, setSession] = useState<SessionTypes.Struct>();
const [web3Modal, setWeb3Modal] = useState<Web3Modal>();
const [cosmosProvider, setCosmosProvider] = useState<UniversalProvider>();
const [isInitializing, setIsInitializing] = useState(false);
const [hasCheckedPersistedSession, setHasCheckedPersistedSession] = useState(false);
const [balances, setBalances] = useState<AccountBalances>({});
const [accounts, setAccounts] = useState<string[]>([]);
const [chainData, setChainData] = useState<ChainNamespaces>({});
const [chain, setChain] = useState<string>("");
const resetApp = () => {
setPairings([]);
setSession(undefined);
setBalances({});
setAccounts([]);
setChain("");
};
const loadChainData = async () => {
const namespaces = getAllChainNamespaces();
const chainData: ChainNamespaces = {};
await Promise.all(
namespaces.map(async namespace => {
let chains: ChainsMap | undefined;
try {
chains = await apiGetChainNamespace(namespace);
} catch (e) {
// ignore error
}
if (typeof chains !== "undefined") {
chainData[namespace] = chains;
}
}),
);
setChainData(chainData);
};
const disconnect = useCallback(async () => {
if (typeof cosmosProvider === "undefined") {
throw new Error("cosmosProvider is not initialized");
}
cosmosProvider.disconnect();
resetApp();
}, [cosmosProvider]);
const onSessionConnected = useCallback(async (_session: SessionTypes.Struct) => {
setSession(_session);
}, []);
const _subscribeToProviderEvents = useCallback(
async (provider: UniversalProvider) => {
provider.on("display_uri", async (uri: string) => {
console.log("EVENT", "QR Code Modal open", uri);
web3Modal?.openModal({ uri });
});
provider.on("session_delete", () => {
console.log("EVENT", "session_deleted");
resetApp();
});
},
[web3Modal],
);
const createClient = useCallback(async () => {
try {
setIsInitializing(true);
const provider = await UniversalProvider.init({
projectId: DEFAULT_PROJECT_ID,
logger: DEFAULT_LOGGER,
relayUrl: DEFAULT_RELAY_URL,
});
setCosmosProvider(provider);
setClient(provider.client);
const web3Modal = new Web3Modal({
projectId: DEFAULT_PROJECT_ID || "",
walletConnectVersion: 2,
});
setWeb3Modal(web3Modal);
} catch (err) {
throw err;
} finally {
setIsInitializing(false);
}
}, []);
const onEnable = useCallback(
async (caipChainId: string) => {
if (!cosmosProvider) {
throw new ReferenceError("WalletConnect Client is not initialized.");
}
const chainId = caipChainId.split(":").pop();
if (!chainId) {
throw new Error("Could not derive chainId from CAIP chainId");
}
console.log("Enabling cosmosProvider for chainId: ", chainId);
// Create WalletConnect Provider
const session = await cosmosProvider.connect({
namespaces: {
cosmos: {
methods: DEFAULT_COSMOS_METHODS,
chains: [caipChainId],
events: ["chainChanged", "accountsChanged"],
},
},
});
const _accounts = await cosmosProvider.enable();
setAccounts(_accounts);
setSession(session);
onSessionConnected(session!);
setChain(caipChainId);
web3Modal?.closeModal();
},
[cosmosProvider, onSessionConnected, web3Modal],
);
const _checkForPersistedSession = useCallback(
async (provider: IUniversalProvider) => {
if (!provider) {
throw new Error("Universal Provider is not initialized");
}
// populates existing pairings to state
setPairings(provider.client!.pairing.getAll({ active: true }));
if (typeof session !== "undefined") return;
// populates existing session to state (assume only the top one)
if (provider.session) {
console.log("provider.session", provider.session);
const session = provider.session;
const accounts = session.namespaces[Object.keys(session.namespaces)[0]].accounts;
const [namespace, chainId] = accounts[0].split(":");
const caipChainId = `${namespace}:${chainId}`;
setAccounts(accounts);
setSession(session);
setChain(caipChainId);
onSessionConnected(session!);
}
},
[session, onSessionConnected],
);
useEffect(() => {
loadChainData();
}, []);
useEffect(() => {
if (!client) {
createClient();
}
}, [client, createClient]);
useEffect(() => {
if (cosmosProvider && web3Modal) _subscribeToProviderEvents(cosmosProvider);
}, [_subscribeToProviderEvents, cosmosProvider, web3Modal]);
useEffect(() => {
const getPersistedSession = async () => {
if (cosmosProvider && !hasCheckedPersistedSession) {
await _checkForPersistedSession(cosmosProvider);
setHasCheckedPersistedSession(true);
}
};
getPersistedSession();
}, [cosmosProvider, _checkForPersistedSession, hasCheckedPersistedSession]);
const value = useMemo(
() => ({
pairings,
isInitializing,
balances,
accounts,
chain,
client,
session,
disconnect,
chainData,
onEnable,
cosmosProvider,
}),
[
pairings,
isInitializing,
balances,
accounts,
chain,
client,
session,
disconnect,
chainData,
onEnable,
cosmosProvider,
],
);
return (
<ClientContext.Provider
value={{
...value,
}}
>
{children}
</ClientContext.Provider>
);
}
export function useWalletConnectClient() {
const context = useContext(ClientContext);
if (context === undefined) {
throw new Error("useWalletConnectClient must be used within a ClientContextProvider");
}
return context;
}
@@ -1,45 +0,0 @@
import axios, { AxiosInstance } from "axios";
import { AssetData, GasPrices, ParsedTx } from "./types";
const ethereumApi: AxiosInstance = axios.create({
baseURL: "https://ethereum-api.xyz",
timeout: 30000, // 30 secs
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
});
export async function apiGetAccountAssets(address: string, chainId: string): Promise<AssetData[]> {
const ethChainId = chainId.split(":")[1];
const response = await ethereumApi.get(
`/account-assets?address=${address}&chainId=${ethChainId}`,
);
const { result } = response.data;
return result;
}
export async function apiGetAccountTransactions(
address: string,
chainId: string,
): Promise<ParsedTx[]> {
const ethChainId = chainId.split(":")[1];
const response = await ethereumApi.get(
`/account-transactions?address=${address}&chainId=${ethChainId}`,
);
const { result } = response.data;
return result;
}
export const apiGetAccountNonce = async (address: string, chainId: string): Promise<number> => {
const ethChainId = chainId.split(":")[1];
const response = await ethereumApi.get(`/account-nonce?address=${address}&chainId=${ethChainId}`);
const { result } = response.data;
return result;
};
export const apiGetGasPrices = async (): Promise<GasPrices> => {
const response = await ethereumApi.get(`/gas-prices`);
const { result } = response.data;
return result;
};
@@ -1,55 +0,0 @@
import { Contract, providers, utils } from "ethers";
const spec = {
magicValue: "0x1626ba7e",
abi: [
{
constant: true,
inputs: [
{
name: "_hash",
type: "bytes32",
},
{
name: "_sig",
type: "bytes",
},
],
name: "isValidSignature",
outputs: [
{
name: "magicValue",
type: "bytes4",
},
],
payable: false,
stateMutability: "view",
type: "function",
},
],
};
async function isValidSignature(
address: string,
sig: string,
data: string,
provider: providers.Provider,
abi = eip1271.spec.abi,
magicValue = eip1271.spec.magicValue,
): Promise<boolean> {
let returnValue;
try {
returnValue = await new Contract(address, abi, provider).isValidSignature(
utils.arrayify(data),
sig,
);
} catch (e) {
return false;
}
return returnValue.toLowerCase() === magicValue.toLowerCase();
}
export const eip1271 = {
spec,
isValidSignature,
};
@@ -1,50 +0,0 @@
const example = {
types: {
EIP712Domain: [
{ name: "name", type: "string" },
{ name: "version", type: "string" },
{ name: "verifyingContract", type: "address" },
],
RelayRequest: [
{ name: "target", type: "address" },
{ name: "encodedFunction", type: "bytes" },
{ name: "gasData", type: "GasData" },
{ name: "relayData", type: "RelayData" },
],
GasData: [
{ name: "gasLimit", type: "uint256" },
{ name: "gasPrice", type: "uint256" },
{ name: "pctRelayFee", type: "uint256" },
{ name: "baseRelayFee", type: "uint256" },
],
RelayData: [
{ name: "senderAddress", type: "address" },
{ name: "senderNonce", type: "uint256" },
{ name: "relayWorker", type: "address" },
{ name: "paymaster", type: "address" },
],
},
domain: {
name: "GSN Relayed Transaction",
version: "1",
chainId: 42,
verifyingContract: "0x6453D37248Ab2C16eBd1A8f782a2CBC65860E60B",
},
primaryType: "RelayRequest",
message: {
target: "0x9cf40ef3d1622efe270fe6fe720585b4be4eeeff",
encodedFunction:
"0xa9059cbb0000000000000000000000002e0d94754b348d208d64d52d78bcd443afa9fa520000000000000000000000000000000000000000000000000000000000000007",
gasData: { gasLimit: "39507", gasPrice: "1700000000", pctRelayFee: "70", baseRelayFee: "0" },
relayData: {
senderAddress: "0x22d491bde2303f2f43325b2108d26f1eaba1e32b",
senderNonce: "3",
relayWorker: "0x3baee457ad824c94bd3953183d725847d023a2cf",
paymaster: "0x957F270d45e9Ceca5c5af2b49f1b5dC1Abb0421c",
},
},
};
export const eip712 = {
example,
};
@@ -1,6 +0,0 @@
export * from "./api";
export * from "./eip712";
export * from "./eip1271";
export * from "./tx";
export * from "./types";
export * from "./utilities";
@@ -1,35 +0,0 @@
import * as encoding from "@walletconnect/encoding";
import { apiGetAccountNonce, apiGetGasPrices } from "./api";
import { toWad } from "./utilities";
export async function getGasPrice(chainId: string): Promise<string> {
if (chainId === "eip155:1") return toWad("20", 9).toHexString();
const gasPrices = await apiGetGasPrices();
return toWad(`${gasPrices.slow.price}`, 9).toHexString();
}
export async function formatTestTransaction(account: string) {
const [namespace, reference, address] = account.split(":");
const chainId = `${namespace}:${reference}`;
// nonce
const _nonce = await apiGetAccountNonce(address, chainId);
const nonce = encoding.sanitizeHex(encoding.numberToHex(_nonce));
// gasPrice
const _gasPrice = await getGasPrice(chainId);
const gasPrice = encoding.sanitizeHex(_gasPrice);
// gasLimit
const _gasLimit = 21000;
const gasLimit = encoding.sanitizeHex(encoding.numberToHex(_gasLimit));
// value
const _value = 0;
const value = encoding.sanitizeHex(encoding.numberToHex(_value));
const tx = { from: address, to: address, data: "0x", nonce, gasPrice, gasLimit, value };
return tx;
}
@@ -1,158 +0,0 @@
import { ChainsMap } from "caip-api";
export interface AssetData {
account: string;
symbol: string;
balance: string;
contractAddress?: string;
}
export interface ChainData {
name: string;
short_name: string;
chain: string;
network: string;
chain_id: number;
network_id: number;
rpc_url: string;
native_currency: AssetData;
}
export interface TxData {
from: string;
to: string;
nonce: string;
gasPrice: string;
gasLimit: string;
value: string;
data: string;
}
export interface BlockScoutTx {
value: string;
txreceipt_status: string;
transactionIndex: string;
to: string;
timeStamp: string;
nonce: string;
isError: string;
input: string;
hash: string;
gasUsed: string;
gasPrice: string;
gas: string;
from: string;
cumulativeGasUsed: string;
contractAddress: string;
confirmations: string;
blockNumber: string;
blockHash: string;
}
export interface BlockScoutTokenTx {
value: string;
transactionIndex: string;
tokenSymbol: string;
tokenName: string;
tokenDecimal: string;
to: string;
timeStamp: string;
nonce: string;
input: string;
hash: string;
gasUsed: string;
gasPrice: string;
gas: string;
from: string;
cumulativeGasUsed: string;
contractAddress: string;
confirmations: string;
blockNumber: string;
blockHash: string;
}
export interface ParsedTx {
timestamp: string;
hash: string;
from: string;
to: string;
nonce: string;
gasPrice: string;
gasUsed: string;
fee: string;
value: string;
input: string;
error: boolean;
asset: AssetData;
operations: TxOperation[];
}
export interface TxOperation {
asset: AssetData;
value: string;
from: string;
to: string;
functionName: string;
}
export interface GasPricesResponse {
fastWait: number;
avgWait: number;
blockNum: number;
fast: number;
fastest: number;
fastestWait: number;
safeLow: number;
safeLowWait: number;
speed: number;
block_time: number;
average: number;
}
export interface GasPrice {
time: number;
price: number;
}
export interface GasPrices {
timestamp: number;
slow: GasPrice;
average: GasPrice;
fast: GasPrice;
}
export interface MethodArgument {
type: string;
}
export interface Method {
signature: string;
name: string;
args: MethodArgument[];
}
export interface ChainRequestRender {
label: string;
value: string;
}
export interface ChainMetadata {
name?: string;
logo: string;
rgb: string;
}
export interface NamespaceMetadata {
[reference: string]: ChainMetadata;
}
export interface ChainNamespaces {
[namespace: string]: ChainsMap;
}
export interface AccountAction {
method: string;
callback: (account: string) => Promise<void>;
}
export interface AccountBalances {
[account: string]: AssetData;
}
@@ -1,212 +0,0 @@
import { BigNumber, BigNumberish, providers, utils } from "ethers";
import * as encoding from "@walletconnect/encoding";
import { TypedDataUtils } from "eth-sig-util";
import * as ethUtil from "ethereumjs-util";
import { eip1271 } from "./eip1271";
import { DEFAULT_CHAINS } from "../constants";
export function capitalize(string: string): string {
return string
.split(" ")
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
.join(" ");
}
export function ellipseText(text = "", maxLength = 9999): string {
if (text.length <= maxLength) {
return text;
}
const _maxLength = maxLength - 3;
let ellipse = false;
let currentLength = 0;
const result =
text
.split(" ")
.filter(word => {
currentLength += word.length;
if (ellipse || currentLength >= _maxLength) {
ellipse = true;
return false;
} else {
return true;
}
})
.join(" ") + "...";
return result;
}
export function ellipseAddress(address = "", width = 10): string {
return `${address.slice(0, width)}...${address.slice(-width)}`;
}
export function getDataString(func: string, arrVals: any[]): string {
let val = "";
for (let i = 0; i < arrVals.length; i++) {
val += encoding.padLeft(arrVals[i], 64);
}
const data = func + val;
return data;
}
export function isMobile(): boolean {
let mobile = false;
function hasTouchEvent(): boolean {
try {
document.createEvent("TouchEvent");
return true;
} catch (e) {
return false;
}
}
function hasMobileUserAgent(): boolean {
if (
/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(
navigator.userAgent,
) ||
/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55\/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene|gf-5|g-mo|go(.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk\/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|-|m3|m5)|tx-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i.test(
navigator.userAgent.substr(0, 4),
)
) {
return true;
} else if (hasTouchEvent()) {
return true;
}
return false;
}
mobile = hasMobileUserAgent();
return mobile;
}
export function encodePersonalMessage(msg: string): string {
const data = encoding.utf8ToBuffer(msg);
const buf = Buffer.concat([
Buffer.from("\u0019Ethereum Signed Message:\n" + data.length.toString(), "utf8"),
data,
]);
return ethUtil.bufferToHex(buf);
}
export function hashPersonalMessage(msg: string): string {
const data = encodePersonalMessage(msg);
const buf = ethUtil.toBuffer(data);
const hash = ethUtil.keccak256(buf);
return ethUtil.bufferToHex(hash);
}
export function encodeTypedDataMessage(msg: string): string {
const data = TypedDataUtils.sanitizeData(JSON.parse(msg));
const buf = Buffer.concat([
Buffer.from("1901", "hex"),
TypedDataUtils.hashStruct("EIP712Domain", data.domain, data.types),
TypedDataUtils.hashStruct(data.primaryType as string, data.message, data.types),
]);
return ethUtil.bufferToHex(buf);
}
export function hashTypedDataMessage(msg: string): string {
const data = encodeTypedDataMessage(msg);
const buf = ethUtil.toBuffer(data);
const hash = ethUtil.keccak256(buf);
return ethUtil.bufferToHex(hash);
}
export function recoverAddress(sig: string, hash: string): string {
const params = ethUtil.fromRpcSig(sig);
const result = ethUtil.ecrecover(ethUtil.toBuffer(hash), params.v, params.r, params.s);
const signer = ethUtil.bufferToHex(ethUtil.publicToAddress(result));
return signer;
}
export function recoverPersonalSignature(sig: string, msg: string): string {
const hash = hashPersonalMessage(msg);
const signer = recoverAddress(sig, hash);
return signer;
}
export function recoverTypedMessage(sig: string, msg: string): string {
const hash = hashTypedDataMessage(msg);
const signer = recoverAddress(sig, hash);
return signer;
}
export async function verifySignature(
address: string,
sig: string,
hash: string,
rpcUrl: string,
): Promise<boolean> {
const provider = new providers.JsonRpcProvider(rpcUrl);
const bytecode = await provider.getCode(address);
if (!bytecode || bytecode === "0x" || bytecode === "0x0" || bytecode === "0x00") {
const signer = recoverAddress(sig, hash);
return signer.toLowerCase() === address.toLowerCase();
} else {
return eip1271.isValidSignature(address, sig, hash, provider);
}
}
export function convertHexToNumber(hex: string) {
try {
return encoding.hexToNumber(hex);
} catch (e) {
return hex;
}
}
export function convertHexToUtf8(hex: string) {
try {
return encoding.hexToUtf8(hex);
} catch (e) {
return hex;
}
}
export const sanitizeDecimals = (value: string, decimals = 18): string => {
const [integer, fractional] = value.split(".");
const _fractional = fractional
? fractional.substring(0, decimals).replace(/0+$/gi, "")
: undefined;
return _fractional ? [integer, _fractional].join(".") : integer;
};
export const toWad = (amount: string, decimals = 18): BigNumber => {
return utils.parseUnits(sanitizeDecimals(amount, decimals), decimals);
};
export const fromWad = (wad: BigNumberish, decimals = 18): string => {
return sanitizeDecimals(utils.formatUnits(wad, decimals), decimals);
};
export const LOCALSTORAGE_KEY_TESTNET = "TESTNET";
export const INITIAL_STATE_TESTNET_DEFAULT = true;
export function setLocaleStorageTestnetFlag(value: boolean): void {
window.localStorage.setItem(LOCALSTORAGE_KEY_TESTNET, `${value}`);
}
export function getLocalStorageTestnetFlag(): boolean {
let value = INITIAL_STATE_TESTNET_DEFAULT;
const persisted = window.localStorage.getItem(LOCALSTORAGE_KEY_TESTNET);
if (!persisted) {
setLocaleStorageTestnetFlag(value);
} else {
value = persisted === "true" ? true : false;
}
return value;
}
export const getAllChainNamespaces = () => {
const namespaces: string[] = [];
DEFAULT_CHAINS.forEach(chainId => {
const [namespace] = chainId.split(":");
if (!namespaces.includes(namespace)) {
namespaces.push(namespace);
}
});
return namespaces;
};
@@ -1,35 +0,0 @@
import * as React from "react";
import { PairingTypes } from "@walletconnect/types";
import Button from "../components/Button";
import Pairing from "../components/Pairing";
import { STable } from "../components/shared";
import { SModalContainer, SModalTitle } from "./shared";
interface PairingModalProps {
pairings: PairingTypes.Struct[];
connect: (pairing?: { topic: string }) => Promise<void>;
}
const PairingModal = (props: PairingModalProps) => {
const { pairings, connect } = props;
return (
<SModalContainer>
<SModalTitle>{"Select available pairing or create new one"}</SModalTitle>
<STable>
{pairings.map(pairing => (
<Pairing
key={pairing.topic}
pairing={pairing}
onClick={() => connect({ topic: pairing.topic })}
/>
))}
</STable>
<Button onClick={() => connect()}>{`New Pairing`}</Button>
</SModalContainer>
);
};
export default PairingModal;
@@ -1,39 +0,0 @@
import * as React from "react";
import Loader from "../components/Loader";
import { SContainer } from "../components/shared";
import { SModalContainer, SModalTitle } from "./shared";
interface PingModalProps {
pending: boolean;
result: any;
}
const PingModal = (props: PingModalProps) => {
const { pending, result } = props;
return (
<>
{pending ? (
<SModalContainer>
<SModalTitle>{"Pending Session Ping"}</SModalTitle>
<SContainer>
<Loader />
</SContainer>
</SModalContainer>
) : result ? (
<SModalContainer>
<SModalTitle>
{result.valid ? "Successful Session Ping" : "Failed Session Ping"}
</SModalTitle>
</SModalContainer>
) : (
<SModalContainer>
<SModalTitle>{"Unknown Error with Session Ping"}</SModalTitle>
</SModalContainer>
)}
</>
);
};
export default PingModal;
@@ -1,48 +0,0 @@
import * as React from "react";
import Loader from "../components/Loader";
import { SContainer, STable, SRow, SKey, SValue } from "../components/shared";
import { SModalContainer, SModalTitle, SModalParagraph } from "./shared";
interface RequestModalProps {
pending: boolean;
result: any;
}
const RequestModal = (props: RequestModalProps) => {
const { pending, result } = props;
return (
<>
{pending ? (
<SModalContainer>
<SModalTitle>{"Pending JSON-RPC Request"}</SModalTitle>
<SContainer>
<Loader />
<SModalParagraph>{"Approve or reject request using your wallet"}</SModalParagraph>
</SContainer>
</SModalContainer>
) : result ? (
<SModalContainer>
<SModalTitle>
{result.valid ? "JSON-RPC Request Approved" : "JSON-RPC Request Failed"}
</SModalTitle>
<STable>
{Object.keys(result).map(key => (
<SRow key={key}>
<SKey>{key}</SKey>
<SValue>{result[key].toString()}</SValue>
</SRow>
))}
</STable>
</SModalContainer>
) : (
<SModalContainer>
<SModalTitle>{"JSON-RPC Request Rejected"}</SModalTitle>
</SModalContainer>
)}
</>
);
};
export default RequestModal;
@@ -1,17 +0,0 @@
import styled from "styled-components";
export const SModalContainer = styled.div`
width: 100%;
position: relative;
word-wrap: break-word;
`;
export const SModalTitle = styled.div`
margin: 1em 0;
font-size: 20px;
font-weight: 700;
`;
export const SModalParagraph = styled.p`
margin-top: 30px;
`;
@@ -1,3 +0,0 @@
export default function FourOhFour() {
return <h1>404 Page Not Found</h1>;
}
@@ -1,24 +0,0 @@
import "../styles/globals.css";
import type { AppProps } from "next/app";
import { createGlobalStyle } from "styled-components";
import { ClientContextProvider } from "../contexts/ClientContext";
import Metadata from "../components/Metadata";
import { globalStyle } from "./../styles";
const GlobalStyle = createGlobalStyle`
${globalStyle}
`;
function MyApp({ Component, pageProps }: AppProps) {
return (
<>
<Metadata />
<GlobalStyle />
<ClientContextProvider>
<Component {...pageProps} />
</ClientContextProvider>
</>
);
}
export default MyApp;
@@ -1,3 +0,0 @@
export default function Error() {
return <div>An error as occured</div>;
}
@@ -1,274 +0,0 @@
import React, { useState } from "react";
import { RELAYER_SDK_VERSION as version } from "@walletconnect/core";
import {
formatDirectSignDoc,
stringifySignDocValues,
verifyAminoSignature,
verifyDirectSignature,
} from "cosmos-wallet";
import Banner from "./../components/Banner";
import Blockchain from "./../components/Blockchain";
import Column from "./../components/Column";
import Header from "./../components/Header";
import Modal from "./../components/Modal";
import { DEFAULT_MAIN_CHAINS } from "./../constants";
import { AccountAction } from "./../helpers";
import RequestModal from "./../modals/RequestModal";
import PingModal from "./../modals/PingModal";
import {
SAccounts,
SAccountsContainer,
SButtonContainer,
SContent,
SLanding,
SLayout,
} from "./../components/app";
import { useWalletConnectClient } from "./../contexts/ClientContext";
interface IFormattedRpcResponse {
method?: string;
address?: string;
valid?: boolean;
result: string;
}
interface CosmosRpcResponse {
pub_key: {
type: string;
value: string;
};
signature: string;
}
export default function App() {
const [isRpcRequestPending, setIsRpcRequestPending] = useState(false);
const [rpcResult, setRpcResult] = useState<IFormattedRpcResponse | null>();
const [modal, setModal] = useState("");
const closeModal = () => setModal("");
const openPingModal = () => setModal("ping");
const openRequestModal = () => setModal("request");
// Initialize the WalletConnect client.
const {
client,
session,
disconnect,
chain,
accounts,
balances,
chainData,
isInitializing,
onEnable,
cosmosProvider,
} = useWalletConnectClient();
const ping = async () => {
if (typeof client === "undefined") {
throw new Error("WalletConnect Client is not initialized");
}
try {
setIsRpcRequestPending(true);
const session = cosmosProvider?.session;
if (!session) return;
await cosmosProvider.client?.ping({ topic: session.topic! });
setRpcResult({
address: "",
method: "ping",
valid: true,
result: "success",
});
} catch (error) {
console.error("RPC request failed:", error);
} finally {
setIsRpcRequestPending(false);
}
};
const onPing = async () => {
openPingModal();
await ping();
};
const testSignDirect: (account: string) => Promise<IFormattedRpcResponse> = async account => {
if (!cosmosProvider) {
throw new Error("cosmosProvider not connected");
}
// test direct sign doc inputs
const inputs = {
fee: [{ amount: "2000", denom: "ucosm" }],
pubkey: "AgSEjOuOr991QlHCORRmdE5ahVKeyBrmtgoYepCpQGOW",
gasLimit: 200000,
accountNumber: 1,
sequence: 1,
bodyBytes:
"0a90010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412700a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d120731323334353637",
authInfoBytes:
"0a500a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a21034f04181eeba35391b858633a765c4a0c189697b40d216354d50890d350c7029012040a020801180112130a0d0a0575636f736d12043230303010c09a0c",
};
// format sign doc
const signDoc = formatDirectSignDoc(
inputs.fee,
inputs.pubkey,
inputs.gasLimit,
inputs.accountNumber,
inputs.sequence,
inputs.bodyBytes,
"cosmoshub-4",
);
const address = account.split(":").pop();
if (!address) {
throw new Error(`Could not derive address from account: ${account}`);
}
// cosmos_signDirect params
const params = {
signerAddress: address,
signDoc: stringifySignDocValues(signDoc),
};
const result = await cosmosProvider.request<CosmosRpcResponse>({
method: "cosmos_signDirect",
params,
});
const valid = await verifyDirectSignature(address, result.signature, signDoc);
return {
method: "cosmos_signDirect",
address,
valid,
result: result.signature,
};
};
const testSignAmino: (account: string) => Promise<IFormattedRpcResponse> = async account => {
if (!cosmosProvider) {
throw new Error("cosmosProvider not connected");
}
// test amino sign doc
const signDoc = {
msgs: [],
fee: { amount: [], gas: "23" },
chain_id: "foochain",
memo: "hello, world",
account_number: "7",
sequence: "54",
};
const address = account.split(":").pop();
if (!address) {
throw new Error(`Could not derive address from account: ${account}`);
}
// cosmos_signAmino params
const params = { signerAddress: address, signDoc };
const result = await cosmosProvider.request<CosmosRpcResponse>({
method: "cosmos_signAmino",
params,
});
const valid = await verifyAminoSignature(address, result.signature, signDoc);
return {
method: "cosmos_signAmino",
address,
valid,
result: result.signature,
};
};
const getCosmosActions = (): AccountAction[] => {
const wrapRpcRequest =
(rpcRequest: (account: string) => Promise<IFormattedRpcResponse>) =>
async (account: string) => {
openRequestModal();
try {
setIsRpcRequestPending(true);
const result = await rpcRequest(account);
setRpcResult(result);
} catch (error) {
console.error("RPC request failed:", error);
setRpcResult({ result: (error as Error).message as string });
} finally {
setIsRpcRequestPending(false);
}
};
return [
{ method: "cosmos_signDirect", callback: wrapRpcRequest(testSignDirect) },
{ method: "cosmos_signAmino", callback: wrapRpcRequest(testSignAmino) },
];
};
// Renders the appropriate model for the given request that is currently in-flight.
const renderModal = () => {
switch (modal) {
case "request":
return <RequestModal pending={isRpcRequestPending} result={rpcResult} />;
case "ping":
return <PingModal pending={isRpcRequestPending} result={rpcResult} />;
default:
return null;
}
};
const renderContent = () => {
const chainOptions = DEFAULT_MAIN_CHAINS;
return !accounts.length && !Object.keys(balances).length ? (
<SLanding center>
<Banner />
<h6>
<span>{`Using v${version}`}</span>
</h6>
<SButtonContainer>
<h6>Select Cosmos chain:</h6>
{chainOptions.map(chainId => (
<Blockchain key={chainId} chainId={chainId} chainData={chainData} onClick={onEnable} />
))}
</SButtonContainer>
</SLanding>
) : (
<SAccountsContainer>
<h3>Account</h3>
<SAccounts>
{accounts.map(account => {
return (
<Blockchain
key={account}
active={true}
chainData={chainData}
address={account}
chainId={chain}
balances={balances}
actions={getCosmosActions()}
/>
);
})}
</SAccounts>
</SAccountsContainer>
);
};
return (
<SLayout>
<Column maxWidth={1000} spanHeight>
<Header ping={onPing} disconnect={disconnect} session={session} />
<SContent>{isInitializing ? "Loading..." : renderContent()}</SContent>
</Column>
<Modal show={!!modal} closeModal={closeModal}>
{renderModal()}
</Modal>
</SLayout>
);
}

Some files were not shown because too many files have changed in this diff Show More