diff --git a/.env.example b/.env.example index 6a227c9be..0b3870195 100644 --- a/.env.example +++ b/.env.example @@ -9,6 +9,7 @@ NUXT_PUBLIC_NETWORK_NAME=MAINNET NUXT_PUBLIC_NETWORK_ID=ae_mainnet NUXT_PUBLIC_ALTERNATIVE_NETWORK_URL=http://localhost:8081 NUXT_PUBLIC_ALTERNATIVE_NETWORK_NAME=TESTNET +NUXT_PUBLIC_NETWORK_ID="ae_mainnet" NUXT_PUBLIC_AE_TOKEN_ID=ct_J3zBY8xxjsRr3QojETNw48Eb38fjvEuJKkQ6KzECvubvEcvCa NUXT_PUBLIC_DEBUG_MODE=false NUXT_PUBLIC_SH_DEX_CONTRACTS='ct_2mfj3FoZxnhkSw5RZMcP8BfPoB1QR4QiYGNCdkAvLZ1zfF6paW;ct_azbNZ1XrPjXfqBqbAh1ffLNTQ1sbnuUDFvJrXjYz7JQA1saQ3' diff --git a/nuxt.config.ts b/nuxt.config.ts index c57b8e5cc..9cfb7e054 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -33,6 +33,7 @@ export default defineNuxtConfig({ WEBSOCKET_URL: process.env.WEBSOCKET_URL, DEX_BACKEND_URL: process.env.DEX_BACKEND_URL, NETWORK_NAME: process.env.NETWORK_NAME, + NETWORK_ID: process.env.NETWORK_ID, ALTERNATIVE_NETWORK_URL: process.env.ALTERNATIVE_NETWORK_URL, ALTERNATIVE_NETWORK_NAME: process.env.ALTERNATIVE_NETWORK_NAME, AE_TOKEN_ID: process.env.AE_TOKEN_ID, @@ -41,6 +42,7 @@ export default defineNuxtConfig({ SH_DEX_CONTRACTS: process.env.SH_DEX_CONTRACTS?.split(';'), }, }, + postcss: { plugins: { autoprefixer: {}, diff --git a/package.json b/package.json index ac6e8db69..bb5beb429 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,6 @@ }, "dependencies": { "@aeternity/aepp-sdk": "^13.2.2", - "@download/blockies": "^1.0.3", "@vuepic/vue-datepicker": "^7.1.0", "@sentry/tracing": "^7.88.0", "@sentry/vue": "^7.88.0", diff --git a/src/components/AppButton.vue b/src/components/AppButton.vue index ad0838cec..8ccebd661 100644 --- a/src/components/AppButton.vue +++ b/src/components/AppButton.vue @@ -47,15 +47,6 @@ defineProps({ type: String, default: null, }, - target: { - type: String, - default: '_blank', - }, - size: { - type: String, - default: null, - validator: val => ['lg'].includes(val), - }, variant: { type: String, default: 'primary', @@ -66,6 +57,15 @@ defineProps({ 'light'] .includes(val), }, + target: { + type: String, + default: '_blank', + }, + size: { + type: String, + default: null, + validator: val => ['lg'].includes(val), + }, type: { type: String, default: 'button', diff --git a/src/components/AppDropdown.vue b/src/components/AppDropdown.vue new file mode 100644 index 000000000..131faec77 --- /dev/null +++ b/src/components/AppDropdown.vue @@ -0,0 +1,19 @@ + + + diff --git a/src/components/AppIdenticon.vue b/src/components/AppIdenticon.vue index d34a72599..055dd6aad 100644 --- a/src/components/AppIdenticon.vue +++ b/src/components/AppIdenticon.vue @@ -1,34 +1,20 @@ diff --git a/src/components/TheHeader.vue b/src/components/TheHeader.vue index 462bcff12..f880f71d9 100644 --- a/src/components/TheHeader.vue +++ b/src/components/TheHeader.vue @@ -36,12 +36,15 @@ :class="[ 'header__network-select', { 'header__network-select--open': isNavigationOpen }]"/> + + + + diff --git a/src/components/TokenSymbolIcon.vue b/src/components/TokenSymbolIcon.vue index adb5f7b85..1054c0566 100644 --- a/src/components/TokenSymbolIcon.vue +++ b/src/components/TokenSymbolIcon.vue @@ -11,7 +11,7 @@ + :hash="contractId"/> + + diff --git a/src/components/WalletConnectButton.vue b/src/components/WalletConnectButton.vue new file mode 100644 index 000000000..21ae569be --- /dev/null +++ b/src/components/WalletConnectButton.vue @@ -0,0 +1,12 @@ + + + diff --git a/src/components/WalletConnectionPanel.vue b/src/components/WalletConnectionPanel.vue new file mode 100644 index 000000000..4220cfec6 --- /dev/null +++ b/src/components/WalletConnectionPanel.vue @@ -0,0 +1,95 @@ + + + + + diff --git a/src/pages/accounts/[id].vue b/src/pages/accounts/[id].vue index 3baad5568..a34ee4a9a 100644 --- a/src/pages/accounts/[id].vue +++ b/src/pages/accounts/[id].vue @@ -43,14 +43,6 @@ import { storeToRefs } from 'pinia' import { useRoute, useRouter } from 'nuxt/app' import { useAccountStore } from '@/stores/accountDetails' -import AppTabs from '@/components/AppTabs' -import AppTab from '@/components/AppTab' -import AccountActivitiesPanel from '@/components/AccountActivitiesPanel' -import AccountTransactionsPanel from '@/components/AccountTransactionsPanel' -import AccountNamesPanel from '@/components/AccountNamesPanel' -import AccountTokensPanel from '@/components/AccountTokensPanel' -import PageHeader from '@/components/PageHeader' -import AccountDetailsPanel from '@/components/AccountDetailsPanel' import { accountHints } from '@/utils/hints/accountHints' import { isDesktop } from '@/utils/screen' diff --git a/src/pages/wallet/index.vue b/src/pages/wallet/index.vue new file mode 100644 index 000000000..1949be699 --- /dev/null +++ b/src/pages/wallet/index.vue @@ -0,0 +1,35 @@ + + + diff --git a/src/server/plugins/response-headers.js b/src/server/plugins/response-headers.js index a8e4058f6..3acac7cbb 100644 --- a/src/server/plugins/response-headers.js +++ b/src/server/plugins/response-headers.js @@ -4,11 +4,11 @@ const DEFAULT_HEADERS = { 'X-XSS-Protection': '1; mode=block', 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains; preload', 'Cache-control': 'no-cache', - 'Content-Security-Policy': 'default-src \'self\' *; font-src \'self\' data:; img-src \'self\' data:; script-src \'self\' \'unsafe-inline\'; style-src \'self\' \'unsafe-inline\'; frame-src \'self\'; upgrade-insecure-requests; block-all-mixed-content', + 'Content-Security-Policy': 'default-src \'self\' *; font-src \'self\' data:; img-src * data:; script-src \'self\' \'unsafe-inline\'; style-src \'self\' \'unsafe-inline\'; frame-src \'self\'; upgrade-insecure-requests; block-all-mixed-content', 'Permissions-Policy': 'camera=(), geolocation=(), microphone=()', 'Referrer-Policy': 'strict-origin-when-cross-origin', 'X-Permitted-Cross-Domain-Policies': 'none', - 'Cross-Origin-Embedder-Policy': 'require-corp', + 'Cross-Origin-Embedder-Policy': 'unsafe-none', 'Cross-Origin-Opener-Policy': 'same-origin', 'Cross-Origin-Resource-Policy': 'cross-origin', } diff --git a/src/stores/aesdk.js b/src/stores/aesdk.js index e8bf50cde..cea50c380 100644 --- a/src/stores/aesdk.js +++ b/src/stores/aesdk.js @@ -1,6 +1,6 @@ import { defineStore } from 'pinia' import { useRuntimeConfig } from 'nuxt/app' -import { Node, AeSdk } from '@aeternity/aepp-sdk' +import { AeSdk, Node } from '@aeternity/aepp-sdk' export const useAesdk = defineStore('aesdk', () => { const { NODE_URL } = useRuntimeConfig().public diff --git a/src/stores/wallet.js b/src/stores/wallet.js new file mode 100644 index 000000000..289d6d330 --- /dev/null +++ b/src/stores/wallet.js @@ -0,0 +1,84 @@ +import { defineStore } from 'pinia' +import { useRuntimeConfig } from 'nuxt/app' +import { AeSdkAepp, BrowserWindowMessageConnection, Node, walletDetector } from '@aeternity/aepp-sdk' + +export const useWalletStore = defineStore('wallet', () => { + const { NODE_URL, NETWORK_ID } = useRuntimeConfig().public + + const aeSdk = ref(null) + const detectedWallets = ref(null) + const status = ref(null) + + async function initWallet() { + try { + const aeSdkOptions = { + nodes: [{ + name: NETWORK_ID, + instance: new Node(NODE_URL), + }], + compilerUrl: 'https://compiler.aepps.com', + } + + aeSdk.value = shallowReactive(new AeSdkAepp({ + name: 'æScan', + ...aeSdkOptions, + onNetworkChange({ networkId }) { + aeSdk.value.selectNode(networkId) + }, + onDisconnect() { + status.value = 'disconnecting' + }, + })) + await connect() + } catch (error) { + status.value = 'failed' + throw error + } + } + + async function scanWallets() { + status.value = 'detecting' + + detectedWallets.value = await new Promise(resolve => { + const timeout = setTimeout(() => { + resolve(undefined) + status.value = 'not detected' + disconnect() + }, 10000) + + function setDetected({ newWallet }) { + stopScan() + resolve(newWallet) + clearTimeout(timeout) + status.value = 'detected' + } + + const stopScan = walletDetector(new BrowserWindowMessageConnection(), setDetected) + }) + } + + async function connect() { + status.value = 'connecting' + try { + await aeSdk.value.connectToWallet(detectedWallets.value.getConnection()) + await aeSdk.value.subscribeAddress('subscribe', 'current') + status.value = 'connected' + } catch (error) { + disconnect() + status.value = 'denied' + } + } + + function disconnect() { + aeSdk.value.disconnectWallet() + } + + return { + detectedWallets, + status, + scanWallets, + initWallet, + disconnect, + aeSdk, + } +}) diff --git a/src/utils/hints/walletHints.js b/src/utils/hints/walletHints.js new file mode 100644 index 000000000..ef7e743fe --- /dev/null +++ b/src/utils/hints/walletHints.js @@ -0,0 +1,3 @@ +export const walletHints = { + wallet: 'A detail view of a wallet connected to æScan.', +} diff --git a/yarn.lock b/yarn.lock index e05a0afe1..4caaa56ff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -531,10 +531,10 @@ resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-3.0.1.tgz#d84597fbc0f897240c12fc0a31e492b036c70e40" integrity sha512-NPljRHkq4a14YzZ3YD406uaxh7s0g6eAq3L9aLOWywoqe8PkYamAvtsh7KNX6c++ihDrJ0RiU+/z7rGnhlZ5ww== -"@download/blockies@^1.0.3": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@download/blockies/-/blockies-1.0.3.tgz#9aea770f9de72f3f947f1b3a53ee1e92f8dc4a68" - integrity sha512-iGDh2M6pFuXg9kyW+U//963LKylSLFpLG5hZvUppCjhkiDwsYquQPyamxCQlLASYySS3gGKAki2eWG9qIHKCew== +"@esbuild/aix-ppc64@0.19.10": + version "0.19.10" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.19.10.tgz#fb3922a0183d27446de00cf60d4f7baaadf98d84" + integrity sha512-Q+mk96KJ+FZ30h9fsJl+67IjNJm3x2eX+GBWGmocAKgzp27cowCOOqSdscX80s0SpdFXZnIv/+1xD1EctFx96Q== "@esbuild/aix-ppc64@0.19.10": version "0.19.10" @@ -2403,6 +2403,7 @@ resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.3.13.tgz#4cb73cda958d77ffd389c8640cf7d93a10ac676f" integrity sha512-/zYUwiHD8j7gKx2argXEMCUXVST6q/21DFU0sTfNX0URJroCe3b1UF6vLJ3lQDfLNIiiRl2ONp7Nh5UVWS6QnA== + "@vuepic/vue-datepicker@^7.1.0": version "7.4.0" resolved "https://registry.yarnpkg.com/@vuepic/vue-datepicker/-/vue-datepicker-7.4.0.tgz#e0f5f8d28e996f81d69cffb8e43b8b24bcaf9c83"