From 1a377d5f715243836e1ebf3dec7ea4cc3db5ad9e Mon Sep 17 00:00:00 2001
From: Martin Kaintas
Date: Mon, 27 May 2024 15:01:37 +0300
Subject: [PATCH 01/55] fix: custom networks are testnet type
---
src/protocols/bitcoin/libs/BitcoinAdapter.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/protocols/bitcoin/libs/BitcoinAdapter.ts b/src/protocols/bitcoin/libs/BitcoinAdapter.ts
index f5f2390a12..fee0d5081d 100644
--- a/src/protocols/bitcoin/libs/BitcoinAdapter.ts
+++ b/src/protocols/bitcoin/libs/BitcoinAdapter.ts
@@ -180,8 +180,8 @@ export class BitcoinAdapter extends BaseProtocolAdapter {
): IHdWalletAccount {
const { activeNetwork } = useNetworks();
- const network = networks[activeNetwork.value.type as keyof typeof networks] || networks.bitcoin;
- const pathCoinType = activeNetwork.value.type === NETWORK_TYPE_TESTNET ? 1 : 0;
+ const network = networks[activeNetwork.value.type === NETWORK_TYPE_MAINNET ? 'bitcoin' : 'testnet'];
+ const pathCoinType = activeNetwork.value.type === NETWORK_TYPE_MAINNET ? 0 : 1;
const node = this.bip32.fromSeed(Buffer.from(seed));
const path = `m/84'/${pathCoinType}'/${accountIndex}'/0/0`; // 84 for Native-SegWit and 44 for Legacy
From b69cb4a7848dabb68e74791012efe22d316cc78d Mon Sep 17 00:00:00 2001
From: Martin Kaintas
Date: Mon, 27 May 2024 13:54:50 +0300
Subject: [PATCH 02/55] fix: reset input after generating gift card
---
package-lock.json | 159 ++++++++++++++++++-------------------
package.json | 6 +-
src/popup/pages/Invite.vue | 9 ++-
3 files changed, 87 insertions(+), 87 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index d3e3f057c9..d4c62aba36 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -40,8 +40,8 @@
"@ionic/vue-router": "^7.1.2",
"@rushstack/eslint-patch": "^1.3.2",
"@trapezedev/configure": "^7.0.10",
- "@vee-validate/i18n": "^4.8.6",
- "@vee-validate/rules": "^4.8.6",
+ "@vee-validate/i18n": "^4.12.8",
+ "@vee-validate/rules": "^4.12.8",
"@vue/vue3-jest": "^27.0.0",
"bignumber.js": "^9.0.2",
"bip32": "^4.0.0",
@@ -63,7 +63,7 @@
"tweetnacl": "^1.0.3",
"uuid": "^9.0.1",
"validator": "^13.9.0",
- "vee-validate": "^4.8.6",
+ "vee-validate": "^4.12.8",
"vue": "^3.3.2",
"vue-i18n": "^9.2.2",
"vue-loader": "^17.2.2",
@@ -6445,16 +6445,18 @@
"integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ=="
},
"node_modules/@vee-validate/i18n": {
- "version": "4.12.6",
- "resolved": "https://registry.npmjs.org/@vee-validate/i18n/-/i18n-4.12.6.tgz",
- "integrity": "sha512-K3xbjLIQQwDrllS18Ka5nyQDIF/1FuHnedXTr6bfmTm44/7C4MyMGo9KDahnrMWTcFkYIKR7XMshM3R6o54MFg=="
+ "version": "4.12.8",
+ "resolved": "https://registry.npmjs.org/@vee-validate/i18n/-/i18n-4.12.8.tgz",
+ "integrity": "sha512-Jl63ZUICt15GIDi+GIcdPRVIVcLJcVMApTnSprfs53BllXnxbq/A3QNo9wCbQk4bP3L/S3NcPoLBFEH6PQU65g==",
+ "license": "MIT"
},
"node_modules/@vee-validate/rules": {
- "version": "4.12.6",
- "resolved": "https://registry.npmjs.org/@vee-validate/rules/-/rules-4.12.6.tgz",
- "integrity": "sha512-vXhunbllildEbCWaBI2m6mij7U4pDTeoqX9CQ7/0gVMhtkTZh+QhKsDSaGI2evatECINH2cpLOBaMkubdN82VQ==",
+ "version": "4.12.8",
+ "resolved": "https://registry.npmjs.org/@vee-validate/rules/-/rules-4.12.8.tgz",
+ "integrity": "sha512-RisPzo+jGt0B0QTVhyZK5Pxb0Q6TESnS2rbR+CE3HRtJ+z0q5rkqkSIwvKrTKJFZpowJIoAHYS02jm5Pty5C3Q==",
+ "license": "MIT",
"dependencies": {
- "vee-validate": "4.12.6"
+ "vee-validate": "4.12.8"
}
},
"node_modules/@vue/babel-helper-vue-jsx-merge-props": {
@@ -7672,49 +7674,49 @@
"dev": true
},
"node_modules/@vue/compiler-core": {
- "version": "3.4.21",
- "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.21.tgz",
- "integrity": "sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==",
+ "version": "3.4.27",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.27.tgz",
+ "integrity": "sha512-E+RyqY24KnyDXsCuQrI+mlcdW3ALND6U7Gqa/+bVwbcpcR3BRRIckFoz7Qyd4TTlnugtwuI7YgjbvsLmxb+yvg==",
"dependencies": {
- "@babel/parser": "^7.23.9",
- "@vue/shared": "3.4.21",
+ "@babel/parser": "^7.24.4",
+ "@vue/shared": "3.4.27",
"entities": "^4.5.0",
"estree-walker": "^2.0.2",
- "source-map-js": "^1.0.2"
+ "source-map-js": "^1.2.0"
}
},
"node_modules/@vue/compiler-dom": {
- "version": "3.4.21",
- "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.21.tgz",
- "integrity": "sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==",
+ "version": "3.4.27",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.27.tgz",
+ "integrity": "sha512-kUTvochG/oVgE1w5ViSr3KUBh9X7CWirebA3bezTbB5ZKBQZwR2Mwj9uoSKRMFcz4gSMzzLXBPD6KpCLb9nvWw==",
"dependencies": {
- "@vue/compiler-core": "3.4.21",
- "@vue/shared": "3.4.21"
+ "@vue/compiler-core": "3.4.27",
+ "@vue/shared": "3.4.27"
}
},
"node_modules/@vue/compiler-sfc": {
- "version": "3.4.21",
- "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.21.tgz",
- "integrity": "sha512-me7epoTxYlY+2CUM7hy9PCDdpMPfIwrOvAXud2Upk10g4YLv9UBW7kL798TvMeDhPthkZ0CONNrK2GoeI1ODiQ==",
+ "version": "3.4.27",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.27.tgz",
+ "integrity": "sha512-nDwntUEADssW8e0rrmE0+OrONwmRlegDA1pD6QhVeXxjIytV03yDqTey9SBDiALsvAd5U4ZrEKbMyVXhX6mCGA==",
"dependencies": {
- "@babel/parser": "^7.23.9",
- "@vue/compiler-core": "3.4.21",
- "@vue/compiler-dom": "3.4.21",
- "@vue/compiler-ssr": "3.4.21",
- "@vue/shared": "3.4.21",
+ "@babel/parser": "^7.24.4",
+ "@vue/compiler-core": "3.4.27",
+ "@vue/compiler-dom": "3.4.27",
+ "@vue/compiler-ssr": "3.4.27",
+ "@vue/shared": "3.4.27",
"estree-walker": "^2.0.2",
- "magic-string": "^0.30.7",
- "postcss": "^8.4.35",
- "source-map-js": "^1.0.2"
+ "magic-string": "^0.30.10",
+ "postcss": "^8.4.38",
+ "source-map-js": "^1.2.0"
}
},
"node_modules/@vue/compiler-ssr": {
- "version": "3.4.21",
- "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.21.tgz",
- "integrity": "sha512-M5+9nI2lPpAsgXOGQobnIueVqc9sisBFexh5yMIMRAPYLa7+5wEJs8iqOZc1WAa9WQbx9GR2twgznU8LTIiZ4Q==",
+ "version": "3.4.27",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.27.tgz",
+ "integrity": "sha512-CVRzSJIltzMG5FcidsW0jKNQnNRYC8bT21VegyMMtHmhW3UOI7knmUehzswXLrExDLE6lQCZdrhD4ogI7c+vuw==",
"dependencies": {
- "@vue/compiler-dom": "3.4.21",
- "@vue/shared": "3.4.21"
+ "@vue/compiler-dom": "3.4.27",
+ "@vue/shared": "3.4.27"
}
},
"node_modules/@vue/component-compiler-utils": {
@@ -7847,48 +7849,48 @@
}
},
"node_modules/@vue/reactivity": {
- "version": "3.4.21",
- "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.21.tgz",
- "integrity": "sha512-UhenImdc0L0/4ahGCyEzc/pZNwVgcglGy9HVzJ1Bq2Mm9qXOpP8RyNTjookw/gOCUlXSEtuZ2fUg5nrHcoqJcw==",
+ "version": "3.4.27",
+ "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.27.tgz",
+ "integrity": "sha512-kK0g4NknW6JX2yySLpsm2jlunZJl2/RJGZ0H9ddHdfBVHcNzxmQ0sS0b09ipmBoQpY8JM2KmUw+a6sO8Zo+zIA==",
"dependencies": {
- "@vue/shared": "3.4.21"
+ "@vue/shared": "3.4.27"
}
},
"node_modules/@vue/runtime-core": {
- "version": "3.4.21",
- "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.21.tgz",
- "integrity": "sha512-pQthsuYzE1XcGZznTKn73G0s14eCJcjaLvp3/DKeYWoFacD9glJoqlNBxt3W2c5S40t6CCcpPf+jG01N3ULyrA==",
+ "version": "3.4.27",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.27.tgz",
+ "integrity": "sha512-7aYA9GEbOOdviqVvcuweTLe5Za4qBZkUY7SvET6vE8kyypxVgaT1ixHLg4urtOlrApdgcdgHoTZCUuTGap/5WA==",
"dependencies": {
- "@vue/reactivity": "3.4.21",
- "@vue/shared": "3.4.21"
+ "@vue/reactivity": "3.4.27",
+ "@vue/shared": "3.4.27"
}
},
"node_modules/@vue/runtime-dom": {
- "version": "3.4.21",
- "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.21.tgz",
- "integrity": "sha512-gvf+C9cFpevsQxbkRBS1NpU8CqxKw0ebqMvLwcGQrNpx6gqRDodqKqA+A2VZZpQ9RpK2f9yfg8VbW/EpdFUOJw==",
+ "version": "3.4.27",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.27.tgz",
+ "integrity": "sha512-ScOmP70/3NPM+TW9hvVAz6VWWtZJqkbdf7w6ySsws+EsqtHvkhxaWLecrTorFxsawelM5Ys9FnDEMt6BPBDS0Q==",
"dependencies": {
- "@vue/runtime-core": "3.4.21",
- "@vue/shared": "3.4.21",
+ "@vue/runtime-core": "3.4.27",
+ "@vue/shared": "3.4.27",
"csstype": "^3.1.3"
}
},
"node_modules/@vue/server-renderer": {
- "version": "3.4.21",
- "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.21.tgz",
- "integrity": "sha512-aV1gXyKSN6Rz+6kZ6kr5+Ll14YzmIbeuWe7ryJl5muJ4uwSwY/aStXTixx76TwkZFJLm1aAlA/HSWEJ4EyiMkg==",
+ "version": "3.4.27",
+ "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.27.tgz",
+ "integrity": "sha512-dlAMEuvmeA3rJsOMJ2J1kXU7o7pOxgsNHVr9K8hB3ImIkSuBrIdy0vF66h8gf8Tuinf1TK3mPAz2+2sqyf3KzA==",
"dependencies": {
- "@vue/compiler-ssr": "3.4.21",
- "@vue/shared": "3.4.21"
+ "@vue/compiler-ssr": "3.4.27",
+ "@vue/shared": "3.4.27"
},
"peerDependencies": {
- "vue": "3.4.21"
+ "vue": "3.4.27"
}
},
"node_modules/@vue/shared": {
- "version": "3.4.21",
- "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.21.tgz",
- "integrity": "sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g=="
+ "version": "3.4.27",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.27.tgz",
+ "integrity": "sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA=="
},
"node_modules/@vue/test-utils": {
"version": "2.4.5",
@@ -21556,14 +21558,11 @@
}
},
"node_modules/magic-string": {
- "version": "0.30.9",
- "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.9.tgz",
- "integrity": "sha512-S1+hd+dIrC8EZqKyT9DstTH/0Z+f76kmmvZnkfQVmOpDEF9iVgdYif3Q/pIWHmCoo59bQVGW0kVL3e2nl+9+Sw==",
+ "version": "0.30.10",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz",
+ "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.15"
- },
- "engines": {
- "node": ">=12"
}
},
"node_modules/make-dir": {
@@ -29184,15 +29183,15 @@
}
},
"node_modules/vee-validate": {
- "version": "4.12.6",
- "resolved": "https://registry.npmjs.org/vee-validate/-/vee-validate-4.12.6.tgz",
- "integrity": "sha512-EKM3YHy8t1miPh30d5X6xOrfG/Ctq0nbN4eMpCK7ezvI6T98/S66vswP+ihL4QqAK/k5KqreWOxof09+JG7N/A==",
+ "version": "4.12.8",
+ "resolved": "https://registry.npmjs.org/vee-validate/-/vee-validate-4.12.8.tgz",
+ "integrity": "sha512-A07rm3+y7SRk0CMD/O4nBT0nxtwjyfzGZwjEUDk18SxK0ZMzd4AFCzzdHlIiCE1QgHetxd0I3kVkZdN0GG0Oww==",
"dependencies": {
- "@vue/devtools-api": "^6.5.1",
+ "@vue/devtools-api": "^6.6.1",
"type-fest": "^4.8.3"
},
"peerDependencies": {
- "vue": "^3.3.11"
+ "vue": "^3.4.26"
}
},
"node_modules/vee-validate/node_modules/type-fest": {
@@ -29226,15 +29225,15 @@
"integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ=="
},
"node_modules/vue": {
- "version": "3.4.21",
- "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.21.tgz",
- "integrity": "sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==",
- "dependencies": {
- "@vue/compiler-dom": "3.4.21",
- "@vue/compiler-sfc": "3.4.21",
- "@vue/runtime-dom": "3.4.21",
- "@vue/server-renderer": "3.4.21",
- "@vue/shared": "3.4.21"
+ "version": "3.4.27",
+ "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.27.tgz",
+ "integrity": "sha512-8s/56uK6r01r1icG/aEOHqyMVxd1bkYcSe9j8HcKtr/xTOFWvnzIVTehNW+5Yt89f+DLBe4A569pnZLS5HzAMA==",
+ "dependencies": {
+ "@vue/compiler-dom": "3.4.27",
+ "@vue/compiler-sfc": "3.4.27",
+ "@vue/runtime-dom": "3.4.27",
+ "@vue/server-renderer": "3.4.27",
+ "@vue/shared": "3.4.27"
},
"peerDependencies": {
"typescript": "*"
diff --git a/package.json b/package.json
index 6814667ec5..2ac9a70f85 100644
--- a/package.json
+++ b/package.json
@@ -63,8 +63,8 @@
"@ionic/vue-router": "^7.1.2",
"@rushstack/eslint-patch": "^1.3.2",
"@trapezedev/configure": "^7.0.10",
- "@vee-validate/i18n": "^4.8.6",
- "@vee-validate/rules": "^4.8.6",
+ "@vee-validate/i18n": "^4.12.8",
+ "@vee-validate/rules": "^4.12.8",
"@vue/vue3-jest": "^27.0.0",
"bignumber.js": "^9.0.2",
"bip32": "^4.0.0",
@@ -86,7 +86,7 @@
"tweetnacl": "^1.0.3",
"uuid": "^9.0.1",
"validator": "^13.9.0",
- "vee-validate": "^4.8.6",
+ "vee-validate": "^4.12.8",
"vue": "^3.3.2",
"vue-i18n": "^9.2.2",
"vue-loader": "^17.2.2",
diff --git a/src/popup/pages/Invite.vue b/src/popup/pages/Invite.vue
index 417843b647..9ed87874c8 100644
--- a/src/popup/pages/Invite.vue
+++ b/src/popup/pages/Invite.vue
@@ -16,7 +16,7 @@
{{ $t('pages.invite.generate-link') }}
{{ $t('pages.invite.generate') }}
@@ -121,7 +121,7 @@ export default defineComponent({
const { max, fee } = useMaxAmount({ formModel });
- async function generate() {
+ async function generate(resetField: () => void) {
setLoaderVisible(true);
const { publicKey, secretKey } = generateKeyPair();
@@ -143,7 +143,8 @@ export default defineComponent({
}
addInvite(Buffer.from(secretKey, 'hex').slice(0, 32));
- formModel.value.amount = '';
+ // Field is dirty after submit, so we need to reset it and not just clear the value
+ resetField();
}
return {
From 80fd11a49b50c788bab5a93f627d14fda8724cf5 Mon Sep 17 00:00:00 2001
From: Bartosz
Date: Fri, 24 May 2024 10:24:20 +0200
Subject: [PATCH 03/55] refactor: apps browser types and composables update
---
src/composables/index.ts | 1 +
src/popup/pages/AppsBrowser.vue | 20 ++++++++++----------
2 files changed, 11 insertions(+), 10 deletions(-)
diff --git a/src/composables/index.ts b/src/composables/index.ts
index deac2acbc6..93026e749d 100644
--- a/src/composables/index.ts
+++ b/src/composables/index.ts
@@ -31,3 +31,4 @@ export * from './scrollTransactions';
export * from './languages';
export * from './permissions';
export * from './auth';
+export * from './appsBrowserHistory';
diff --git a/src/popup/pages/AppsBrowser.vue b/src/popup/pages/AppsBrowser.vue
index 16299102ac..3e8f66ffe7 100644
--- a/src/popup/pages/AppsBrowser.vue
+++ b/src/popup/pages/AppsBrowser.vue
@@ -7,7 +7,7 @@
>
@@ -28,7 +28,7 @@
show-message-help
:placeholder="$t('pages.appsBrowser.inputPlaceholder')"
:message="errorMessage"
- @keydown.enter.stop="(event) => handleEnter(event, errorMessage)"
+ @keydown.enter.stop="(event: any) => handleEnter(event, errorMessage)"
>
();
- const iframeRef = ref();
+ const iframeEl = ref();
const customAppURL = ref('');
const currentClientId = ref('');
let shareWalletInfoInterval: any;
@@ -177,13 +177,13 @@ export default defineComponent({
}
async function onAppLoaded() {
- if (!iframeRef.value || !selectedApp.value) return;
+ if (!iframeEl.value || !selectedApp.value) return;
// Don't recreate RpcClient in Safari desktop and iOS webview
// because on these platforms `load` event triggers on anchor navigation
if (IS_SAFARI && currentClientId.value) return;
await removeRpcClientIfAny();
const sdk = await getAeSdk();
- const target = iframeRef.value.contentWindow;
+ const target = iframeEl.value.contentWindow!;
const connection = new BrowserWindowMessageConnection({ target });
currentClientId.value = sdk.addRpcClient(connection);
const app = selectedApp.value;
@@ -209,7 +209,7 @@ export default defineComponent({
}
function refresh() {
- if (iframeRef.value && selectedApp.value) {
+ if (iframeEl.value && selectedApp.value) {
setLocalStorageItem([LOCAL_STORAGE_ITEM], selectedApp.value);
window.location.reload();
}
@@ -259,7 +259,7 @@ export default defineComponent({
return {
refresh,
- iframeRef,
+ iframeEl,
customAppURL,
DAPPS_LIST,
selectedApp,
From 07cb2e1d8d5bb9b0bbc22dbe53863ebf9fe900d7 Mon Sep 17 00:00:00 2001
From: Bartosz
Date: Tue, 21 May 2024 14:48:44 +0200
Subject: [PATCH 04/55] feat: eth chain id numeric instead of hex
---
src/popup/locales/en.json | 2 +-
src/popup/plugins/veeValidate.ts | 19 ++++++-------------
src/protocols/ethereum/config.ts | 5 +++--
.../ethereum/libs/EthereumAdapter.ts | 8 ++++----
src/protocols/ethereum/types.ts | 3 ++-
src/utils/common.ts | 4 ++++
6 files changed, 20 insertions(+), 21 deletions(-)
diff --git a/src/popup/locales/en.json b/src/popup/locales/en.json
index e4e797bcff..af4d4f4c2b 100644
--- a/src/popup/locales/en.json
+++ b/src/popup/locales/en.json
@@ -1238,6 +1238,7 @@
"required": "This field is required",
"url": "Invalid URL",
"address": "Enter valid blockchain address or .chain name",
+ "numeric": "The value should be a number",
"name": "Enter valid .chain name",
"nameRegisteredAddress": "Invalid address or .chain name",
"nameUnregistered": "This name has already been registered.",
@@ -1250,7 +1251,6 @@
"enoughAeSigner": "Insufficient balance on your signing account to propose a transaction",
"maxLength": "The value should be no longer than {0} characters",
"maxValue": "This field must be {0} or less",
- "hexFormat": "The value should be in hex format",
"maxRedeem": "Maximum redeemable amount is {0} AE.",
"maxValueVault": "Amount is exceeding the vault's balance: {0}",
"notToken": "Tokens are currently not allowed, please switch to AE",
diff --git a/src/popup/plugins/veeValidate.ts b/src/popup/plugins/veeValidate.ts
index eff959dcb5..87b5442f4e 100644
--- a/src/popup/plugins/veeValidate.ts
+++ b/src/popup/plugins/veeValidate.ts
@@ -1,5 +1,5 @@
import { defineRule } from 'vee-validate';
-import { required } from '@vee-validate/rules';
+import { numeric, required } from '@vee-validate/rules';
import BigNumber from 'bignumber.js';
import { debounce } from 'lodash-es';
import { isAddressValid, isNameValid } from '@aeternity/aepp-sdk';
@@ -32,6 +32,11 @@ defineRule(
(value: string) => !value || isUrlValid(value) || tg('validation.url'),
);
+defineRule(
+ 'numeric',
+ (value: string) => numeric(value) || tg('validation.numeric'),
+);
+
defineRule(
'account',
(value: string) => isAddressValid(value) || isNameValid(value) || tg('validation.address'),
@@ -109,18 +114,6 @@ defineRule(
},
);
-defineRule(
- 'is_hex_format',
- (value: string) => (
- (
- value?.toString()?.startsWith('0x')
- && value.length >= 3
- && parseInt(value.slice(2), 16).toString(16) === value.slice(2).toLowerCase()
- )
- || tg('validation.hexFormat')
- ),
-);
-
export default () => {
const { balance, updateBalances } = useBalances();
const { currencyRates } = useCurrencies({ pollingDisabled: true });
diff --git a/src/protocols/ethereum/config.ts b/src/protocols/ethereum/config.ts
index 8d6e7a9336..8bcb0e3303 100644
--- a/src/protocols/ethereum/config.ts
+++ b/src/protocols/ethereum/config.ts
@@ -14,6 +14,7 @@ export const ETH_COIN_PRECISION = 18; // Amount of decimals
export const ETH_COINGECKO_COIN_ID = 'ethereum';
export const ETH_GAS_LIMIT = 21000;
+export const ETH_CHAIN_NAMESPACE = 'eip155';
/**
* Estimated time we need to wait for the middleware (etherscan) to sync it's state
@@ -70,11 +71,11 @@ export const ETH_SAFE_CONFIRMATION_COUNT = 12;
export const ETH_NETWORK_DEFAULT_SETTINGS: IDefaultNetworkTypeData = {
[NETWORK_TYPE_MAINNET]: {
nodeUrl: 'https://ethereum-rpc.publicnode.com', // TODO replace temp values - use our own node
- chainId: '0x1',
+ chainId: '1',
},
[NETWORK_TYPE_TESTNET]: {
nodeUrl: 'https://ethereum-sepolia-rpc.publicnode.com', // TODO replace temp values - use our own node
- chainId: '0xaa36a7',
+ chainId: '11155111',
},
};
diff --git a/src/protocols/ethereum/libs/EthereumAdapter.ts b/src/protocols/ethereum/libs/EthereumAdapter.ts
index a9e5a57fc1..0ee26d6f28 100644
--- a/src/protocols/ethereum/libs/EthereumAdapter.ts
+++ b/src/protocols/ethereum/libs/EthereumAdapter.ts
@@ -41,7 +41,7 @@ import type {
NetworkTypeDefault,
} from '@/types';
import { PROTOCOLS } from '@/constants';
-import { getLastNotEmptyAccountIndex } from '@/utils';
+import { getLastNotEmptyAccountIndex, toHex } from '@/utils';
import Logger from '@/lib/logger';
import { BaseProtocolAdapter } from '@/protocols/BaseProtocolAdapter';
import { tg } from '@/popup/plugins/i18n';
@@ -100,7 +100,7 @@ export class EthereumAdapter extends BaseProtocolAdapter {
defaultValue: ETH_NETWORK_DEFAULT_ENV_SETTINGS.chainId,
validationRules: {
url: false,
- is_hex_format: true,
+ numeric: true,
},
getPlaceholder: () => tg('pages.network.chainIdPlaceholder'),
getLabel: () => tg('pages.network.chainIdLabel'),
@@ -296,7 +296,7 @@ export class EthereumAdapter extends BaseProtocolAdapter {
// All values are in wei
const txData: FeeMarketEIP1559TxData = {
- chainId,
+ chainId: toHex(chainId),
nonce,
to: contractId,
data: contract.methods.transfer(recipient, hexAmount).encodeABI(),
@@ -435,7 +435,7 @@ export class EthereumAdapter extends BaseProtocolAdapter {
// All values are in wei
const txData: FeeMarketEIP1559TxData = {
- chainId,
+ chainId: toHex(chainId),
nonce,
to: recipient,
value: hexAmount,
diff --git a/src/protocols/ethereum/types.ts b/src/protocols/ethereum/types.ts
index 266576c9cd..705d0e28c8 100644
--- a/src/protocols/ethereum/types.ts
+++ b/src/protocols/ethereum/types.ts
@@ -3,7 +3,8 @@ import { INetworkProtocolSettings } from '@/types';
/**
* Settings specific to this protocol.
*/
-export type EthNetworkProtocolSettings = 'chainId';
+export type EthNetworkProtocolSettings =
+ | 'chainId'; // ref: https://docs.simplehash.com/reference/supported-chains-testnets
/**
* Settings that are not editable by the user but are assigned to specific network types.
diff --git a/src/utils/common.ts b/src/utils/common.ts
index f7c1253a82..39ab1023d9 100644
--- a/src/utils/common.ts
+++ b/src/utils/common.ts
@@ -392,6 +392,10 @@ export function splitAddress(address: string | null): string {
: '';
}
+export function toHex(value: string) {
+ return `0x${parseInt(value, 10).toString(16)}`;
+}
+
export function toShiftedBigNumber(value: number | string, precision: number): BigNumberPublic {
return new BigNumber(value).shiftedBy(precision);
}
From 2687b0e49e0c767a73f725d1f4dc5c16562cf099 Mon Sep 17 00:00:00 2001
From: Martin Kaintas
Date: Tue, 28 May 2024 15:07:59 +0300
Subject: [PATCH 05/55] fix: improve input error message & throttle balance
updating
---
src/popup/locales/en.json | 2 +-
src/popup/plugins/veeValidate.ts | 8 +++++---
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/src/popup/locales/en.json b/src/popup/locales/en.json
index af4d4f4c2b..33466f9327 100644
--- a/src/popup/locales/en.json
+++ b/src/popup/locales/en.json
@@ -1247,7 +1247,7 @@
"addressGeneric": "Invalid {protocol} address",
"minValue": "This field must be {0} or more",
"minValueExclusive": "This field must be more than {0}",
- "enoughCoin": "Not enough {0} balance to pay transaction fee",
+ "enoughCoin": "{0} balance is not enough to pay for transaction fee",
"enoughAeSigner": "Insufficient balance on your signing account to propose a transaction",
"maxLength": "The value should be no longer than {0} characters",
"maxValue": "This field must be {0} or less",
diff --git a/src/popup/plugins/veeValidate.ts b/src/popup/plugins/veeValidate.ts
index 87b5442f4e..4c348b6789 100644
--- a/src/popup/plugins/veeValidate.ts
+++ b/src/popup/plugins/veeValidate.ts
@@ -1,7 +1,7 @@
import { defineRule } from 'vee-validate';
import { numeric, required } from '@vee-validate/rules';
import BigNumber from 'bignumber.js';
-import { debounce } from 'lodash-es';
+import { debounce, throttle } from 'lodash-es';
import { isAddressValid, isNameValid } from '@aeternity/aepp-sdk';
import { NameEntry } from '@aeternity/aepp-sdk/es/apis/node';
import {
@@ -126,6 +126,8 @@ export default () => {
NOT_SAME: Symbol('name state: not same as provided'),
};
+ const updateBalancesThrottled = throttle(updateBalances, 1000, { trailing: false });
+
const checkNameDebounced = debounce(
async (name, expectedNameState, comparedAddress, { resolve, reject }) => {
try {
@@ -240,7 +242,7 @@ export default () => {
defineRule(
'enough_coin',
async (value: string, [amount, coinSymbol]: [number, string]) => {
- await updateBalances(); // TODO add debounce to avoid firing to often
+ await updateBalancesThrottled();
return balance.value.isGreaterThanOrEqualTo(amount) || tg('validation.enoughCoin', [coinSymbol || AE_SYMBOL]);
},
);
@@ -248,7 +250,7 @@ export default () => {
defineRule(
'enough_ae_signer',
async (value: string, [arg]: [number]) => {
- await updateBalances(); // TODO add debounce to avoid firing to often
+ await updateBalancesThrottled();
return balance.value.isGreaterThanOrEqualTo(arg) || tg('validation.enoughAeSigner');
},
);
From 38ecb52162d62b259c8b65cd727002f6ad7273ca Mon Sep 17 00:00:00 2001
From: Martin Kaintas
Date: Tue, 28 May 2024 15:35:27 +0300
Subject: [PATCH 06/55] refactor: css imports
---
src/popup/App.vue | 22 +++++-----
src/popup/components/AccordionItem.vue | 2 +-
src/popup/components/AccountCardAdd.vue | 4 +-
src/popup/components/AccountCardBase.vue | 2 +-
src/popup/components/AccountCardConsensus.vue | 6 +--
src/popup/components/AccountCardMultisig.vue | 4 +-
.../components/AccountCardTotalTokens.vue | 6 +--
src/popup/components/AccountDetailsBase.vue | 20 +++++-----
.../components/AccountDetailsNavigation.vue | 4 +-
src/popup/components/AccountInfo.vue | 8 ++--
src/popup/components/AccountItem.vue | 4 +-
src/popup/components/AccountSelector.vue | 2 +-
src/popup/components/AccountSwiper.vue | 4 +-
src/popup/components/AddressTruncated.vue | 2 +-
.../AppsBrowser/AppsBrowserHeader.vue | 8 ++--
src/popup/components/AssetIcon.vue | 2 +-
src/popup/components/Assets/AssetListItem.vue | 6 +--
src/popup/components/Assets/DetailsRow.vue | 8 ++--
src/popup/components/AuctionCard.vue | 6 +--
src/popup/components/AuctionOverview.vue | 6 +--
src/popup/components/AuthorizedAccounts.vue | 4 +-
src/popup/components/Avatar.vue | 4 +-
src/popup/components/AvatarWithChainName.vue | 10 ++---
src/popup/components/Badge.vue | 4 +-
src/popup/components/BalanceInfo.vue | 6 +--
src/popup/components/BulletSwitcher.vue | 10 ++---
src/popup/components/Card.vue | 6 +--
src/popup/components/CardMnemonic.vue | 2 +-
src/popup/components/CheckBox.vue | 20 +++++-----
.../components/ConsensusApprovedLabel.vue | 2 +-
src/popup/components/CopyText.vue | 8 ++--
src/popup/components/DashboardBase.vue | 2 +-
src/popup/components/DetailsItem.vue | 4 +-
src/popup/components/DialogBox.vue | 4 +-
src/popup/components/Filters.vue | 16 ++++----
src/popup/components/Header.vue | 16 ++++----
src/popup/components/IconBoxed.vue | 2 +-
src/popup/components/InfoBox.vue | 20 +++++-----
src/popup/components/InputAmount.vue | 10 ++---
src/popup/components/InputField.vue | 40 +++++++++----------
src/popup/components/InputSelectAsset.vue | 16 ++++----
src/popup/components/InviteItem.vue | 12 +++---
src/popup/components/LinkButton.vue | 22 +++++-----
src/popup/components/ListItemWrapper.vue | 4 +-
src/popup/components/Loader.vue | 10 ++---
src/popup/components/MainBalance.vue | 4 +-
src/popup/components/MessageOffline.vue | 4 +-
src/popup/components/Modal.vue | 26 ++++++------
src/popup/components/ModalHeader.vue | 8 ++--
src/popup/components/Modals/AccountImport.vue | 6 +--
src/popup/components/Modals/AssetSelector.vue | 6 +--
src/popup/components/Modals/ClaimGiftCard.vue | 10 ++---
src/popup/components/Modals/ClaimSuccess.vue | 4 +-
.../components/Modals/ConfirmRawSign.vue | 8 ++--
.../Modals/ConfirmTransactionSign.vue | 16 ++++----
.../components/Modals/ConfirmUnsafeSign.vue | 8 ++--
src/popup/components/Modals/ConsensusInfo.vue | 2 +-
src/popup/components/Modals/Default.vue | 4 +-
.../components/Modals/EnableSecureLogin.vue | 6 +--
src/popup/components/Modals/ErrorLog.vue | 4 +-
.../Modals/MultisigProposalConfirmActions.vue | 12 +++---
.../components/Modals/MultisigVaultCreate.vue | 26 ++++++------
src/popup/components/Modals/QrCodeReader.vue | 6 +--
.../components/Modals/RecipientHelper.vue | 8 ++--
src/popup/components/Modals/RecipientInfo.vue | 10 ++---
.../components/Modals/ResetWalletModal.vue | 12 +++---
.../components/Modals/TransferReceiveBase.vue | 16 ++++----
.../components/Modals/WarningDappBrowser.vue | 8 ++--
.../components/MultisigProposalConsensus.vue | 14 +++----
.../MultisigVaultCreateProgress.vue | 18 ++++-----
.../components/MultisigVaultCreateReview.vue | 4 +-
src/popup/components/NameItem.vue | 32 +++++++--------
src/popup/components/NameRow.vue | 6 +--
src/popup/components/NetworkButton.vue | 8 ++--
src/popup/components/NodeConnectionStatus.vue | 14 +++----
src/popup/components/NotificationItem.vue | 16 ++++----
src/popup/components/Panel.vue | 4 +-
src/popup/components/PanelItem.vue | 4 +-
src/popup/components/PayloadDetails.vue | 6 +--
.../PendingMultisigTransactionCard.vue | 6 +--
src/popup/components/PlatformIcon.vue | 2 +-
src/popup/components/Platforms.vue | 10 ++---
src/popup/components/ProgressBar.vue | 2 +-
.../components/QrCodeReaderMobileOverlay.vue | 2 +-
src/popup/components/RadioButton.vue | 8 ++--
src/popup/components/RegisterName.vue | 6 +--
src/popup/components/Scrollable.vue | 2 +-
src/popup/components/SeedPhraseBadge.vue | 18 ++++-----
.../components/SeedPhraseNotification.vue | 16 ++++----
src/popup/components/SelectableListItem.vue | 2 +-
src/popup/components/StatusIcon.vue | 10 ++---
src/popup/components/SwapRates.vue | 4 +-
src/popup/components/SwapRoute.vue | 14 +++----
src/popup/components/SwitchButton.vue | 16 ++++----
src/popup/components/ToggleMultisigButton.vue | 6 +--
src/popup/components/TokenAmount.vue | 10 ++---
src/popup/components/Tokens.vue | 10 ++---
src/popup/components/Tooltip.vue | 2 +-
src/popup/components/TotalWalletAmount.vue | 6 +--
src/popup/components/TransactionAssetRows.vue | 8 ++--
.../components/TransactionDetailsBase.vue | 14 +++----
.../TransactionDetailsPoolTokenRow.vue | 12 +++---
.../TransactionDetailsPoolTokens.vue | 6 +--
.../components/TransactionErrorStatus.vue | 12 +++---
src/popup/components/TransactionInfo.vue | 14 +++----
.../TransactionInfoDetailsParty.vue | 4 +-
src/popup/components/TransactionLabel.vue | 16 ++++----
src/popup/components/TransactionList.vue | 8 ++--
src/popup/components/TransactionListItem.vue | 8 ++--
src/popup/components/TransactionTag.vue | 8 ++--
src/popup/components/TransactionTagList.vue | 2 +-
src/popup/components/UrlStatus.vue | 14 +++----
src/popup/components/buttons/BtnBox.vue | 6 +--
src/popup/components/buttons/BtnHelp.vue | 4 +-
src/popup/components/buttons/BtnIcon.vue | 4 +-
src/popup/components/buttons/BtnMain.vue | 4 +-
src/popup/components/buttons/BtnMaxAmount.vue | 10 ++---
src/popup/components/buttons/BtnPill.vue | 20 +++++-----
src/popup/components/buttons/BtnPlain.vue | 2 +-
src/popup/components/buttons/BtnSubheader.vue | 6 +--
src/popup/components/buttons/BtnText.vue | 8 ++--
.../components/form/FormNumberSelect.vue | 22 +++++-----
src/popup/components/form/FormSelect.vue | 4 +-
src/popup/components/form/FormTextarea.vue | 6 +--
src/popup/components/tabs/Tab.vue | 22 +++++-----
src/popup/components/tabs/Tabs.vue | 10 ++---
src/popup/pages/About.vue | 2 +-
src/popup/pages/AppsBrowser.vue | 6 +--
src/popup/pages/Assets/AssetDetails.vue | 2 +-
src/popup/pages/Assets/AssetDetailsInfo.vue | 12 +++---
src/popup/pages/CommentNew.vue | 4 +-
src/popup/pages/CurrencySettings.vue | 10 ++---
src/popup/pages/DonateError.vue | 6 +--
src/popup/pages/ErrorLogSettings.vue | 4 +-
src/popup/pages/Index.vue | 22 +++++-----
src/popup/pages/Invite.vue | 6 +--
src/popup/pages/LanguageSettings.vue | 4 +-
src/popup/pages/MultisigDetails.vue | 10 ++---
src/popup/pages/MultisigProposalDetails.vue | 18 ++++-----
src/popup/pages/Names/Auction.vue | 2 +-
src/popup/pages/Names/AuctionBid.vue | 2 +-
src/popup/pages/Names/AuctionHistory.vue | 10 ++---
src/popup/pages/Names/AuctionList.vue | 8 ++--
src/popup/pages/Names/Claim.vue | 6 +--
src/popup/pages/Names/NamesList.vue | 4 +-
src/popup/pages/NetworkForm.vue | 2 +-
src/popup/pages/Networks.vue | 2 +-
src/popup/pages/NotFound.vue | 2 +-
src/popup/pages/NotificationSettings.vue | 4 +-
src/popup/pages/Notifications.vue | 4 +-
src/popup/pages/PermissionManager.vue | 8 ++--
src/popup/pages/PermissionsSettings.vue | 4 +-
src/popup/pages/Popups/AccountList.vue | 4 +-
src/popup/pages/Popups/Connect.vue | 14 +++----
src/popup/pages/Popups/MessageSign.vue | 6 +--
src/popup/pages/PrivacyPolicy.vue | 6 +--
src/popup/pages/ResetWallet.vue | 4 +-
src/popup/pages/Retip.vue | 6 +--
src/popup/pages/SecureLoginSettings.vue | 6 +--
src/popup/pages/SeedPhraseDetailsSettings.vue | 4 +-
src/popup/pages/SeedPhraseSettings.vue | 6 +--
src/popup/pages/SeedPhraseVerifySettings.vue | 6 +--
src/popup/pages/TermsOfService.vue | 6 +--
src/popup/pages/TipsClaim.vue | 4 +-
src/popup/popup.ts | 2 +-
.../components/TransferReviewTip.vue | 14 +++----
.../aeternity/components/TransferSendForm.vue | 8 ++--
.../aeternity/views/AccountDetails.vue | 4 +-
.../aeternity/views/TransactionDetails.vue | 1 -
.../bitcoin/views/AccountDetails.vue | 4 +-
.../ethereum/views/AccountDetails.vue | 4 +-
171 files changed, 699 insertions(+), 700 deletions(-)
diff --git a/src/popup/App.vue b/src/popup/App.vue
index 80f765d767..56b210e806 100644
--- a/src/popup/App.vue
+++ b/src/popup/App.vue
@@ -284,9 +284,9 @@ export default defineComponent({
diff --git a/src/popup/components/AuctionCard.vue b/src/popup/components/AuctionCard.vue
index 3a149a3c4e..6f22984b46 100644
--- a/src/popup/components/AuctionCard.vue
+++ b/src/popup/components/AuctionCard.vue
@@ -29,8 +29,8 @@ export default {
diff --git a/src/popup/components/InputAmount.vue b/src/popup/components/InputAmount.vue
index eb647737d6..583dd4bece 100644
--- a/src/popup/components/InputAmount.vue
+++ b/src/popup/components/InputAmount.vue
@@ -150,9 +150,9 @@ export default defineComponent({
diff --git a/src/popup/components/NotificationItem.vue b/src/popup/components/NotificationItem.vue
index 7ebd56ca6c..b0d229c7be 100644
--- a/src/popup/components/NotificationItem.vue
+++ b/src/popup/components/NotificationItem.vue
@@ -174,9 +174,9 @@ export default defineComponent({
diff --git a/src/popup/components/SwapRates.vue b/src/popup/components/SwapRates.vue
index 95c7b2df6c..a0ba95e2e0 100644
--- a/src/popup/components/SwapRates.vue
+++ b/src/popup/components/SwapRates.vue
@@ -125,8 +125,8 @@ export default defineComponent({
diff --git a/src/popup/components/buttons/BtnPill.vue b/src/popup/components/buttons/BtnPill.vue
index cd8b9e3a01..b356f8fdb8 100644
--- a/src/popup/components/buttons/BtnPill.vue
+++ b/src/popup/components/buttons/BtnPill.vue
@@ -27,9 +27,9 @@ export default defineComponent({
diff --git a/src/popup/pages/About.vue b/src/popup/pages/About.vue
index 0e685891e5..f6248e161f 100644
--- a/src/popup/pages/About.vue
+++ b/src/popup/pages/About.vue
@@ -135,7 +135,7 @@ export default defineComponent({
diff --git a/src/protocols/aeternity/views/TransactionDetails.vue b/src/protocols/aeternity/views/TransactionDetails.vue
index 6c6d4f8770..994fbcec29 100644
--- a/src/protocols/aeternity/views/TransactionDetails.vue
+++ b/src/protocols/aeternity/views/TransactionDetails.vue
@@ -406,7 +406,6 @@ export default defineComponent({
diff --git a/src/protocols/ethereum/views/AccountDetails.vue b/src/protocols/ethereum/views/AccountDetails.vue
index cd3ea37d69..8287d3e559 100644
--- a/src/protocols/ethereum/views/AccountDetails.vue
+++ b/src/protocols/ethereum/views/AccountDetails.vue
@@ -48,10 +48,10 @@ export default defineComponent({
From b973c34d8d893d101cd83521f0b55b62f51174e2 Mon Sep 17 00:00:00 2001
From: Bartosz
Date: Wed, 22 May 2024 11:05:14 +0200
Subject: [PATCH 07/55] refactor: transaction data composable arguments as refs
---
src/composables/multisigAccounts.ts | 2 +-
src/composables/transactionData.ts | 112 +++++++------
.../Modals/ConfirmTransactionSign.vue | 149 +++++++-----------
src/popup/components/SwapRates.vue | 34 ++--
src/popup/components/SwapRoute.vue | 35 ++--
.../TransactionDetailsPoolTokens.vue | 23 ++-
src/popup/components/TransactionLabel.vue | 12 +-
src/popup/components/TransactionListItem.vue | 7 +-
src/popup/components/TransactionOverview.vue | 6 +-
src/popup/components/TransactionTagList.vue | 14 +-
src/popup/pages/MultisigProposalDetails.vue | 29 ++--
src/protocols/aeternity/config.ts | 31 +---
src/protocols/aeternity/helpers/index.ts | 44 ++----
.../aeternity/views/TransactionDetails.vue | 56 +++----
.../ethereum/views/TransactionDetails.vue | 24 ++-
src/types/index.ts | 3 +-
16 files changed, 250 insertions(+), 331 deletions(-)
diff --git a/src/composables/multisigAccounts.ts b/src/composables/multisigAccounts.ts
index 7131000a6d..72144a5146 100644
--- a/src/composables/multisigAccounts.ts
+++ b/src/composables/multisigAccounts.ts
@@ -91,7 +91,7 @@ export function useMultisigAccounts({
() => (activeMultisigAccount.value)
? (new AeScan(aeActiveNetworkPredefinedSettings.value.explorerUrl!))
.prepareUrlForHash(activeMultisigAccount.value.contractId)
- : null,
+ : undefined,
);
const isActiveMultisigAccountPending = computed(
diff --git a/src/composables/transactionData.ts b/src/composables/transactionData.ts
index 4451bbe9e8..939d777b46 100644
--- a/src/composables/transactionData.ts
+++ b/src/composables/transactionData.ts
@@ -1,12 +1,10 @@
-import { computed, ref } from 'vue';
+import { computed, Ref } from 'vue';
import { Tag } from '@aeternity/aepp-sdk';
-import { camelCase } from 'lodash-es';
import type {
AccountAddress,
ITokenResolved,
ITransaction,
ObjectValues,
- TxFunctionParsed,
TxFunctionRaw,
TxType,
} from '@/types';
@@ -25,19 +23,19 @@ import {
TX_RETURN_TYPE_OK,
TX_FUNCTIONS,
TX_FUNCTIONS_MULTISIG,
+ TX_FUNCTIONS_TYPE_DEX,
} from '@/protocols/aeternity/config';
import {
getInnerTransaction,
getOwnershipStatus,
+ getTransactionTokenInfoResolver,
getTxDirection,
+ getTxFunctionParsed,
+ getTxFunctionRaw,
getTxOwnerAddress,
getTxTag,
+ isTransactionAex9,
isTxDex,
- isTxFunctionDexAddLiquidity,
- isTxFunctionDexAllowance,
- isTxFunctionDexRemoveLiquidity,
- isTxFunctionDexPool,
- getTransactionTokenInfoResolver,
} from '@/protocols/aeternity/helpers';
import { useFungibleTokens } from '@/composables/fungibleTokens';
import { useAccounts } from './accounts';
@@ -46,8 +44,8 @@ import { useAeSdk } from './aeSdk';
import { useTippingContracts } from './tippingContracts';
interface UseTransactionOptions {
- transaction?: ITransaction;
- externalAddress?: AccountAddress;
+ transaction: Ref;
+ transactionCustomOwner?: Ref;
showDetailedAllowanceInfo?: boolean;
}
@@ -56,22 +54,22 @@ interface UseTransactionOptions {
*/
export function useTransactionData({
transaction,
- externalAddress,
+ transactionCustomOwner,
showDetailedAllowanceInfo = false,
-}: UseTransactionOptions = {}) {
+}: UseTransactionOptions) {
const { dexContracts } = useAeSdk();
const { accounts, activeAccount } = useAccounts();
const { tippingContractAddresses } = useTippingContracts();
const { getProtocolAvailableTokens, getTxAmountTotal, getTxAssetSymbol } = useFungibleTokens();
- const activeTransaction = ref(transaction);
- const ownerAddress = ref(externalAddress);
-
- const outerTx = computed(() => activeTransaction.value?.tx);
+ const protocol = computed(() => transaction.value?.protocol || PROTOCOLS.aeternity);
+ const outerTx = computed(() => transaction.value?.tx);
const innerTx = computed(() => outerTx.value ? getInnerTransaction(outerTx.value) : undefined);
const outerTxTag = computed(() => outerTx.value ? getTxTag(outerTx.value) : null);
const innerTxTag = computed(() => innerTx.value ? getTxTag(innerTx.value) : null);
const txType = computed(() => outerTxTag.value ? Tag[outerTxTag.value] as TxType : null);
+ const txFunctionParsed = computed(() => getTxFunctionParsed(innerTx.value?.function));
+ const txFunctionRaw = computed(() => getTxFunctionRaw(innerTx.value?.function));
/**
* Transaction TX type value converted into human readable label
@@ -105,20 +103,27 @@ export function useTransactionData({
const isDexAllowance = computed((): boolean => (
!!innerTx.value
- && isTxFunctionDexAllowance(innerTx.value?.function)
+ && includes(TX_FUNCTIONS_TYPE_DEX.allowance, txFunctionRaw.value)
&& !!getProtocolAvailableTokens(PROTOCOLS.aeternity)[innerTx.value.contractId]
));
- const isDexAddLiquidity = computed(
- (): boolean => isTxFunctionDexAddLiquidity(innerTx.value?.function),
+ const isDexLiquidityAdd = computed(
+ (): boolean => includes(TX_FUNCTIONS_TYPE_DEX.addLiquidity, txFunctionRaw.value),
);
-
- const isDexRemoveLiquidity = computed(
- (): boolean => isTxFunctionDexRemoveLiquidity(innerTx.value?.function),
+ const isDexLiquidityRemove = computed(
+ (): boolean => includes(TX_FUNCTIONS_TYPE_DEX.removeLiquidity, txFunctionRaw.value),
);
-
const isDexPool = computed(
- (): boolean => isTxFunctionDexPool(innerTx.value?.function),
+ (): boolean => includes(TX_FUNCTIONS_TYPE_DEX.pool, txFunctionRaw.value),
+ );
+ const isDexSwap = computed(
+ (): boolean => includes(TX_FUNCTIONS_TYPE_DEX.swap, txFunctionRaw.value),
+ );
+ const isDexMaxSpent = computed(
+ (): boolean => includes(TX_FUNCTIONS_TYPE_DEX.maxSpent, txFunctionRaw.value),
+ );
+ const isDexMinReceived = computed(
+ (): boolean => includes(TX_FUNCTIONS_TYPE_DEX.minReceived, txFunctionRaw.value),
);
const isMultisig = computed((): boolean => (
@@ -129,6 +134,10 @@ export function useTransactionData({
)
));
+ const isAex9 = computed(
+ () => transaction.value && isTransactionAex9(transaction.value),
+ );
+
const isTip = computed((): boolean => !!(
innerTx.value?.contractId
&& innerTx.value?.function
@@ -162,7 +171,8 @@ export function useTransactionData({
? TX_DIRECTION.received
: getTxDirection(
outerTx.value?.payerId ? outerTx.value : innerTx.value,
- externalAddress
+ transactionCustomOwner?.value
+ || transaction.value?.transactionOwner
|| (
ownershipStatus.value !== AE_TRANSACTION_OWNERSHIP_STATUS.current
&& txOwnerAddress.value
@@ -171,32 +181,37 @@ export function useTransactionData({
),
);
+ /**
+ * Amount and fee calculated based on the direction.
+ */
+ const amountTotal = computed(
+ (): number => (transaction.value) ? getTxAmountTotal(transaction.value, direction.value) : 0,
+ );
+
/**
* List of assets used within the transaction.
* Contains more than one item if the transaction is for example a swapping event.
*/
const transactionAssets = computed((): ITokenResolved[] => {
- if (!activeTransaction.value?.tx) {
+ if (!transaction.value?.tx) {
return [];
}
let convertToCoin = false;
- const { protocol = PROTOCOLS.aeternity } = activeTransaction.value;
- const adapter = ProtocolAdapterFactory.getAdapter(protocol);
- const protocolTokens = getProtocolAvailableTokens(protocol);
+ const adapter = ProtocolAdapterFactory.getAdapter(protocol.value);
+ const protocolTokens = getProtocolAvailableTokens(protocol.value);
// TODO move AE specific logic to adapter and store resolved data in the transactions
- if (protocol === PROTOCOLS.aeternity) {
+ if (protocol.value === PROTOCOLS.aeternity) {
// AE DEX and wrapped AE (WAE)
if (
- innerTx.value?.function
+ isDex.value
+ && txFunctionParsed.value
&& (!isDexAllowance.value || showDetailedAllowanceInfo)
) {
- const functionName = camelCase(innerTx.value.function) as TxFunctionParsed;
- const functionResolver = getTransactionTokenInfoResolver(functionName);
-
- if (functionResolver && isDex.value) {
+ const functionResolver = getTransactionTokenInfoResolver(txFunctionParsed.value);
+ if (functionResolver) {
return functionResolver({ tx: outerTx.value } as ITransaction, protocolTokens)
.tokens
.map(({
@@ -218,8 +233,8 @@ export function useTransactionData({
}
const amount = (isDexAllowance.value)
- ? toShiftedBigNumber(innerTx.value?.fee || 0, -adapter.coinPrecision).toString()
- : getTxAmountTotal(activeTransaction.value, direction.value);
+ ? toShiftedBigNumber(innerTx.value?.fee || 0, -adapter.coinPrecision).toNumber()
+ : amountTotal.value;
const isReceived = direction.value === TX_DIRECTION.received;
if (isTransactionCoin.value || isDexAllowance.value || isMultisig.value || convertToCoin) {
@@ -243,18 +258,10 @@ export function useTransactionData({
isReceived,
name: token?.name,
protocol,
- symbol: getTxAssetSymbol(activeTransaction.value),
+ symbol: getTxAssetSymbol(transaction.value),
}];
});
- function setActiveTransaction(newTransaction: ITransaction) {
- activeTransaction.value = newTransaction;
- }
-
- function setExternalAddress(address: AccountAddress) {
- ownerAddress.value = address;
- }
-
function getOwnershipAddress(externalOwnerAddress?: AccountAddress): AccountAddress {
const { current, subAccount } = AE_TRANSACTION_OWNERSHIP_STATUS;
switch (ownershipStatus.value) {
@@ -272,25 +279,30 @@ export function useTransactionData({
}
return {
+ amountTotal,
outerTxTag,
innerTxTag,
innerTx,
txTypeLabel,
txTypeListLabel,
txFunctionLabel,
+ txFunctionParsed,
+ txFunctionRaw,
+ isAex9,
isErrorTransaction,
isDex,
- isDexAddLiquidity,
isDexAllowance,
+ isDexLiquidityAdd,
+ isDexLiquidityRemove,
+ isDexMaxSpent,
+ isDexMinReceived,
isDexPool,
- isDexRemoveLiquidity,
+ isDexSwap,
isMultisig,
isTip,
isTransactionCoin,
direction,
transactionAssets,
getOwnershipAddress,
- setActiveTransaction,
- setExternalAddress,
};
}
diff --git a/src/popup/components/Modals/ConfirmTransactionSign.vue b/src/popup/components/Modals/ConfirmTransactionSign.vue
index 13d5a76746..44fb804270 100644
--- a/src/popup/components/Modals/ConfirmTransactionSign.vue
+++ b/src/popup/components/Modals/ConfirmTransactionSign.vue
@@ -12,7 +12,7 @@
@@ -96,13 +96,13 @@
({
+ protocol,
+ tx: popupProps.value?.tx,
+ } as ITransaction);
const {
+ amountTotal,
direction,
- isDexAllowance,
+ isAex9,
isDex,
- setActiveTransaction,
- } = useTransactionData({
- transaction: { tx: popupProps.value?.tx } as ITransaction,
- });
+ isDexAllowance,
+ isDexLiquidityAdd,
+ isDexLiquidityRemove,
+ isDexMaxSpent,
+ isDexMinReceived,
+ isDexPool,
+ isDexSwap,
+ } = useTransactionData({ transaction });
- const showAdvanced = ref(false);
const tokenList = ref([]);
- const txFunction = ref();
const executionCost = ref(0);
const loading = ref(false);
const error = ref('');
@@ -287,44 +293,25 @@ export default defineComponent({
() => SUPERHERO_CHAT_URLS
.includes(`${popupProps.value?.app?.protocol}//${popupProps.value?.app?.name}`),
);
- const transactionWrapped = computed(
- (): Partial => ({ tx: popupProps.value?.tx as any }),
- );
- const isSwap = computed(() => isDex.value && isTxFunctionDexSwap(txFunction.value));
- const isPool = computed(() => isDex.value && isTxFunctionDexPool(txFunction.value));
- const isMaxSpent = computed(() => isTxFunctionDexMaxSpent(txFunction.value));
- const isMinReceived = computed(() => isTxFunctionDexMinReceived(txFunction.value));
const txAeFee = computed(() => getAeFee(popupProps.value?.tx?.fee!));
const nameAeFee = computed(() => getAeFee(popupProps.value?.tx?.nameFee!));
const activeAccount = getLastActiveProtocolAccount(PROTOCOLS.aeternity);
- const swapDirection = computed(() => {
- if (isMaxSpent.value) {
- return 'maxSpent';
- }
- if (isMinReceived.value) {
- return 'minReceived';
- }
- return 'total';
- });
-
const swapDirectionTranslation = computed(() => {
- switch (swapDirection.value) {
- case 'maxSpent': return t('pages.signTransaction.maxSpent');
- case 'minReceived': return t('pages.signTransaction.minReceived');
- default: return t('common.total');
+ if (isDexMaxSpent.value) {
+ return t('pages.signTransaction.maxSpent');
+ }
+ if (isDexMinReceived.value) {
+ return t('pages.signTransaction.minReceived');
}
+ return t('common.total');
});
- const totalAmount = computed(
- () => getTxAmountTotal(transactionWrapped.value as ITransaction, direction.value),
- );
-
const singleToken = computed((): ITokenResolved => ({
isReceived: direction.value === TX_DIRECTION.received,
- amount: totalAmount.value,
+ amount: amountTotal.value,
symbol: getTxAssetSymbol(popupProps.value?.tx as any),
}));
@@ -340,7 +327,7 @@ export default defineComponent({
);
const swapTokenAmountData = computed((): ITokenResolved => {
- const token = swapDirection.value === 'maxSpent' ? tokenList.value[0] : tokenList.value[1];
+ const token = (isDexMaxSpent.value) ? tokenList.value[0] : tokenList.value[1];
return token || {};
});
@@ -353,14 +340,6 @@ export default defineComponent({
() => swapTokenAmountData.value.isWrappedCoin ? AE_SYMBOL : swapTokenAmountData.value.symbol,
);
- const completeTransaction = computed(
- () => ({ tx: { ...popupProps.value?.tx, function: txFunction.value } }),
- );
-
- const isProvideLiquidity = computed(
- () => txFunction.value && DEX_TRANSACTION_TAGS[txFunction.value] === DEX_PROVIDE_LIQUIDITY,
- );
-
const transactionArguments = computed(() => decodedCallData.value?.args?.length
? JsonBig.stringify(decodedCallData.value.args)
: undefined);
@@ -378,10 +357,10 @@ export default defineComponent({
{ tx: { ...txParams, ...popupProps.value?.tx } } as ITransaction,
getProtocolAvailableTokens(PROTOCOLS.aeternity),
)?.tokens;
- if (!isPool.value) {
+ if (!isDexPool.value) {
return tokens;
}
- if (isProvideLiquidity.value) {
+ if (isDexLiquidityAdd.value) {
return tokens.filter((token) => !token.isPool);
}
return tokens.reverse();
@@ -396,17 +375,13 @@ export default defineComponent({
if (isDexAllowance.value) {
return t('pages.signTransaction.approveUseOfToken');
}
- if (isSwap.value) {
+ if (isDexSwap.value) {
return !idx ? t('pages.signTransaction.from') : t('pages.signTransaction.to');
}
- if (isPool.value && isProvideLiquidity.value) {
+ if (isDexLiquidityAdd.value) {
return token.isPool ? '' : t('pages.signTransaction.maximumDeposited');
}
- if (
- isPool.value
- && txFunction.value
- && DEX_TRANSACTION_TAGS[txFunction.value] === DEX_REMOVE_LIQUIDITY
- ) {
+ if (isDexLiquidityRemove.value) {
return token.isPool
? t('pages.signTransaction.poolTokenSpent')
: t('pages.signTransaction.minimumWithdrawn');
@@ -482,31 +457,22 @@ export default defineComponent({
if (!decodedCallData.value) return;
- const txParams = {
- function: decodedCallData.value.functionName as TxFunctionRaw,
- arguments: decodedCallData.value.args.map((arg: any) => ({
- type: Array.isArray(arg) ? 'list' : 'any',
- value: Array.isArray(arg) ? arg.map((element) => ({ value: element })) : arg,
- })) as TxArguments[],
- };
- const tx: ITx = { ...txParams, ...popupProps.value.tx };
-
- txFunction.value = txParams.function;
-
- // TODO temporary solution to create transaction boilerplate
- setActiveTransaction({ tx } as ITransaction);
+ transaction.value.tx.function = decodedCallData.value.functionName as TxFunctionRaw;
+ transaction.value.tx.arguments = decodedCallData.value.args.map((arg: any) => ({
+ type: Array.isArray(arg) ? 'list' : 'any',
+ value: Array.isArray(arg) ? arg.map((element) => ({ value: element })) : arg,
+ }));
- const allTokens = getTokens(tx);
+ const allTokens = getTokens(transaction.value.tx);
tokenList.value = allTokens.map((token) => ({
...token,
- tokens: token.isPool && !isProvideLiquidity.value
+ tokens: token.isPool && !isDexLiquidityAdd.value
? allTokens.filter((tkn) => !tkn.isPool).reverse()
: [token],
}));
} catch (e) {
tokenList.value = [];
- txFunction.value = undefined;
} finally {
loading.value = false;
}
@@ -538,7 +504,6 @@ export default defineComponent({
AnimatedSpinner,
PAYLOAD_FIELD,
cancel,
- completeTransaction,
decodedCallData,
decodedPayload,
error,
@@ -548,25 +513,25 @@ export default defineComponent({
getTxKeyLabel,
getTxAssetSymbol,
isAeppChatSuperhero,
+ isAex9,
isDex,
isDexAllowance,
+ isDexMaxSpent,
+ isDexMinReceived,
+ isDexSwap,
isHash,
- isSwap,
- isTransactionAex9,
loading,
nameAeFee,
popupProps,
- showAdvanced,
singleToken,
- swapDirection,
swapDirectionTranslation,
swapTokenAmountData,
tokenAmount,
tokenList,
tokenSymbol,
- totalAmount,
+ amountTotal,
+ transaction,
transactionArguments,
- transactionWrapped,
txAeFee,
verifying,
};
diff --git a/src/popup/components/SwapRates.vue b/src/popup/components/SwapRates.vue
index 95c7b2df6c..ce06136271 100644
--- a/src/popup/components/SwapRates.vue
+++ b/src/popup/components/SwapRates.vue
@@ -39,20 +39,13 @@ import {
computed,
defineComponent,
PropType,
+ toRef,
} from 'vue';
-import { camelCase } from 'lodash-es';
-import type {
- ITransaction,
- TxFunctionParsed,
-} from '@/types';
+import type { ITransaction } from '@/types';
import { PROTOCOLS } from '@/constants';
-import {
- getTransactionTokenInfoResolver,
- isTxFunctionDexSwap,
- isTxFunctionDexPool,
-} from '@/protocols/aeternity/helpers';
-import { useFungibleTokens } from '@/composables';
+import { getTransactionTokenInfoResolver } from '@/protocols/aeternity/helpers';
+import { useFungibleTokens, useTransactionData } from '@/composables';
import DetailsItem from './DetailsItem.vue';
import Tokens from './Tokens.vue';
@@ -70,21 +63,19 @@ export default defineComponent({
setup(props) {
const { getProtocolAvailableTokens } = useFungibleTokens();
- const isSwapTx = computed(() => (
- isTxFunctionDexSwap(props.transaction.tx.function)
- || isTxFunctionDexPool(props.transaction.tx.function)
- ));
+ const { isDexSwap, txFunctionParsed } = useTransactionData({
+ transaction: toRef(() => props.transaction),
+ });
const rates = computed(() => {
- if (!isSwapTx.value) {
+ if (!isDexSwap.value || !txFunctionParsed.value) {
return [];
}
- const resolver = getTransactionTokenInfoResolver(
- camelCase(props.transaction.tx.function) as TxFunctionParsed,
- );
-
- if (!resolver) return [];
+ const resolver = getTransactionTokenInfoResolver(txFunctionParsed.value);
+ if (!resolver) {
+ return [];
+ }
const { tokens } = resolver(
props.transaction,
@@ -117,7 +108,6 @@ export default defineComponent({
return {
PROTOCOLS,
- isSwapTx,
rates,
};
},
diff --git a/src/popup/components/SwapRoute.vue b/src/popup/components/SwapRoute.vue
index 3deb841895..7f2a06e6f0 100644
--- a/src/popup/components/SwapRoute.vue
+++ b/src/popup/components/SwapRoute.vue
@@ -27,22 +27,23 @@
+
+
diff --git a/src/popup/components/NotificationsIcon.vue b/src/popup/components/NotificationsIcon.vue
index 55907fa76e..1373e1fa93 100644
--- a/src/popup/components/NotificationsIcon.vue
+++ b/src/popup/components/NotificationsIcon.vue
@@ -5,6 +5,7 @@
:icon="BellIcon"
:to="{ name: ROUTE_NOTIFICATIONS }"
:badge-text="notificationsCount"
+ dimmed
/>
diff --git a/src/popup/components/SwapRoute.vue b/src/popup/components/SwapRoute.vue
index ca32bf0256..c407eeb7b0 100644
--- a/src/popup/components/SwapRoute.vue
+++ b/src/popup/components/SwapRoute.vue
@@ -79,15 +79,16 @@ export default defineComponent({
}
let { tokens } = resolver(props.transaction, aeTokensAvailable.value);
- const index = props.transaction.tx.arguments.findIndex(({ type }) => type === 'list');
+ const args = props.transaction.tx.arguments || [];
+ const index = args.findIndex(({ type }) => type === 'list');
const waeContract = DEX_CONTRACTS[nodeNetworkId.value!]?.wae;
const tokenLastIndex = tokens.length - 1;
- if (index >= 0 && props.transaction.tx.arguments[index].value.length > tokens.length) {
+ if (index >= 0 && args[index].value.length > tokens.length) {
tokens = [
tokens[0],
- ...props.transaction.tx.arguments[index].value
- .slice(1, props.transaction.tx.arguments[index].value.length - 1)
+ ...args[index].value
+ .slice(1, args[index].value.length - 1)
.map((element: any) => aeTokensAvailable.value[element.value]),
tokens[1],
];
diff --git a/src/popup/components/TransactionDetailsPoolTokenRow.vue b/src/popup/components/TransactionDetailsPoolTokenRow.vue
index cd666c5df3..ff39259b61 100644
--- a/src/popup/components/TransactionDetailsPoolTokenRow.vue
+++ b/src/popup/components/TransactionDetailsPoolTokenRow.vue
@@ -50,7 +50,8 @@ export default defineComponent({
props: {
label: { type: String, default: '' },
token: { type: Object as PropType, required: true },
- tokens: { type: Array as PropType, required: true },
+ // TODO having `token` and `tokens` prop is confusing, refactor to one `assets` prop
+ tokens: { type: Array as PropType, default: null },
hideAmount: Boolean,
},
setup(props) {
@@ -61,7 +62,7 @@ export default defineComponent({
));
const assetsMapped = computed(
- () => (props.token.isPool ? props.tokens : [props.token])
+ () => ((props.token.isPool && props.tokens) ? props.tokens : [props.token])
.map((asset) => convertWrappedCoinTokenToCoin(asset)),
);
diff --git a/src/popup/components/buttons/BtnIcon.vue b/src/popup/components/buttons/BtnIcon.vue
index 5e35a32c43..26e22cd80e 100644
--- a/src/popup/components/buttons/BtnIcon.vue
+++ b/src/popup/components/buttons/BtnIcon.vue
@@ -32,37 +32,39 @@
-
diff --git a/src/popup/components/TransactionOverview.vue b/src/popup/components/TransactionOverview.vue
index cfe9d73a5a..a057dd7ebf 100644
--- a/src/popup/components/TransactionOverview.vue
+++ b/src/popup/components/TransactionOverview.vue
@@ -124,6 +124,7 @@ export default defineComponent({
label: t('transaction.overview.accountAddress'),
},
};
+
case Tag.ContractCallTx: {
const contract: IAccountOverview = {
address: contractId,
@@ -133,6 +134,21 @@ export default defineComponent({
: t('common.smartContract'),
};
+ if (protocol.value === PROTOCOLS.ethereum) {
+ return {
+ sender: {
+ address: senderId,
+ url: protocolExplorer.prepareUrlForAccount(senderId),
+ label: t('transaction.overview.accountAddress'),
+ },
+ recipient: {
+ address: recipientId,
+ url: protocolExplorer.prepareUrlForAccount(recipientId),
+ label: t('common.smartContract'),
+ },
+ };
+ }
+
let transactionOwner;
let transactionReceiver = contract;
@@ -159,6 +175,7 @@ export default defineComponent({
: contract,
};
}
+
case Tag.ContractCreateTx:
return {
sender: ownershipAccount.value,
@@ -166,6 +183,7 @@ export default defineComponent({
label: t('transaction.overview.contractCreate'),
},
};
+
case Tag.NamePreclaimTx:
case Tag.NameClaimTx:
case Tag.NameUpdateTx:
@@ -175,6 +193,7 @@ export default defineComponent({
label: t('transaction.overview.aens'),
},
};
+
default:
throw new Error(`Unsupported transaction type ${outerTxTag.value}`);
}
diff --git a/src/popup/locales/en.json b/src/popup/locales/en.json
index 93bffefaef..2ad393ae8c 100644
--- a/src/popup/locales/en.json
+++ b/src/popup/locales/en.json
@@ -1,5 +1,6 @@
{
"common": {
+ "activeAccount": "Active account",
"address": "Address",
"addressCopied": "Copied",
"ae": "AE",
@@ -15,6 +16,7 @@
"connect": "Connect",
"connected": "Connected",
"connecting": "Connecting...",
+ "connectingAs": "Connecting as",
"connectionFailed": "Connection failed",
"contractId": "Contract ID",
"notNow": "Not now",
@@ -33,6 +35,7 @@
"minutesShort": "{n} mins | {n} min | {n} mins",
"next": "Next",
"nonce": "Nonce",
+ "noProtocolAccountFound": "No {protocol} account found",
"of": "of",
"ok": "OK",
"pending": "Pending",
diff --git a/src/protocols/aeternity/views/TransactionDetails.vue b/src/protocols/aeternity/views/TransactionDetails.vue
index a187bbf572..fb2a23c503 100644
--- a/src/protocols/aeternity/views/TransactionDetails.vue
+++ b/src/protocols/aeternity/views/TransactionDetails.vue
@@ -172,6 +172,7 @@ import {
} from '@/utils';
import {
useAccounts,
+ useFungibleTokens,
useMultisigAccounts,
useTransactionData,
useTransactionList,
@@ -228,6 +229,7 @@ export default defineComponent({
const { activeMultisigAccountId } = useMultisigAccounts({ pollOnce: true });
const { activeAccount, isLocalAccountAddress } = useAccounts();
const { setLoaderVisible } = useUi();
+ const { getTxAmountTotal } = useFungibleTokens();
const hash = route.params.hash as string;
const transactionOwner = route.params.transactionOwner as Encoded.AccountAddress;
@@ -249,8 +251,6 @@ export default defineComponent({
const multisigContractId = ref();
const {
- amount,
- amountTotal,
direction,
isAex9,
isErrorTransaction,
@@ -266,6 +266,12 @@ export default defineComponent({
showDetailedAllowanceInfo: true,
});
+ const amount = computed((): number => transaction.value
+ ? getTxAmountTotal(transaction.value, TX_DIRECTION.received)
+ : 0);
+ const amountTotal = computed((): number => transaction.value
+ ? getTxAmountTotal(transaction.value, direction.value)
+ : 0);
const tipUrl = computed(() => transaction.value ? getTransactionTipUrl(transaction.value) : '');
const tipLink = computed(() => /^http[s]*:\/\//.test(tipUrl.value) ? tipUrl.value : `http://${tipUrl.value}`);
diff --git a/src/protocols/bitcoin/helpers/index.ts b/src/protocols/bitcoin/helpers/index.ts
index f3088231c7..0c433702d7 100644
--- a/src/protocols/bitcoin/helpers/index.ts
+++ b/src/protocols/bitcoin/helpers/index.ts
@@ -1,3 +1,4 @@
+import BigNumber from 'bignumber.js';
import type { ITransaction } from '@/types';
import { PROTOCOLS } from '@/constants';
import { BTC_COIN_PRECISION, BTC_CONTRACT_ID } from '../config';
@@ -6,6 +7,15 @@ export function satoshiToBtc(amount: number) {
return amount / 10 ** BTC_COIN_PRECISION;
}
+// TODO Duplicate in fungibleTokens.ts composable
+export function getTxAmountTotal(transaction: ITransaction, isReceived: boolean): number {
+ return new BigNumber(
+ transaction.tx?.amount || 0,
+ )
+ .plus(isReceived ? 0 : transaction.tx?.fee || 0)
+ .toNumber();
+}
+
export function normalizeTransactionStructure(
transaction: any,
transactionOwner?: string,
diff --git a/src/protocols/bitcoin/views/TransactionDetails.vue b/src/protocols/bitcoin/views/TransactionDetails.vue
index ee204d1576..3d2ea531da 100644
--- a/src/protocols/bitcoin/views/TransactionDetails.vue
+++ b/src/protocols/bitcoin/views/TransactionDetails.vue
@@ -14,8 +14,8 @@
>
();
- const {
- amount,
- amountTotal,
- transactionAssets,
- } = useTransactionData({ transaction });
-
const isReceived = computed(() => transaction.value?.tx?.senderId !== transactionOwner);
const fee = computed((): number => transaction.value?.tx?.fee || 0);
+ const amount = computed((): number => transaction.value?.tx?.amount || 0);
+ const amountTotal = computed((): number => transaction.value?.tx
+ ? getTxAmountTotal(transaction.value, isReceived.value)
+ : 0);
+
const direction = computed(() => isReceived.value ? TX_DIRECTION.received : TX_DIRECTION.sent);
+ const assets = computed((): ITokenResolved[] => [{
+ amount: amount.value,
+ symbol: BTC_SYMBOL,
+ name: BTC_PROTOCOL_NAME,
+ isReceived: direction.value === TX_DIRECTION.received,
+ contractId: adapter.coinContractId,
+ }]);
+
watch(
transaction,
(value) => {
@@ -97,11 +106,10 @@ export default defineComponent({
PROTOCOLS,
amount,
amountTotal,
- direction,
fee,
hash,
+ assets,
transaction,
- transactionAssets,
};
},
});
diff --git a/src/protocols/ethereum/views/TransactionDetails.vue b/src/protocols/ethereum/views/TransactionDetails.vue
index 4392dc2cf9..58ba03c868 100644
--- a/src/protocols/ethereum/views/TransactionDetails.vue
+++ b/src/protocols/ethereum/views/TransactionDetails.vue
@@ -54,6 +54,7 @@
+
+
diff --git a/src/popup/components/AccountImportRow.vue b/src/popup/components/AccountImportRow.vue
new file mode 100644
index 0000000000..22226f4dfb
--- /dev/null
+++ b/src/popup/components/AccountImportRow.vue
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
diff --git a/src/popup/components/AccountInfo.vue b/src/popup/components/AccountInfo.vue
index 88868ddfb1..2bd8ce21fe 100644
--- a/src/popup/components/AccountInfo.vue
+++ b/src/popup/components/AccountInfo.vue
@@ -33,7 +33,7 @@
v-else
data-cy="account-name-number"
class="account-name"
- v-text="getDefaultAccountLabel(account)"
+ v-text="getDefaultAccountLabel(account, { isAirGap })"
/>
@@ -88,6 +88,7 @@ export default defineComponent({
customName: { type: String, default: null },
canCopyAddress: Boolean,
isMultisig: Boolean,
+ isAirGap: Boolean,
avatarBorderless: Boolean,
isListName: Boolean,
isPlaceholder: Boolean,
diff --git a/src/popup/components/Modals/AirGapConfirmImport.vue b/src/popup/components/Modals/AirGapConfirmImport.vue
new file mode 100644
index 0000000000..8372bce1d6
--- /dev/null
+++ b/src/popup/components/Modals/AirGapConfirmImport.vue
@@ -0,0 +1,191 @@
+
+
+
+
+ {{ $t('modals.importAirGapAccount.importConfirmDialog.title') }}
+
+
+
+
+
+
+ {{ $t('modals.importAirGapAccount.importConfirmDialog.selectAll') }}
+
+
+ {{ $t('modals.importAirGapAccount.importConfirmDialog.noAccountsFound') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/popup/locales/en.json b/src/popup/locales/en.json
index f903f64644..a2b5ab5b02 100644
--- a/src/popup/locales/en.json
+++ b/src/popup/locales/en.json
@@ -67,7 +67,8 @@
"slow": "Slow"
},
"actionMayTakeFewMoments": "This may take a few moments.",
- "backToHome": "Proceed to dashboard"
+ "backToHome": "Proceed to dashboard",
+ "airGap": "AirGap"
},
"connectionStatus": {
"offline": "You are offline. Go online to get real time data.",
@@ -242,6 +243,20 @@
"thirdPoint": "{'<'}strong{'>'}Z ={'<'}/strong{'>'} number of authorized signers.",
"description": "If {'<'}strong{'>'}X=Y{'<'}/strong{'>'} the multisig consensus has been reached. The transaction proposal has been approved and the transaction can be sent by any of the authorized signers."
},
+ "importAirGapAccount": {
+ "btnText": "Pair QR Wallet",
+ "btnSubtitle": "Connect QR-based Hardware Wallet",
+ "scanTitle": "Scan QR code",
+ "scanDescription": "from your AirGap vault",
+
+ "importConfirmDialog": {
+ "title": "Select accounts to import from AirGap",
+ "selectAll": "Select all",
+ "search": "Search for account",
+ "importAccounts": "Import Accounts",
+ "noAccountsFound": "There are no accounts found."
+ }
+ },
"removeAccount": {
"title": "Are you sure you want to remove this account?",
"msg": "This action will remove your account from the wallet and will delete the extension storage. Make sure you have backed up your seed phrase before proceeding. This action cannot be undone!"
diff --git a/src/popup/router/modals.ts b/src/popup/router/modals.ts
index c6d058fb9f..ea2fc7805d 100644
--- a/src/popup/router/modals.ts
+++ b/src/popup/router/modals.ts
@@ -35,6 +35,7 @@ import {
MODAL_WARNING_DAPP_BROWSER,
MODAL_SECURE_LOGIN,
MODAL_ENABLE_SECURE_LOGIN,
+ MODAL_AIR_GAP_CONFIRM_IMPORT,
} from '@/constants';
import { useModals } from '@/composables';
@@ -72,6 +73,7 @@ import BrowserActions from '../components/Modals/BrowserActions.vue';
import SecureLogin from '../components/Modals/SecureLogin.vue';
import EnableSecureLogin from '../components/Modals/EnableSecureLogin.vue';
import WalletConnect from '../components/Modals/WalletConnectModal.vue';
+import AirGapConfirmImport from '../components/Modals/AirGapConfirmImport.vue';
export default () => {
const { registerModal } = useModals();
@@ -85,6 +87,9 @@ export default () => {
registerModal(MODAL_ACCOUNT_IMPORT, {
component: AccountImport,
});
+ registerModal(MODAL_AIR_GAP_CONFIRM_IMPORT, {
+ component: AirGapConfirmImport,
+ });
registerModal(MODAL_CLAIM_SUCCESS, {
component: ClaimSuccess,
});
diff --git a/src/protocols/aeternity/views/AccountCreateModal.vue b/src/protocols/aeternity/views/AccountCreateModal.vue
index 59bb07b88b..e477c78a1a 100644
--- a/src/protocols/aeternity/views/AccountCreateModal.vue
+++ b/src/protocols/aeternity/views/AccountCreateModal.vue
@@ -39,6 +39,13 @@
:disabled="!isOnline"
@click="createMultisigAccount()"
/>
+
@@ -46,12 +53,19 @@
+
diff --git a/src/popup/components/TransferQRCodeGenerator.vue b/src/popup/components/TransferQRCodeGenerator.vue
new file mode 100644
index 0000000000..fd9204dbe9
--- /dev/null
+++ b/src/popup/components/TransferQRCodeGenerator.vue
@@ -0,0 +1,94 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/popup/components/TransferRawTxReview.vue b/src/popup/components/TransferRawTxReview.vue
new file mode 100644
index 0000000000..4eaa94577a
--- /dev/null
+++ b/src/popup/components/TransferRawTxReview.vue
@@ -0,0 +1,173 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/popup/components/TransferSend/TransferReviewBase.vue b/src/popup/components/TransferSend/TransferReviewBase.vue
index f0283bcc56..d0afa45972 100644
--- a/src/popup/components/TransferSend/TransferReviewBase.vue
+++ b/src/popup/components/TransferSend/TransferReviewBase.vue
@@ -2,7 +2,7 @@
@@ -114,6 +114,7 @@ export default defineComponent({
},
props: {
title: { type: String, default: tg('pages.send.reviewtx') },
+ subtitle: { type: String, default: tg('pages.send.checkalert') },
senderLabel: { type: String, default: tg('pages.send.sender') },
amountLabel: { type: String, default: tg('common.amount') },
baseTokenSymbol: { type: String, required: true },
diff --git a/src/popup/locales/en.json b/src/popup/locales/en.json
index a2b5ab5b02..ca16ee96b2 100644
--- a/src/popup/locales/en.json
+++ b/src/popup/locales/en.json
@@ -247,8 +247,7 @@
"btnText": "Pair QR Wallet",
"btnSubtitle": "Connect QR-based Hardware Wallet",
"scanTitle": "Scan QR code",
- "scanDescription": "from your AirGap vault",
-
+ "scanDescription": "Scan QR code from your AirGap vault in order to pair wallets and import accounts.",
"importConfirmDialog": {
"title": "Select accounts to import from AirGap",
"selectAll": "Select all",
@@ -257,6 +256,15 @@
"noAccountsFound": "There are no accounts found."
}
},
+ "scanAirGapTx": {
+ "heading": "Scan QR code",
+ "title": "Scan the QR code of the signed transaction from your AirGap vault in order to send it.",
+
+ "help": {
+ "title": "Sign AirGap transaction",
+ "msg": "In order to sign an AirGap transaction and send it from Superhero wallet you need to follow 3 simple steps:
- Review carefully transaction details and scan the QR code generated by Superhero wallet with your AirGap vault.
- Sign the transaction in your AirGap vault. Another QR code will be generated by AirGap vault.
- Scan the QR code from your AirGap vault with Superhero wallet and confirm sending the transaction.
"
+ }
+ },
"removeAccount": {
"title": "Are you sure you want to remove this account?",
"msg": "This action will remove your account from the wallet and will delete the extension storage. Make sure you have backed up your seed phrase before proceeding. This action cannot be undone!"
@@ -405,6 +413,11 @@
"proposeAndApprove": "Propose and approve",
"tokenWarning": "Multisig transactions currently support AE coin only."
},
+ "airGapSend": {
+ "sendTitle": "Send funds with AirGap",
+ "reviewTitle": "Review and sign transaction",
+ "reviewSubTitle": "Scan QR-code with your AirGap vault in order to sign the transaction"
+ },
"wrongNetwork": {
"title": "Network discrepancy",
"msg": "This action was initiated on a different network. Please switch to {0} in your wallet settings to proceed.",
diff --git a/src/popup/pages/Assets/AssetDetails.vue b/src/popup/pages/Assets/AssetDetails.vue
index 9016646417..93fb09aae6 100644
--- a/src/popup/pages/Assets/AssetDetails.vue
+++ b/src/popup/pages/Assets/AssetDetails.vue
@@ -31,6 +31,7 @@
!!route?.meta?.isMultisig);
const { isNodeMainnet, isNodeTestnet, getAeSdk } = useAeSdk();
- const { activeAccount, getLastActiveProtocolAccount } = useAccounts();
+ const { activeAccount, getLastActiveProtocolAccount, isAirGap } = useAccounts();
const { protocolCoinBalance } = useAccountAssetsList({
isMultisig: isMultisig.value,
});
@@ -403,6 +404,7 @@ export default defineComponent({
stickyTabsWrapperEl,
fungibleToken,
isAeCoin,
+ isAirGap,
isCoin,
isNodeMainnet,
isNodeTestnet,
diff --git a/src/popup/pages/Dashboard.vue b/src/popup/pages/Dashboard.vue
index e8559b5125..9396c92ef2 100644
--- a/src/popup/pages/Dashboard.vue
+++ b/src/popup/pages/Dashboard.vue
@@ -29,7 +29,7 @@
-
+
@@ -154,6 +154,7 @@ export default defineComponent({
activeAccountGlobalIdx,
setActiveAccountByGlobalIdx,
setActiveAccountByAddress,
+ isAirGap,
} = useAccounts();
const { accountsTotalBalance } = useBalances();
@@ -211,6 +212,7 @@ export default defineComponent({
pageIsActive,
setActiveAccountByGlobalIdx,
setActiveAccountByAddress,
+ isAirGap,
};
},
});
diff --git a/src/protocols/aeternity/components/TransferReview.vue b/src/protocols/aeternity/components/TransferReview.vue
index 0e6702f62a..78fdc63a7e 100644
--- a/src/protocols/aeternity/components/TransferReview.vue
+++ b/src/protocols/aeternity/components/TransferReview.vue
@@ -1,6 +1,7 @@
+
+
+
props.transferData?.selectedAsset?.contractId !== AE_CONTRACT_ID,
);
+ const headerTitle = computed(() => {
+ if (props.isMultisig) {
+ return tg('modals.multisigTxProposal.title');
+ }
+ if (props.isAirGap) {
+ return tg('modals.airGapSend.reviewTitle');
+ }
+ return tg('pages.send.reviewtx');
+ });
+ const headerSubtitle = computed(() => {
+ if (props.isAirGap) {
+ return tg('modals.airGapSend.reviewSubTitle');
+ }
+ return tg('pages.send.checkalert');
+ });
+
function openTransactionFailedModal() {
openDefaultModal({
title: t('modals.transaction-failed.msg'),
@@ -352,7 +384,29 @@ export default defineComponent({
}
}
+ async function scanSignedTransaction() {
+ const scanResult = await root.$store.dispatch('modals/open', {
+ name: MODAL_READ_QR_CODE,
+ heading: root.$t('modals.scanAirGapTx.heading'),
+ title: root.$t('modals.scanAirGapTx.title'),
+ icon: 'critical',
+ });
+
+ if (!scanResult) return;
+
+ const txRaw = await extractSignedTransactionResponseData(scanResult);
+
+ if (!txRaw) {
+ return;
+ }
+ emit('success', txRaw);
+ }
+
async function submit(): Promise
{
+ if (props.isAirGap) {
+ return scanSignedTransaction();
+ }
+
const {
amount: amountRaw,
address: recipient,
@@ -361,7 +415,7 @@ export default defineComponent({
} = props.transferData;
if (!amountRaw || !recipient || !selectedAsset) {
- return;
+ return undefined;
}
const amount = (selectedAsset.contractId === AE_CONTRACT_ID)
@@ -385,6 +439,7 @@ export default defineComponent({
});
router.push({ name: homeRouteName.value, query: { latestTxHash: hash } });
}
+ return undefined;
}
return {
@@ -395,6 +450,8 @@ export default defineComponent({
AE_CONTRACT_ID,
PROPOSE_TRANSACTION_FEE,
loading,
+ headerTitle,
+ headerSubtitle,
submit,
};
},
@@ -407,6 +464,13 @@ export default defineComponent({
margin-top: 16px;
}
+ .custom-header-title {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ gap: 10px;
+ }
+
.multisig-account {
display: flex;
justify-content: center;
diff --git a/src/protocols/aeternity/components/TransferSendForm.vue b/src/protocols/aeternity/components/TransferSendForm.vue
index 67757b5bd7..78753e15c2 100644
--- a/src/protocols/aeternity/components/TransferSendForm.vue
+++ b/src/protocols/aeternity/components/TransferSendForm.vue
@@ -5,6 +5,7 @@
:fee="+fee.toFixed()"
:fee-symbol="AE_SYMBOL"
:protocol="PROTOCOLS.aeternity"
+ :custom-title="isAirGap ? $t('modals.airGapSend.sendTitle') : undefined"
class="transfer-send-form"
>
, required: true },
isMultisig: Boolean,
+ isAirGap: Boolean,
},
emits: [
'update:transferData',
diff --git a/src/protocols/aeternity/views/TransferSendModal.vue b/src/protocols/aeternity/views/TransferSendModal.vue
index 37ebf4959a..ed00254052 100644
--- a/src/protocols/aeternity/views/TransferSendModal.vue
+++ b/src/protocols/aeternity/views/TransferSendModal.vue
@@ -14,7 +14,9 @@
:is="currentStepConfig.component"
ref="currentRenderedComponent"
v-model:transferData="transferData"
+ :tx-raw="txRaw"
:is-multisig="isMultisig"
+ :is-air-gap="isAirGap"
:is-address-chain="isAddressChain"
:is-address-url="isAddressUrl"
@success="currentStepConfig.onSuccess"
@@ -53,6 +55,7 @@ import TransferSendBase, { transferSendModalRequiredProps } from '@/popup/compon
import TransferSendForm from '../components/TransferSendForm.vue';
import TransferReviewTip from '../components/TransferReviewTip.vue';
import TransferReview from '../components/TransferReview.vue';
+import TransferRawTxReview from '../components/TransferRawTxReview.vue';
export default defineComponent({
name: PROTOCOL_VIEW_TRANSFER_SEND,
@@ -63,6 +66,7 @@ export default defineComponent({
...transferSendModalRequiredProps,
tokenContractId: { type: String as PropType, default: null },
isMultisig: Boolean,
+ isAirGap: Boolean,
},
setup(props) {
const { t } = useI18n();
@@ -72,6 +76,7 @@ export default defineComponent({
const currentRenderedComponent = ref();
const currentStep = ref(TRANSFER_SEND_STEPS.form);
const error = ref(false);
+ const txRaw = ref();
const transferData = ref({
address: props.address as any, // TODO change to string globally
amount: props.amount,
@@ -96,9 +101,24 @@ export default defineComponent({
|| !transferData.value.amount
));
- const customPrimaryButtonText = computed(() => (props.isMultisig)
- ? t('modals.multisigTxProposal.proposeAndApprove')
- : '');
+ const showScanButton = computed(() => (
+ currentStep.value === TRANSFER_SEND_STEPS.review
+ && props.isAirGap
+ ));
+
+ // const customPrimaryButtonText = computed(() => (props.isMultisig)
+ // ? t('modals.multisigTxProposal.proposeAndApprove')
+ // : '');
+
+ const customPrimaryButtonText = computed(() => {
+ if (props.isMultisig) {
+ return t('modals.multisigTxProposal.proposeAndApprove');
+ }
+ if (props.isAirGap && showScanButton.value) {
+ return t('common.scan');
+ }
+ return '';
+ });
function proceedToNextStep() {
(currentRenderedComponent.value as any).submit();
@@ -114,11 +134,25 @@ export default defineComponent({
currentStep.value = TRANSFER_SEND_STEPS.review;
}
+ function handleReviewSuccess(rawTx: string = '') {
+ if (props.isAirGap && rawTx) {
+ txRaw.value = rawTx;
+ currentStep.value = STEPS.reviewRawTx;
+ } else {
+ props.resolve();
+ }
+ }
+
function editTransfer() {
error.value = false;
currentStep.value = TRANSFER_SEND_STEPS.form;
}
+ function cancelTransfer() {
+ error.value = false;
+ props.resolve();
+ }
+
const steps: TransferSendStepConfigRegistry = {
[TRANSFER_SEND_STEPS.form]: {
component: TransferSendForm,
@@ -130,7 +164,11 @@ export default defineComponent({
},
[TRANSFER_SEND_STEPS.review]: {
component: TransferReview,
- onSuccess: props.resolve,
+ onSuccess: handleReviewSuccess,
+ },
+ [TRANSFER_SEND_STEPS.reviewRawTx]: {
+ component: TransferRawTxReview,
+ onSuccess: handleReviewSuccess,
},
};
@@ -143,6 +181,7 @@ export default defineComponent({
steps,
currentStep,
error,
+ txRaw,
transferData,
currentStepConfig,
isAddressChain,
diff --git a/src/types/index.ts b/src/types/index.ts
index 2ea3fdec43..38facb3290 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -272,6 +272,8 @@ export interface IAccount extends IHdWalletAccount, IAccountRaw {
address: AccountAddress;
globalIdx: number;
idx: number;
+ airGapPublicKey?: string;
+ showed: boolean;
}
/**
From 95e067f912cb02bb77f2d76ec271357743373ec5 Mon Sep 17 00:00:00 2001
From: badi
Date: Fri, 31 Mar 2023 08:31:37 +0100
Subject: [PATCH 21/55] feat(airgap): sign transactions
---
src/composables/airGap.ts | 98 ++++--------
src/constants/common.ts | 1 +
src/popup/components/AccountImportRow.vue | 13 +-
.../components/Modals/AirGapConfirmImport.vue | 22 +--
.../Modals/AirGapSignTransaction.vue | 142 ++++++++++++++++++
src/popup/components/Modals/QrCodeReader.vue | 63 +++++++-
.../components/Modals/TransferSendBase.vue | 12 +-
src/popup/components/MultiFragmentsQrCode.vue | 78 ++++++++++
src/popup/components/TemplateRenderer.vue | 4 +-
.../components/TransferQRCodeGenerator.vue | 22 +--
src/popup/locales/en.json | 15 +-
src/popup/router/modals.ts | 5 +
.../components/TransferRawTxReview.vue | 4 +-
.../aeternity/views/AccountCreateModal.vue | 26 ++--
.../aeternity/views/TransferSendModal.vue | 6 +-
src/store/modules/accounts/airgap.js | 7 +-
src/types/index.ts | 1 +
17 files changed, 375 insertions(+), 144 deletions(-)
create mode 100644 src/popup/components/Modals/AirGapSignTransaction.vue
create mode 100644 src/popup/components/MultiFragmentsQrCode.vue
rename src/{popup => protocols/aeternity}/components/TransferRawTxReview.vue (96%)
diff --git a/src/composables/airGap.ts b/src/composables/airGap.ts
index 5a46236cf5..1ae3164409 100644
--- a/src/composables/airGap.ts
+++ b/src/composables/airGap.ts
@@ -1,5 +1,5 @@
import { decode } from '@aeternity/aepp-sdk/es/tx/builder/helpers';
-import { UR, URDecoder, UREncoder } from '@ngraveio/bc-ur';
+import { UR, UREncoder } from '@ngraveio/bc-ur';
import bs58check from 'bs58check';
import { IACMessageType, SerializerV3 } from '@airgap/serializer';
import type {
@@ -10,45 +10,10 @@ import type {
} from '@airgap/serializer';
import { AeternityAddress } from '@airgap/aeternity';
import { MainProtocolSymbols } from '@airgap/coinlib-core';
-import type { IAccount, IDefaultComposableOptions } from '../types';
+import type { IAccount } from '../types';
import { ACCOUNT_AIR_GAP_WALLET, handleUnknownError } from '../popup/utils';
-// eslint-disable-next-line no-unused-vars
-export function useAirGap({ store }: IDefaultComposableOptions) {
- /**
- * Decodes a serialized data string that conforms to the "Uniform Resource" format (UR).
- * The function first decodes the UR string using the URDecoder class, then decodes the
- * resulting CBOR data, and finally deserializes the decoded data using a SerializerV3 instance.
- *
- * @param serializedData A string containing the serialized UR data to decode.
- *
- * @returns The deserialized data,
- * or null if the input data is not in the expected format or decoding fails.
- */
- async function decodeURSerializedData(
- serializedData: string,
- ): Promise {
- if (!serializedData.toUpperCase().startsWith('UR:')) {
- return [];
- }
-
- const decoder = new URDecoder();
- decoder.receivePart(serializedData);
-
- if (!decoder.isComplete() || !decoder.isSuccess()) {
- return [];
- }
-
- const decoded = decoder.resultUR();
- const combinedData = decoded.decodeCBOR();
- const resultUr = bs58check.encode(combinedData);
-
- const serializer = SerializerV3.getInstance();
- const parsedData: IACMessageDefinitionObjectV3[] = await serializer.deserialize(resultUr);
-
- return parsedData;
- }
-
+export function useAirGap() {
/**
* Encodes an array of IACMessageDefinitionObjectV3 objects into a UR string.
* @param data - The array of IACMessageDefinitionObjectV3 objects to encode.
@@ -64,6 +29,7 @@ export function useAirGap({ store }: IDefaultComposableOptions) {
// Set the chunk sizes for single-chunk and multi-chunk encoding.
const SETTINGS_SERIALIZER_SINGLE_CHUNK_SIZE = 500;
+ const SETTINGS_SERIALIZER_MULTI_CHUNK_SIZE = 250;
// Create a UR encoder for single-chunk encoding.
const singleEncoder = new UREncoder(
@@ -73,21 +39,25 @@ export function useAirGap({ store }: IDefaultComposableOptions) {
// If the UR requires multi-chunk encoding,
// create a new UR encoder with the appropriate chunk size.
if (singleEncoder.fragmentsLength !== 1) {
- // TODO:: handle when it's multi fragments.
- // const SETTINGS_SERIALIZER_MULTI_CHUNK_SIZE = 250;
- // const multiEncoder = new UREncoder(
- // ur,
- // SETTINGS_SERIALIZER_MULTI_CHUNK_SIZE,
- // );
- return null;
+ const multiEncoder = new UREncoder(
+ ur,
+ SETTINGS_SERIALIZER_MULTI_CHUNK_SIZE,
+ );
+ const fragments = [];
+
+ // eslint-disable-next-line no-restricted-syntax, guard-for-in, no-unused-vars
+ for (const _index in [...Array(multiEncoder.fragmentsLength)]) {
+ // eslint-disable-next-line no-await-in-loop
+ fragments.push(await multiEncoder.nextPart());
+ }
+ return fragments;
}
// Encode the UR and return the UR string in upper case.
- const urString = await singleEncoder.nextPart();
- return urString.toUpperCase();
+ return [(await singleEncoder.nextPart()).toUpperCase()];
} catch (error) {
handleUnknownError(error);
- return null;
+ return [];
}
}
@@ -97,15 +67,9 @@ export function useAirGap({ store }: IDefaultComposableOptions) {
* @returns IAccount[]
*/
async function extractAccountShareResponseData(
- serializedData: string,
+ data: IACMessageDefinitionObjectV3[] = [],
): Promise {
- const decodedData = await decodeURSerializedData(serializedData);
-
- if (!decodedData) {
- return [];
- }
-
- return decodedData
+ return data
.filter((item) => item.type === IACMessageType.AccountShareResponse)
.map((item) => {
const address = AeternityAddress.from(
@@ -131,27 +95,21 @@ export function useAirGap({ store }: IDefaultComposableOptions) {
* @returns The transaction object or null if no transaction object is found.
*/
async function extractSignedTransactionResponseData(
- serializedData: string,
+ data: IACMessageDefinitionObjectV3[] = [],
): Promise {
- const decodedData = await decodeURSerializedData(serializedData);
-
- if (!decodedData) {
- return null;
- }
-
- const payload = decodedData.find(
+ const payload = data.find(
(item) => item.type === IACMessageType.TransactionSignResponse,
)?.payload as TransactionSignResponse;
return payload?.transaction || null;
}
- async function generateEncodedTransactionSignRequestUR(
+ async function generateTransactionURDataFragments(
publicKey: string,
transaction: string,
networkId: string,
- ): Promise {
- const id = Math.floor(Math.random() * (99999999 - 10000000 + 1) + 10000000);
+ ): Promise {
+ const id = Math.floor(Math.random() * 90000000 + 10000000);
const callbackURL = 'superhero://?d=';
const payload: TransactionSignRequest = {
callbackURL,
@@ -167,16 +125,14 @@ export function useAirGap({ store }: IDefaultComposableOptions) {
protocol: MainProtocolSymbols.AE,
type: IACMessageType.TransactionSignRequest,
};
- const encodedUR = await encodeIACMessageDefinitionObjects([messageDefinitionObject]);
- return encodedUR || null;
+ return encodeIACMessageDefinitionObjects([messageDefinitionObject]);
}
return {
- decodeURSerializedData,
encodeIACMessageDefinitionObjects,
extractAccountShareResponseData,
- generateEncodedTransactionSignRequestUR,
+ generateTransactionURDataFragments,
extractSignedTransactionResponseData,
};
}
diff --git a/src/constants/common.ts b/src/constants/common.ts
index 267415c1f9..25bdf5c258 100644
--- a/src/constants/common.ts
+++ b/src/constants/common.ts
@@ -330,6 +330,7 @@ export const MODAL_SECURE_LOGIN = 'secure-login';
export const MODAL_ENABLE_SECURE_LOGIN = 'enable-secure-login';
export const MODAL_AIR_GAP_CONFIRM_IMPORT = 'air-gap-confirm-import';
export const MODAL_AIR_GAP_TRANSACTION_QR = 'air-gap-transaction-qr';
+export const MODAL_AIR_GAP_SIGN_TRANSACTION = 'air-gap-sign-transaction';
export const POPUP_TYPE_CONNECT = 'connectConfirm';
export const POPUP_TYPE_ACCOUNT_LIST = 'account-list';
diff --git a/src/popup/components/AccountImportRow.vue b/src/popup/components/AccountImportRow.vue
index 22226f4dfb..907dedcf09 100644
--- a/src/popup/components/AccountImportRow.vue
+++ b/src/popup/components/AccountImportRow.vue
@@ -7,7 +7,10 @@
:is-air-gap="isAirGapAccount"
/>
-
+
@@ -63,13 +66,5 @@ export default defineComponent({
.account-import-row {
@include mixins.flex(space-between, center, row);
-
- ::v-deep .token-amount {
- .amount,
- .fiat {
- display: block;
- text-align: right;
- }
- }
}
diff --git a/src/popup/components/Modals/AirGapConfirmImport.vue b/src/popup/components/Modals/AirGapConfirmImport.vue
index 8372bce1d6..71f8e96d9b 100644
--- a/src/popup/components/Modals/AirGapConfirmImport.vue
+++ b/src/popup/components/Modals/AirGapConfirmImport.vue
@@ -3,7 +3,7 @@
full-screen
has-close-button
dense
- class="ari-gap-confirm-import"
+ class="air-gap-confirm-import"
@close="cancel()"
>
@@ -12,12 +12,6 @@
-
('');
const selectAll = ref
(false);
const selectedAccounts = ref>({});
- const hasSelectedAccounts = computed((): boolean => !!props.accounts.filter(
+ const hasSelectedAccounts = computed((): boolean => props.accounts.some(
(account) => selectedAccounts.value[account.address],
- ).length);
+ ));
const filteredAccounts = computed((): IAccount[] => (
props.accounts.filter((account) => account.address.includes(searchPhrase.value))
));
function isAccountAlreadyImported(account: IAccount) {
- return !!importedAccounts.value.filter((acc) => acc.address === account.address).length;
+ return importedAccounts.value.some((acc) => acc.address === account.address);
}
function confirm() {
@@ -152,7 +144,7 @@ export default defineComponent({
@use '../../../styles/typography';
@use '../../../styles/mixins';
-.ari-gap-confirm-import {
+.air-gap-confirm-import {
.title {
@extend %face-sans-15-medium;
@@ -161,10 +153,6 @@ export default defineComponent({
padding: 12px;
}
- .input-search {
- margin-bottom: 20px;
- }
-
.select-all,
.account-row {
@include mixins.flex(flex-start, center, row);
diff --git a/src/popup/components/Modals/AirGapSignTransaction.vue b/src/popup/components/Modals/AirGapSignTransaction.vue
new file mode 100644
index 0000000000..ebec8697fd
--- /dev/null
+++ b/src/popup/components/Modals/AirGapSignTransaction.vue
@@ -0,0 +1,142 @@
+
+
+
+
+ {{ $t('modals.scanSignedAirGapTx.heading') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/popup/components/Modals/QrCodeReader.vue b/src/popup/components/Modals/QrCodeReader.vue
index c0ed99331b..0b28e7b9ef 100644
--- a/src/popup/components/Modals/QrCodeReader.vue
+++ b/src/popup/components/Modals/QrCodeReader.vue
@@ -30,6 +30,11 @@
v-text="title"
/>
+
+ {{ $t('modals.qrCodeReader.qrCodeHasMultipleFragments') }}
+ {{ scanProgress }}%
+
+
{
setMobileQrScannerVisible(true);
setTimeout(() => {
@@ -152,9 +173,21 @@ export default defineComponent({
'barcodeScanned',
async ({ barcode }) => {
if (barcode.displayValue) {
- await listener.remove();
- stopReading();
- resolve(barcode.displayValue);
+ if (barcode.displayValue.includes('BYTES/')) {
+ decoder.value.receivePart(barcode.displayValue);
+ if (decoder.value.isComplete()) {
+ await listener.remove();
+ stopReading();
+ resolve(await getEncoderData());
+ } else {
+ isMultiFragmentQr.value = true;
+ scanProgress.value = Math.floor(decoder.value.getProgress() * 100);
+ }
+ } else {
+ await listener.remove();
+ stopReading();
+ resolve(barcode.displayValue);
+ }
}
},
);
@@ -167,10 +200,21 @@ export default defineComponent({
return new Promise
((resolve) => {
browserReader = new QrScanner(
qrCodeVideoEl.value!,
- (result) => {
- if (result.data) {
+ async (result) => {
+ const text = result.data;
+ if (text) {
stopReading();
- resolve(result.data);
+ if (String(text).includes('BYTES/')) {
+ decoder.value.receivePart(text);
+ if (decoder.value.isComplete()) {
+ resolve(await getEncoderData());
+ } else {
+ isMultiFragmentQr.value = true;
+ scanProgress.value = Math.floor(decoder.value.getProgress() * 100);
+ }
+ } else {
+ resolve(text);
+ }
}
},
{},
@@ -209,6 +253,7 @@ export default defineComponent({
}
isCameraReady.value = true;
+ decoder.value = new URDecoder();
props.resolve((IS_MOBILE_APP) ? await scanMobile() : await scanWeb());
});
@@ -221,6 +266,8 @@ export default defineComponent({
isCameraReady,
hasDeviceCamera,
cameraPermissionGranted,
+ isMultiFragmentQr,
+ scanProgress,
QrScanIcon,
qrCodeVideoEl,
closeQrCodeReaderModal,
@@ -297,5 +344,9 @@ export default defineComponent({
line-height: 24px;
color: rgba($color-white, 0.75);
}
+
+ .info-box {
+ text-align: left;
+ }
}
diff --git a/src/popup/components/Modals/TransferSendBase.vue b/src/popup/components/Modals/TransferSendBase.vue
index bbed0112ea..1c7fd7b30b 100644
--- a/src/popup/components/Modals/TransferSendBase.vue
+++ b/src/popup/components/Modals/TransferSendBase.vue
@@ -19,7 +19,7 @@
class="button-action-secondary"
data-cy="cancel"
extra-padded
- @click="cancelTransfer"
+ @click="$emit('cancel-transfer')"
/>
[
TRANSFER_SEND_STEPS.reviewRawTx,
].includes(props.currentStep as any));
- const showSendButton = computed(() => (
- props.currentStep === TRANSFER_SEND_STEPS.review
- || props.currentStep === TRANSFER_SEND_STEPS.reviewRawTx
- ));
+
+ const showSendButton = computed(() => [
+ TRANSFER_SEND_STEPS.review,
+ TRANSFER_SEND_STEPS.reviewRawTx,
+ ].includes(props.currentStep as any));
const primaryButtonText = computed(() => {
if (props.customPrimaryButtonText) {
diff --git a/src/popup/components/MultiFragmentsQrCode.vue b/src/popup/components/MultiFragmentsQrCode.vue
new file mode 100644
index 0000000000..d414bc9e6b
--- /dev/null
+++ b/src/popup/components/MultiFragmentsQrCode.vue
@@ -0,0 +1,78 @@
+
+
+
+
+
diff --git a/src/popup/components/TemplateRenderer.vue b/src/popup/components/TemplateRenderer.vue
index 3f730465db..2ff4d85086 100644
--- a/src/popup/components/TemplateRenderer.vue
+++ b/src/popup/components/TemplateRenderer.vue
@@ -8,11 +8,9 @@ const renderNodeContent = (createElement, node, option = null) => (!node.childNo
.map((n) => {
switch (n.tagName) {
case 'strong':
- return createElement('strong', renderNodeContent(createElement, n));
case 'ol':
- return createElement('ol', renderNodeContent(createElement, n));
case 'li':
- return createElement('li', renderNodeContent(createElement, n));
+ return createElement(n.tagName, renderNodeContent(createElement, n));
case 'a':
return createElement('a', { ...option }, renderNodeContent(createElement, n));
case 'br':
diff --git a/src/popup/components/TransferQRCodeGenerator.vue b/src/popup/components/TransferQRCodeGenerator.vue
index fd9204dbe9..2bfb03dce2 100644
--- a/src/popup/components/TransferQRCodeGenerator.vue
+++ b/src/popup/components/TransferQRCodeGenerator.vue
@@ -1,8 +1,8 @@
-
-
+ , required: true },
@@ -37,8 +37,8 @@ export default defineComponent({
const activeNetwork = useGetter('activeNetwork');
const { getSdk } = useSdk({ store: root.$store });
const { account } = useAccounts({ store: root.$store });
- const { generateEncodedTransactionSignRequestUR } = useAirGap({ store: root.$store });
- const transactionUR = ref();
+ const { generateTransactionURDataFragments } = useAirGap();
+ const fragments = ref();
onMounted(async () => {
const sdk = await getSdk();
@@ -62,7 +62,7 @@ export default defineComponent({
amount,
payload: props.transferData.payload,
});
- transactionUR.value = await generateEncodedTransactionSignRequestUR(
+ fragments.value = await generateTransactionURDataFragments(
account.value.airGapPublicKey,
txRaw,
activeNetwork.value.networkId,
@@ -71,7 +71,7 @@ export default defineComponent({
});
return {
- transactionUR,
+ fragments,
};
},
});
@@ -80,7 +80,7 @@ export default defineComponent({
diff --git a/src/popup/components/AccountSelectOptionsItem.vue b/src/popup/components/AccountSelectOptionsItem.vue
index a5d74df1ff..ae034a8ac7 100644
--- a/src/popup/components/AccountSelectOptionsItem.vue
+++ b/src/popup/components/AccountSelectOptionsItem.vue
@@ -11,6 +11,7 @@
>
@@ -58,6 +60,7 @@ export default defineComponent({
default: () => ({}),
},
selected: Boolean,
+ isAirGapAccount: Boolean,
},
setup(props) {
const { getAccountBalance } = useBalances();
diff --git a/src/types/index.ts b/src/types/index.ts
index 25f73e1d1f..a6c4806129 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -272,6 +272,7 @@ export interface IAccount extends IHdWalletAccount, IAccountRaw {
address: AccountAddress;
globalIdx: number;
idx: number;
+ name?: string;
airGapPublicKey?: string;
showed: boolean;
}
From bcbc48fce42b3f0588472b152978c979b76a0212 Mon Sep 17 00:00:00 2001
From: Badi Ifaoui
Date: Tue, 4 Jul 2023 07:46:42 +0100
Subject: [PATCH 24/55] fix: scan qr code translation
---
.../Modals/AirGapSignTransaction.vue | 18 +++++++++---------
src/popup/locales/en.json | 2 +-
.../components/TransferRawTxReview.vue | 2 +-
3 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/src/popup/components/Modals/AirGapSignTransaction.vue b/src/popup/components/Modals/AirGapSignTransaction.vue
index 97752d9659..2e66eb3216 100644
--- a/src/popup/components/Modals/AirGapSignTransaction.vue
+++ b/src/popup/components/Modals/AirGapSignTransaction.vue
@@ -42,16 +42,16 @@ import {
onMounted,
PropType,
ref,
-} from '@vue/composition-api';
+} from 'vue';
import { IACMessageType } from '@airgap/serializer';
import type {
IACMessageDefinitionObjectV3,
TransactionSignResponse,
} from '@airgap/serializer';
-import { useGetter } from '../../../composables/vuex';
-import { useAccounts, useModals, useAirGap } from '../../../composables';
-import type { INetwork } from '../../../types';
-import { MODAL_READ_QR_CODE } from '../../utils';
+
+import type { INetwork } from '@/types';
+import { MODAL_READ_QR_CODE } from '@/constants';
+import { useAccounts, useModals, useAirGap } from '@/composables';
import Modal from '../Modal.vue';
import BtnMain from '../buttons/BtnMain.vue';
@@ -71,10 +71,10 @@ export default defineComponent({
resolve: { type: Function as PropType<(txRaw: string) => void>, required: true },
reject: { type: Function as PropType<() => void>, required: true },
},
- setup(props, { root }) {
+ setup(props) {
const fragments = ref();
const { openModal } = useModals();
- const { activeAccount } = useAccounts({ store: root.$store });
+ const { activeAccount } = useAccounts();
const activeNetwork = useGetter('activeNetwork');
const { generateTransactionURDataFragments } = useAirGap();
@@ -88,8 +88,8 @@ export default defineComponent({
async function scanSignedTransaction() {
const scanResult: IACMessageDefinitionObjectV3[] = await openModal(MODAL_READ_QR_CODE, {
- heading: root.$t('modals.importAirGapAccount.scanTitle'),
- title: root.$t('modals.importAirGapAccount.scanDescription'),
+ heading: tg('modals.scanAirGapTx.heading'),
+ title: tg('modals.scanAirGapTx.title'),
icon: 'critical',
});
diff --git a/src/popup/locales/en.json b/src/popup/locales/en.json
index d2f614ba60..c68dfb87a0 100644
--- a/src/popup/locales/en.json
+++ b/src/popup/locales/en.json
@@ -372,7 +372,7 @@
"noWebcamSubtitle": "Please enable your device camera and check again if it is functioning properly.",
"subtitle": "Allow Superhero wallet to access your camera in order to scan QR codes.",
"settings": "Settings",
- "qrCodeHasMultipleFragments": "This transaction consists for multiple QR codes. Keep your camera on the changing QRs and wait until all codes are scanned:"
+ "qrCodeHasMultipleFragments": "This transaction consists of multiple QR codes. Keep your camera on the changing QRs and wait until all codes are scanned:"
},
"name-pointers-help": {
"title": "Name pointers",
diff --git a/src/protocols/aeternity/components/TransferRawTxReview.vue b/src/protocols/aeternity/components/TransferRawTxReview.vue
index 7443c27510..c5c35252f8 100644
--- a/src/protocols/aeternity/components/TransferRawTxReview.vue
+++ b/src/protocols/aeternity/components/TransferRawTxReview.vue
@@ -34,7 +34,7 @@
From 97269e6b1d0cdda6a2725eb23cf75dbc01f2f29a Mon Sep 17 00:00:00 2001
From: martinkaintas
Date: Tue, 26 Mar 2024 11:51:14 +0200
Subject: [PATCH 25/55] chore: issues after rebase
---
package.json | 17 +--
src/composables/accounts.ts | 28 +++-
src/composables/airGap.ts | 120 ++++++++-------
src/composables/notifications.ts | 6 +-
src/popup/components/AccountImportRow.vue | 19 +--
src/popup/components/AccountInfo.vue | 2 +-
.../components/AccountSelectOptionsItem.vue | 32 ++--
src/popup/components/Modals/AccountCreate.vue | 35 +----
.../Modals/AccountSelectOptions.vue | 1 +
.../components/Modals/AirGapConfirmImport.vue | 28 ++--
.../Modals/AirGapSignTransaction.vue | 55 ++++---
src/popup/components/Modals/Help.vue | 2 +
src/popup/components/Modals/QrCodeReader.vue | 60 ++++----
.../components/Modals/TransferSendBase.vue | 21 ++-
src/popup/components/MultiFragmentsQrCode.vue | 7 +-
src/popup/components/TokenAmount.vue | 14 --
.../components/TransferQRCodeGenerator.vue | 59 +++++---
.../TransferSend/TransferReviewBase.vue | 8 +-
src/popup/components/buttons/BtnHelp.vue | 2 +
src/popup/components/form/FormSelect.vue | 1 +
src/popup/locales/en.json | 17 +--
.../components/TransferRawTxReview.vue | 140 ++++++++++++------
.../aeternity/components/TransferReview.vue | 28 +++-
.../components/TransferReviewTip.vue | 2 +-
.../aeternity/libs/AeAccountHdWallet.ts | 23 ++-
.../aeternity/views/AccountCreateModal.vue | 61 +++++---
.../aeternity/views/TransferSendModal.vue | 22 +--
src/store/modules/accounts/airgap.js | 49 ------
src/styles/typography.scss | 7 +
src/types/index.ts | 8 +-
src/utils/common.ts | 21 ++-
vue.config.js | 24 +--
32 files changed, 524 insertions(+), 395 deletions(-)
delete mode 100644 src/store/modules/accounts/airgap.js
diff --git a/package.json b/package.json
index 5a37c8f5f6..229b3403d5 100644
--- a/package.json
+++ b/package.json
@@ -37,6 +37,8 @@
"@aeternity/bip39": "^0.1.0",
"@aeternity/json-bigint": "^0.3.1",
"@aparajita/capacitor-biometric-auth": "^8.0.0",
+ "@airgap/aeternity": "^0.13.25",
+ "@airgap/serializer": "^0.13.25",
"@awesome-cordova-plugins/screen-orientation": "^6.4.0",
"@bitcoin-js/tiny-secp256k1-asmjs": "^2.2.3",
"@capacitor-mlkit/barcode-scanning": "^6.1.0",
@@ -61,6 +63,7 @@
"@ionic/cli": "^7.1.1",
"@ionic/vue": "^7.0.9",
"@ionic/vue-router": "^7.1.2",
+ "@ngraveio/bc-ur": "^1.1.6",
"@rushstack/eslint-patch": "^1.3.2",
"@trapezedev/configure": "^7.0.10",
"@vee-validate/i18n": "^4.12.8",
@@ -69,17 +72,11 @@
"@walletconnect/core": "^2.12.1",
"@walletconnect/utils": "^2.12.1",
"@walletconnect/web3wallet": "^1.11.1",
+ "airgap-coin-lib": "^0.9.14",
"bignumber.js": "^9.0.2",
"bip32": "^4.0.0",
"bitcoinjs-lib": "^6.1.3",
- "@airgap/aeternity": "^0.13.10",
- "@airgap/coinlib-core": "^0.13.10",
- "@airgap/serializer": "^0.13.10",
- "@keystonehq/bc-ur-registry": "^0.5.4",
- "@ledgerhq/hw-transport-webusb": "^6.27.1",
- "@ngraveio/bc-ur": "^1.1.6",
- "@vue/composition-api": "^1.0.3",
- "bs58check": "^2.1.2",
+ "bs58check": "^3.0.1",
"camelcase-keys-deep": "^0.1.0",
"capacitor-native-settings": "^6.0.0",
"cordova-plugin-screen-orientation": "^3.0.3",
@@ -98,9 +95,9 @@
"uuid": "^9.0.1",
"validator": "^13.9.0",
"vee-validate": "^4.12.8",
- "vue": "^3.3.2",
+ "vue": "^3.4.23",
"vue-i18n": "^9.2.2",
- "vue-loader": "^17.2.2",
+ "vue-loader": "^17.4.2",
"vue-router": "^4.2.4",
"web3-eth": "^4.5.0",
"web3-eth-accounts": "^4.1.0",
diff --git a/src/composables/accounts.ts b/src/composables/accounts.ts
index 850fade1d7..1237ae7145 100644
--- a/src/composables/accounts.ts
+++ b/src/composables/accounts.ts
@@ -1,10 +1,12 @@
import { computed, ref } from 'vue';
import { uniq } from 'lodash-es';
+import { Encoded, decode } from '@aeternity/aepp-sdk';
import { generateMnemonic, mnemonicToSeed } from '@aeternity/bip39';
import type {
AccountAddress,
IAccount,
IAccountRaw,
+ IAirgapAccountRaw,
IFormSelectOption,
Protocol,
ProtocolRecord,
@@ -88,15 +90,26 @@ const accounts = computed((): IAccount[] => {
}
const idxList = Object.fromEntries(PROTOCOL_LIST.map(((protocol) => [protocol, 0])));
+ let airGapIdx = 0;
return accountsRaw.value
.map((account, globalIdx) => {
+ if (account.type === ACCOUNT_AIR_GAP_WALLET) {
+ const airGapAccount = account as IAirgapAccountRaw;
+ const idx = airGapIdx;
+ airGapIdx += 1;
+
+ return {
+ globalIdx,
+ idx,
+ publicKey: Buffer.from(decode(airGapAccount.address as Encoded.AccountAddress)),
+ ...airGapAccount,
+ } as unknown as IAccount;
+ }
const idx = idxList[account.protocol];
const hdWallet = ProtocolAdapterFactory
.getAdapter(account.protocol)
- // Type `any` here is used only to satisfy the account address type differences
- // TODO remove `any` when IAccount.address will be set to `string`.
- .getHdWalletAccountFromMnemonicSeed(mnemonicSeed.value, idx) as any;
+ .getHdWalletAccountFromMnemonicSeed(mnemonicSeed.value, idx);
idxList[account.protocol] += 1;
@@ -239,6 +252,14 @@ export function useAccounts() {
return getLastProtocolAccount(protocol)?.idx || 0;
}
+ /**
+ * Note: idx returned for airGap accounts is global and not protocol specific.
+ */
+ function addAirGapAccount(airGapAccount: IAirgapAccountRaw) {
+ accountsRaw.value.push(airGapAccount);
+ return getLastProtocolAccount(PROTOCOLS.aeternity)?.globalIdx || 0;
+ }
+
/**
* Establish the last used account index under the actual seed phrase for each of the protocols
* and collect the raw accounts so they can be stored in the browser storage.
@@ -295,6 +316,7 @@ export function useAccounts() {
discoverAccounts,
isLocalAccountAddress,
addRawAccount,
+ addAirGapAccount,
getAccountByAddress,
getAccountByGlobalIdx,
getLastActiveProtocolAccount,
diff --git a/src/composables/airGap.ts b/src/composables/airGap.ts
index cab3b0c6c4..d942d5aef7 100644
--- a/src/composables/airGap.ts
+++ b/src/composables/airGap.ts
@@ -1,31 +1,46 @@
-import { decode } from '@aeternity/aepp-sdk/es/tx/builder/helpers';
+import { ref } from 'vue';
+import { Encoded } from '@aeternity/aepp-sdk';
import { UR, UREncoder } from '@ngraveio/bc-ur';
import bs58check from 'bs58check';
-import { IACMessageType, SerializerV3 } from '@airgap/serializer';
-import type {
+import { AeternityModule } from '@airgap/aeternity';
+import {
+ MainProtocolSymbols,
+ IACMessageType,
AccountShareResponse,
- IACMessageDefinitionObjectV3,
- TransactionSignResponse,
- TransactionSignRequest,
-} from '@airgap/serializer';
-import { AeternityAddress } from '@airgap/aeternity';
-import { MainProtocolSymbols } from '@airgap/coinlib-core';
-import type { IAccount } from '../types';
-import { ACCOUNT_AIR_GAP_WALLET, MOBILE_SCHEMA, handleUnknownError } from '../popup/utils';
+ AeternityProtocol,
+} from 'airgap-coin-lib';
+import { SerializerV3, IACMessageDefinitionObjectV3 } from '@airgap/serializer';
+
+import type { IAccountRaw } from '@/types';
+import { handleUnknownError } from '@/utils';
+import { ACCOUNT_AIR_GAP_WALLET, MOBILE_SCHEMA, PROTOCOLS } from '@/constants';
+
+const isComposableInitialized = ref(false);
+let serializer: SerializerV3;
export function useAirGap() {
+ (async () => {
+ if (isComposableInitialized.value) {
+ return;
+ }
+ isComposableInitialized.value = true;
+
+ const serializerV3Companion = await new AeternityModule().createV3SerializerCompanion();
+ serializerV3Companion.schemas.forEach((schema) => {
+ SerializerV3.addSchema(schema.type, schema.schema, MainProtocolSymbols.AE);
+ });
+ serializer = SerializerV3.getInstance();
+ })();
+
/**
* Encodes an array of IACMessageDefinitionObjectV3 objects into a UR string.
- * @param data - The array of IACMessageDefinitionObjectV3 objects to encode.
- * @returns The UR string or null if an error occurs.
*/
async function encodeIACMessageDefinitionObjects(data: IACMessageDefinitionObjectV3[]) {
try {
- const serializer = SerializerV3.getInstance();
const serializedData = await serializer.serialize(data);
- const buffer = await bs58check.decode(serializedData);
- const ur = UR.fromBuffer(buffer);
+ const dataUint8Array = bs58check.decode(serializedData);
+ const ur = UR.fromBuffer(Buffer.from(dataUint8Array));
// Set the chunk sizes for single-chunk and multi-chunk encoding.
const SETTINGS_SERIALIZER_SINGLE_CHUNK_SIZE = 500;
@@ -43,66 +58,63 @@ export function useAirGap() {
ur,
SETTINGS_SERIALIZER_MULTI_CHUNK_SIZE,
);
- const fragments = [];
-
- // eslint-disable-next-line no-restricted-syntax, guard-for-in
- for (
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- const _index in [...Array(multiEncoder.fragmentsLength)]
- ) {
- // eslint-disable-next-line no-await-in-loop
- fragments.push(await multiEncoder.nextPart());
- }
+ const fragments: string[] = [];
+
+ [...Array(multiEncoder.fragmentsLength)].forEach(() => {
+ fragments.push(multiEncoder.nextPart());
+ });
+
return fragments;
}
// Encode the UR and return the UR string in upper case.
- return [(await singleEncoder.nextPart()).toUpperCase()];
+ return [(singleEncoder.nextPart()).toUpperCase()];
} catch (error) {
handleUnknownError(error);
return [];
}
}
+ async function deserializeData(data: string) {
+ return serializer.deserialize(data);
+ }
+
/**
- * Extracts shared addresses groups from encoded UR Data.
- * @param serializedData
- * @returns IAccount[]
+ * Extracts shared accounts from deserialized data.
*/
async function extractAccountShareResponseData(
data: IACMessageDefinitionObjectV3[] = [],
- ): Promise {
- return data
- .filter((item) => item.type === IACMessageType.AccountShareResponse)
- .map((item) => {
- const address = AeternityAddress.from(
- (item.payload as AccountShareResponse).publicKey,
- ).asString();
- const publicKey = Buffer.from(decode(address, 'ak'));
-
- return {
- address,
- publicKey,
- name: '',
- showed: true,
- type: ACCOUNT_AIR_GAP_WALLET,
- airGapPublicKey: (item.payload as AccountShareResponse).publicKey,
- } as IAccount;
- });
+ ): Promise {
+ return Promise.all(
+ data
+ .filter((item) => item.type === IACMessageType.AccountShareResponse)
+ .map(async (item) => {
+ const aeProtocol = new AeternityProtocol();
+ const address = await aeProtocol.getAddressFromPublicKey(
+ (item.payload as AccountShareResponse).publicKey,
+ ) as Encoded.AccountAddress;
+
+ return {
+ address,
+ type: ACCOUNT_AIR_GAP_WALLET,
+ airGapPublicKey: (item.payload as AccountShareResponse).publicKey,
+ protocol: PROTOCOLS.aeternity,
+ isRestored: false,
+ };
+ }),
+ );
}
/**
- * Extracts the signed transaction response data from a serialized string
+ * Extracts the signed transaction response data from deserialized data
* and returns the transaction object.
- * @param serializedData - The serialized string to extract data from.
- * @returns The transaction object or null if no transaction object is found.
*/
async function extractSignedTransactionResponseData(
data: IACMessageDefinitionObjectV3[] = [],
): Promise {
const payload = data.find(
(item) => item.type === IACMessageType.TransactionSignResponse,
- )?.payload as TransactionSignResponse;
+ )?.payload as any;
return payload?.transaction || null;
}
@@ -114,7 +126,7 @@ export function useAirGap() {
): Promise {
const id = Math.floor(Math.random() * 90000000 + 10000000);
const callbackURL = `${MOBILE_SCHEMA}?d=`;
- const payload: TransactionSignRequest = {
+ const payload: any = {
callbackURL,
publicKey,
transaction: {
@@ -133,6 +145,8 @@ export function useAirGap() {
}
return {
+ serializer,
+ deserializeData,
encodeIACMessageDefinitionObjects,
extractAccountShareResponseData,
generateTransactionURDataFragments,
diff --git a/src/composables/notifications.ts b/src/composables/notifications.ts
index 48bead89ee..7a89c0e6bf 100644
--- a/src/composables/notifications.ts
+++ b/src/composables/notifications.ts
@@ -18,6 +18,7 @@ import {
PROTOCOLS,
STORAGE_KEYS,
NOTIFICATION_TYPES,
+ ACCOUNT_HD_WALLET,
} from '@/constants';
import migrateNotificationsSettingsVuexToComposable from '@/migrations/007-notifications-settings-vuex-to-composable';
import { useAeNetworkSettings } from '@/protocols/aeternity/composables';
@@ -91,7 +92,10 @@ export function useNotifications({
async function fetchAllNotifications(): Promise {
// TODO: Remove this condition once global filter is ready
- if (activeAccount.value.protocol !== PROTOCOLS.aeternity) {
+ if (
+ activeAccount.value.protocol !== PROTOCOLS.aeternity
+ || activeAccount.value.type !== ACCOUNT_HD_WALLET
+ ) {
return [];
}
diff --git a/src/popup/components/AccountImportRow.vue b/src/popup/components/AccountImportRow.vue
index 8ee3f6064c..05e9032ae7 100644
--- a/src/popup/components/AccountImportRow.vue
+++ b/src/popup/components/AccountImportRow.vue
@@ -1,9 +1,7 @@
@@ -16,12 +14,9 @@ import {
ref,
PropType,
} from 'vue';
-import { Encoded } from '@aeternity/aepp-sdk';
import type { IAccount } from '@/types';
-import { ACCOUNT_AIR_GAP_WALLET } from '@/constants';
-import { aettosToAe } from '@/protocols/aeternity/helpers';
-import { useAeSdk } from '@/composables';
+import { ProtocolAdapterFactory } from '@/lib/ProtocolAdapterFactory';
import { ROUTE_ACCOUNT_DETAILS } from '@/popup/router/routeNames';
import AccountSelectOptionsItem from './AccountSelectOptionsItem.vue';
@@ -34,21 +29,17 @@ export default defineComponent({
account: { type: Object as PropType, required: true },
},
setup(props) {
- const { getAeSdk } = useAeSdk();
const balance = ref(new BigNumber(0));
const numericBalance = computed(() => balance.value.toNumber());
- const isAirGapAccount = computed((): boolean => props.account.type === ACCOUNT_AIR_GAP_WALLET);
onMounted(async () => {
- const sdk = await getAeSdk();
- const fetchedBalance = await sdk.getBalance(props.account.address as Encoded.AccountAddress);
- balance.value = new BigNumber(aettosToAe(fetchedBalance));
+ const adapter = ProtocolAdapterFactory.getAdapter(props.account.protocol);
+ balance.value = new BigNumber(await adapter.fetchBalance(props.account.address));
});
return {
numericBalance,
- isAirGapAccount,
ROUTE_ACCOUNT_DETAILS,
};
},
diff --git a/src/popup/components/AccountInfo.vue b/src/popup/components/AccountInfo.vue
index 2bd8ce21fe..7d0a57f262 100644
--- a/src/popup/components/AccountInfo.vue
+++ b/src/popup/components/AccountInfo.vue
@@ -33,7 +33,7 @@
v-else
data-cy="account-name-number"
class="account-name"
- v-text="getDefaultAccountLabel(account, { isAirGap })"
+ v-text="getDefaultAccountLabel(account)"
/>
diff --git a/src/popup/components/AccountSelectOptionsItem.vue b/src/popup/components/AccountSelectOptionsItem.vue
index ae034a8ac7..28f970229a 100644
--- a/src/popup/components/AccountSelectOptionsItem.vue
+++ b/src/popup/components/AccountSelectOptionsItem.vue
@@ -11,7 +11,7 @@
>
@@ -37,7 +36,7 @@ import {
defineComponent,
PropType,
} from 'vue';
-import type { IFormSelectOption } from '@/types';
+import type { IAccount, IFormSelectOption } from '@/types';
import { useAccounts, useBalances } from '@/composables';
import { getAddressColor } from '@/utils';
import { ProtocolAdapterFactory } from '@/lib/ProtocolAdapterFactory';
@@ -59,22 +58,34 @@ export default defineComponent({
type: Object as PropType,
default: () => ({}),
},
+ airGapAccount: {
+ type: Object as PropType,
+ default: null,
+ },
+ outsideBalance: {
+ type: Number,
+ default: 0,
+ },
selected: Boolean,
- isAirGapAccount: Boolean,
},
setup(props) {
const { getAccountBalance } = useBalances();
const { getAccountByAddress } = useAccounts();
- const account = getAccountByAddress(props.option.value as any);
+ const account = props.airGapAccount ?? getAccountByAddress(props.option.value as string);
+
+ const isAirGap = computed(() => account.type === 'airgap' || props.airGapAccount !== null);
const bgColorStyle = computed(() => ({ '--bg-color': getAddressColor(props.option.value) }));
- const balance = computed(
- () => (props.option?.value)
- ? getAccountBalance(props.option.value as string).toNumber()
- : 0,
- );
+ const balance = computed(() => {
+ switch (true) {
+ case !!props.outsideBalance: return props.outsideBalance;
+ case !!props.option.value:
+ return getAccountBalance(props.option.value.toString()).toNumber();
+ default: return 0;
+ }
+ });
const tokenSymbol = computed(
() => ProtocolAdapterFactory.getAdapter(account!.protocol).coinSymbol,
@@ -85,6 +96,7 @@ export default defineComponent({
balance,
bgColorStyle,
tokenSymbol,
+ isAirGap,
AE_SYMBOL,
};
},
diff --git a/src/popup/components/Modals/AccountCreate.vue b/src/popup/components/Modals/AccountCreate.vue
index d5e663eae8..aafd4bb292 100644
--- a/src/popup/components/Modals/AccountCreate.vue
+++ b/src/popup/components/Modals/AccountCreate.vue
@@ -31,25 +31,20 @@
diff --git a/src/popup/components/Modals/QrCodeReader.vue b/src/popup/components/Modals/QrCodeReader.vue
index a7025ae664..3dbeb075ba 100644
--- a/src/popup/components/Modals/QrCodeReader.vue
+++ b/src/popup/components/Modals/QrCodeReader.vue
@@ -95,7 +95,6 @@ import { useRoute } from 'vue-router';
import { BarcodeScanner } from '@capacitor-mlkit/barcode-scanning';
import type QrScannerType from 'qr-scanner';
import { URDecoder } from '@ngraveio/bc-ur';
-import { SerializerV3 } from '@airgap/serializer';
import bs58check from 'bs58check';
import type { RejectCallback, ResolveCallback } from '@/types';
@@ -129,14 +128,13 @@ export default defineComponent({
},
setup(props) {
const route = useRoute();
- const { setMobileQrScannerVisible } = useUi();
+ const { setMobileQrScannerVisible, scanProgress } = useUi();
const qrCodeVideoEl = ref();
const hasDeviceCamera = ref(false);
const isCameraReady = ref(false);
const cameraPermissionGranted = ref(true);
const isMultiFragmentQr = ref(false);
- const scanProgress = ref(0);
const decoder = ref(new URDecoder());
let browserReader: QrScannerType | null = null;
@@ -151,14 +149,33 @@ export default defineComponent({
browserReader?.destroy();
browserReader = null;
}
+
+ scanProgress.value = -1;
}
- async function getEncoderData() {
+ async function getCombinedData() {
const combinedData = decoder.value.resultUR().decodeCBOR();
const resultUr = bs58check.encode(combinedData);
+ return resultUr;
+ }
- const serializer = SerializerV3.getInstance();
- return serializer.deserialize(resultUr);
+ /**
+ * Returns when the QR code is complete
+ */
+ async function handleReceivePart(text: string) {
+ if (String(text).includes('BYTES/')) {
+ decoder.value.receivePart(text);
+ if (decoder.value.isComplete()) {
+ browserReader?.stop();
+ return getCombinedData();
+ }
+ isMultiFragmentQr.value = true;
+ scanProgress.value = Math.floor(decoder.value.getProgress() * 100);
+ } else {
+ browserReader?.stop();
+ return text;
+ }
+ return null;
}
async function scanMobile(): Promise {
@@ -169,24 +186,17 @@ export default defineComponent({
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve) => {
+ let completeText: string | null = null;
+
const listener = await BarcodeScanner.addListener(
'barcodeScanned',
async ({ barcode }) => {
if (barcode.displayValue) {
- if (barcode.displayValue.includes('BYTES/')) {
- decoder.value.receivePart(barcode.displayValue);
- if (decoder.value.isComplete()) {
- await listener.remove();
- stopReading();
- resolve(await getEncoderData());
- } else {
- isMultiFragmentQr.value = true;
- scanProgress.value = Math.floor(decoder.value.getProgress() * 100);
- }
- } else {
+ completeText = await handleReceivePart(barcode.displayValue);
+ if (completeText) {
await listener.remove();
stopReading();
- resolve(barcode.displayValue);
+ resolve(completeText);
}
}
},
@@ -203,17 +213,9 @@ export default defineComponent({
async (result) => {
const text = result.data;
if (text) {
- stopReading();
- if (String(text).includes('BYTES/')) {
- decoder.value.receivePart(text);
- if (decoder.value.isComplete()) {
- resolve(await getEncoderData());
- } else {
- isMultiFragmentQr.value = true;
- scanProgress.value = Math.floor(decoder.value.getProgress() * 100);
- }
- } else {
- resolve(text);
+ const completeText = await handleReceivePart(text);
+ if (completeText) {
+ resolve(completeText);
}
}
},
diff --git a/src/popup/components/Modals/TransferSendBase.vue b/src/popup/components/Modals/TransferSendBase.vue
index 1c7fd7b30b..192aa890d9 100644
--- a/src/popup/components/Modals/TransferSendBase.vue
+++ b/src/popup/components/Modals/TransferSendBase.vue
@@ -32,7 +32,7 @@
(
+ [TRANSFER_SEND_STEPS.review].includes(props.currentStep as any)
+ && props.isAirGap
+ ));
+
const primaryButtonText = computed(() => {
if (props.customPrimaryButtonText) {
return props.customPrimaryButtonText;
@@ -115,13 +122,25 @@ export default defineComponent({
return t('common.send');
});
+ const primaryButtonIcon = computed(() => {
+ if (showScanButton.value) {
+ return QrScanIcon;
+ }
+ if (showSendButton.value && !props.hideArrowSendIcon) {
+ return ArrowSendIcon;
+ }
+ return null;
+ });
+
return {
isOnline,
primaryButtonText,
+ primaryButtonIcon,
showEditButton,
showCancelButton,
showSendButton,
ArrowSendIcon,
+ QrScanIcon,
TRANSFER_SEND_STEPS,
};
},
diff --git a/src/popup/components/MultiFragmentsQrCode.vue b/src/popup/components/MultiFragmentsQrCode.vue
index d414bc9e6b..6bdfafa3c0 100644
--- a/src/popup/components/MultiFragmentsQrCode.vue
+++ b/src/popup/components/MultiFragmentsQrCode.vue
@@ -10,9 +10,10 @@ import {
PropType,
ref,
watch,
-} from '@vue/composition-api';
+} from 'vue';
import QRCodeStyling, { TypeNumber } from 'qr-code-styling';
-import SHLogo from '../../icons/logo-small-blue.png';
+
+import SHLogo from '@/icons/logo-small-blue.webp';
export default defineComponent({
name: 'MultiFragmentsQrCode',
@@ -57,7 +58,7 @@ export default defineComponent({
watch(() => props.value, () => {
if (updateQrCode.value) {
- clearTimeout(updateQrCode.value);
+ clearInterval(updateQrCode.value);
}
});
diff --git a/src/popup/components/TokenAmount.vue b/src/popup/components/TokenAmount.vue
index fafdd07183..c14c7e44e3 100644
--- a/src/popup/components/TokenAmount.vue
+++ b/src/popup/components/TokenAmount.vue
@@ -27,7 +27,6 @@
@@ -53,8 +52,6 @@ export default defineComponent({
symbol: { type: String, default: null },
protocol: { type: String as PropType, required: true },
vertical: Boolean,
- fiatBelow: Boolean,
- fiatRight: Boolean,
hideFiat: Boolean,
hideSymbol: Boolean,
highPrecision: Boolean,
@@ -157,17 +154,6 @@ export default defineComponent({
color: rgba($color-white, 0.75);
white-space: nowrap;
-
- &.fiat-below {
- display: block;
- margin-left: 0;
- padding-top: 4px;
- white-space: nowrap;
- }
-
- &.fiat-right {
- text-align: right;
- }
}
&.vertical {
diff --git a/src/popup/components/TransferQRCodeGenerator.vue b/src/popup/components/TransferQRCodeGenerator.vue
index 6b44c3254e..b987a2282d 100644
--- a/src/popup/components/TransferQRCodeGenerator.vue
+++ b/src/popup/components/TransferQRCodeGenerator.vue
@@ -7,6 +7,7 @@
:type-number="0"
class="qrcode"
/>
+
@@ -16,56 +17,70 @@ import {
onMounted,
PropType,
ref,
-} from '@vue/composition-api';
-import { useGetter } from '../../composables/vuex';
-import { useAccounts, useAirGap, useSdk } from '../../composables';
+} from 'vue';
+import {
+ encode,
+ Encoded,
+ Encoding,
+ Tag,
+} from '@aeternity/aepp-sdk';
-import type { INetwork } from '../../types';
+import type { TransferFormModel } from '@/types';
+import { AE_CONTRACT_ID } from '@/protocols/aeternity/config';
+import { isAirgapAccount, toShiftedBigNumber } from '@/utils';
+import { aeToAettos } from '@/protocols/aeternity/helpers';
+import {
+ useAccounts,
+ useAirGap,
+ useAeSdk,
+} from '@/composables';
import MultiFragmentsQrCode from './MultiFragmentsQrCode.vue';
-import { TransferFormModel } from './Modals/TransferSend.vue';
-import { AETERNITY_CONTRACT_ID, aeToAettos, convertToken } from '../utils';
+import Loader from './Loader.vue';
export default defineComponent({
components: {
MultiFragmentsQrCode,
+ Loader,
},
props: {
transferData: { type: Object as PropType, required: true },
},
- setup(props, { root }) {
- const activeNetwork = useGetter('activeNetwork');
- const { getSdk } = useSdk({ store: root.$store });
- const { activeAccount } = useAccounts({ store: root.$store });
- const { generateTransactionURDataFragments } = useAirGap();
+ setup(props) {
const fragments = ref();
+ const { nodeNetworkId, getAeSdk } = useAeSdk();
+ const { activeAccount } = useAccounts();
+ const { generateTransactionURDataFragments } = useAirGap();
+
onMounted(async () => {
- const sdk = await getSdk();
+ const aeSdk = await getAeSdk();
const {
amount: amountRaw,
address: recipient,
selectedAsset,
} = props.transferData;
- if (!amountRaw || !recipient || !selectedAsset || !activeAccount.value.airGapPublicKey) {
+ if (!amountRaw || !recipient || !selectedAsset || !isAirgapAccount(activeAccount.value)) {
return null;
}
- const amount = (selectedAsset.contractId === AETERNITY_CONTRACT_ID)
+ const amount = (selectedAsset.contractId === AE_CONTRACT_ID)
? aeToAettos(amountRaw)
- : convertToken(amountRaw, selectedAsset.decimals);
+ : toShiftedBigNumber(amountRaw, -(selectedAsset?.decimals ?? 0));
- const txRaw = await sdk.spendTx({
- senderId: activeAccount.value.address,
+ const txRaw = await aeSdk.buildTx({
+ tag: Tag.SpendTx,
+ senderId: activeAccount.value.address as Encoded.AccountAddress,
recipientId: recipient,
- amount,
- payload: props.transferData.payload,
+ amount: amount.toString(),
+ payload: encode(new TextEncoder().encode(props.transferData.payload), Encoding.Bytearray),
});
+
fragments.value = await generateTransactionURDataFragments(
activeAccount.value.airGapPublicKey,
txRaw,
- activeNetwork.value.networkId,
+ nodeNetworkId.value!,
);
return null;
});
@@ -78,7 +93,7 @@ export default defineComponent({
diff --git a/src/popup/locales/en.json b/src/popup/locales/en.json
index 639457687c..b6c7924a8d 100644
--- a/src/popup/locales/en.json
+++ b/src/popup/locales/en.json
@@ -248,7 +248,7 @@
"btnText": "Pair QR Wallet",
"btnSubtitle": "Connect QR-based Hardware Wallet",
"scanTitle": "Scan QR code",
- "scanDescription": "Scan QR code from your AirGap vault in order to pair wallets and import accounts.",
+ "scanDescription": "Scan the QR code from your AirGap vault in order to pair wallets and import accounts.",
"importConfirmDialog": {
"title": "Import account from AirGap",
"description": "Import æternity account from AirGap into your Superhero wallet in order to manage cryptocurrency funds signing transactions through QR-codes.",
@@ -264,7 +264,7 @@
},
"scanAirGapTx": {
"heading": "Scan QR code",
- "title": "Scan the QR code of the signed transaction from your AirGap vault in order to send it.",
+ "title": "Scan the QR code from your AirGap vault in order to sign the transaction.",
"help": {
"title": "Sign AirGap transaction",
@@ -369,7 +369,8 @@
"noWebcamSubtitle": "Please enable your device camera and check again if it is functioning properly.",
"subtitle": "Allow Superhero wallet to access your camera in order to scan QR codes.",
"settings": "Settings",
- "qrCodeHasMultipleFragments": "This transaction consists of multiple QR codes. Keep your camera on the changing QRs and wait until all codes are scanned:"
+ "qrCodeHasMultipleFragments": "In order to sign this transaction you need to scan multiple QR codes. Keep your camera focused on the changing QRs until all codes have been scanned.",
+ "scanningProgress": "scanned"
},
"name-pointers-help": {
"title": "Name pointers",
From ebbdca3d989043c68bcd34c693b66cb7937e3ec6 Mon Sep 17 00:00:00 2001
From: Martin Kaintas
Date: Thu, 20 Jun 2024 12:19:46 +0300
Subject: [PATCH 27/55] fix: airgap extension issues
---
package-lock.json | 434 +++++++++++++++++-
src/composables/airGap.ts | 14 +-
src/constants/common.ts | 2 +
src/lib/initPolyfills.js | 6 +-
.../components/Modals/ConfirmRawSign.vue | 27 +-
.../Modals/ConfirmTransactionSign.vue | 25 +-
.../aeternity/libs/AeAccountHdWallet.ts | 63 ++-
7 files changed, 538 insertions(+), 33 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index d9be7c2a49..3de4a1c9d1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -13,6 +13,8 @@
"@aeternity/aepp-sdk": "^13.3.2",
"@aeternity/bip39": "^0.1.0",
"@aeternity/json-bigint": "^0.3.1",
+ "@airgap/aeternity": "^0.13.25",
+ "@airgap/serializer": "^0.13.25",
"@aparajita/capacitor-biometric-auth": "^8.0.0",
"@awesome-cordova-plugins/screen-orientation": "^6.4.0",
"@bitcoin-js/tiny-secp256k1-asmjs": "^2.2.3",
@@ -38,6 +40,7 @@
"@ionic/cli": "^7.1.1",
"@ionic/vue": "^7.0.9",
"@ionic/vue-router": "^7.1.2",
+ "@ngraveio/bc-ur": "^1.1.6",
"@rushstack/eslint-patch": "^1.3.2",
"@trapezedev/configure": "^7.0.10",
"@vee-validate/i18n": "^4.12.8",
@@ -46,9 +49,11 @@
"@walletconnect/core": "^2.12.1",
"@walletconnect/utils": "^2.12.1",
"@walletconnect/web3wallet": "^1.11.1",
+ "airgap-coin-lib": "^0.9.14",
"bignumber.js": "^9.0.2",
"bip32": "^4.0.0",
"bitcoinjs-lib": "^6.1.3",
+ "bs58check": "^3.0.1",
"camelcase-keys-deep": "^0.1.0",
"capacitor-native-settings": "^6.0.0",
"cordova-plugin-screen-orientation": "^3.0.3",
@@ -67,9 +72,9 @@
"uuid": "^9.0.1",
"validator": "^13.9.0",
"vee-validate": "^4.12.8",
- "vue": "^3.3.2",
+ "vue": "^3.4.23",
"vue-i18n": "^9.2.2",
- "vue-loader": "^17.2.2",
+ "vue-loader": "^17.4.2",
"vue-router": "^4.2.4",
"web3-eth": "^4.5.0",
"web3-eth-accounts": "^4.1.0",
@@ -242,6 +247,52 @@
"uuid": "dist/bin/uuid"
}
},
+ "node_modules/@airgap/aeternity": {
+ "version": "0.13.27",
+ "resolved": "https://registry.npmjs.org/@airgap/aeternity/-/aeternity-0.13.27.tgz",
+ "integrity": "sha512-4HAwjpkRlzFM+cjuEbHPCR4uirKpGTX7uIYA7jge1J/TRwz1uPSfE77jyLM6/RAODL7XgTadoFA9y2Jvm4HuoQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@airgap/coinlib-core": "^0.13.27",
+ "@airgap/module-kit": "^0.13.27",
+ "@airgap/serializer": "^0.13.27",
+ "@stablelib/ed25519": "^1.0.3"
+ }
+ },
+ "node_modules/@airgap/coinlib-core": {
+ "version": "0.13.27",
+ "resolved": "https://registry.npmjs.org/@airgap/coinlib-core/-/coinlib-core-0.13.27.tgz",
+ "integrity": "sha512-zF0wVGkjtyyOFMH8UerSWY/2QLiqG3enX3ogfs2aBjDm12h/YYNtdypLXmCJsv8xdw/I3lB7RfHqVDWDO/Ia/w==",
+ "license": "MIT",
+ "dependencies": {
+ "@stablelib/blake2b": "^1.0.1",
+ "@stablelib/bytes": "^1.0.1",
+ "@stablelib/ed25519": "^1.0.3",
+ "@stablelib/nacl": "^1.0.4",
+ "@stablelib/utf8": "^1.0.1",
+ "long": "^5.2.0",
+ "protobufjs": "^6.11.2"
+ }
+ },
+ "node_modules/@airgap/module-kit": {
+ "version": "0.13.27",
+ "resolved": "https://registry.npmjs.org/@airgap/module-kit/-/module-kit-0.13.27.tgz",
+ "integrity": "sha512-iSZdBoUaX41z1k1Sq7OrbvQCJoBE4x4kCSyUyqBg7cjBkjPX6rfF+RH97nrzZSV5Zcs4YdjQnCfTW415/mK5gA==",
+ "license": "MIT",
+ "dependencies": {
+ "@airgap/coinlib-core": "^0.13.27",
+ "@airgap/serializer": "^0.13.27"
+ }
+ },
+ "node_modules/@airgap/serializer": {
+ "version": "0.13.27",
+ "resolved": "https://registry.npmjs.org/@airgap/serializer/-/serializer-0.13.27.tgz",
+ "integrity": "sha512-NRAVEilMW/xPK0X4AoB/5x1EOR6Hq/DrYpE09pgOYLT67f6I7SWYU8Zu27m0qXjE3+7bcEtegmwtY8lb5O4h/A==",
+ "license": "MIT",
+ "dependencies": {
+ "@airgap/coinlib-core": "^0.13.27"
+ }
+ },
"node_modules/@ampproject/remapping": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
@@ -5181,6 +5232,12 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
+ "node_modules/@keystonehq/alias-sampling": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/@keystonehq/alias-sampling/-/alias-sampling-0.1.2.tgz",
+ "integrity": "sha512-5ukLB3bcgltgaFfQfYKYwHDUbwHicekYo53fSEa7xhVkAEqsA74kxdIwoBIURmGUtXe3EVIRm4SYlgcrt2Ri0w==",
+ "license": "MIT"
+ },
"node_modules/@ledgerhq/devices": {
"version": "8.4.0",
"resolved": "https://registry.npmjs.org/@ledgerhq/devices/-/devices-8.4.0.tgz",
@@ -5318,6 +5375,21 @@
"node": ">=10"
}
},
+ "node_modules/@ngraveio/bc-ur": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/@ngraveio/bc-ur/-/bc-ur-1.1.13.tgz",
+ "integrity": "sha512-j73akJMV4+vLR2yQ4AphPIT5HZmxVjn/LxpL7YHoINnXoH6ccc90Zzck6/n6a3bCXOVZwBxq+YHwbAKRV+P8Zg==",
+ "license": "MIT",
+ "dependencies": {
+ "@keystonehq/alias-sampling": "^0.1.1",
+ "assert": "^2.0.0",
+ "bignumber.js": "^9.0.1",
+ "cbor-sync": "^1.0.4",
+ "crc": "^3.8.0",
+ "jsbi": "^3.1.5",
+ "sha.js": "^2.4.11"
+ }
+ },
"node_modules/@noble/curves": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.0.tgz",
@@ -5718,6 +5790,106 @@
"integrity": "sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==",
"dev": true
},
+ "node_modules/@polkadot/util": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-2.0.1.tgz",
+ "integrity": "sha512-BjUVLpJ1UtuKpcW2V5NEm5739mmTVO7imQvf87HIpmsR37vC1aWcR6JSwn4O6VBEkCOaxjsVmyOVdzfsbYp4Ew==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@babel/runtime": "^7.8.3",
+ "@types/bn.js": "^4.11.6",
+ "bn.js": "^5.1.1",
+ "camelcase": "^5.3.1",
+ "chalk": "^3.0.0",
+ "ip-regex": "^4.1.0",
+ "moment": "^2.24.0"
+ }
+ },
+ "node_modules/@polkadot/util/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/@polkadot/util/node_modules/camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@polkadot/util/node_modules/chalk": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
+ "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@polkadot/util/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/@polkadot/util/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "license": "MIT"
+ },
+ "node_modules/@polkadot/util/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@polkadot/util/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@polkadot/wasm-crypto": {
+ "version": "0.20.1",
+ "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto/-/wasm-crypto-0.20.1.tgz",
+ "integrity": "sha512-HVmKEQoC7RTS15nGJkQDam8pvwBLjO/JJfhq1OI/zBSqi0KPoh2ZxThkyI+O4CjjY1W+HGvybe0uf0c+VDn62Q==",
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "@polkadot/util": "*"
+ }
+ },
"node_modules/@prettier/plugin-xml": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@prettier/plugin-xml/-/plugin-xml-1.2.0.tgz",
@@ -5727,6 +5899,70 @@
"prettier": ">=2.3"
}
},
+ "node_modules/@protobufjs/aspromise": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
+ "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/base64": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
+ "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/codegen": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
+ "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/eventemitter": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
+ "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/fetch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
+ "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@protobufjs/aspromise": "^1.1.1",
+ "@protobufjs/inquire": "^1.1.0"
+ }
+ },
+ "node_modules/@protobufjs/float": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
+ "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/inquire": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
+ "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/path": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
+ "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/pool": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
+ "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/utf8": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
+ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
+ "license": "BSD-3-Clause"
+ },
"node_modules/@rushstack/eslint-patch": {
"version": "1.10.3",
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.3.tgz",
@@ -5914,6 +6150,17 @@
"@stablelib/int": "^1.0.1"
}
},
+ "node_modules/@stablelib/blake2b": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@stablelib/blake2b/-/blake2b-1.0.1.tgz",
+ "integrity": "sha512-B3KyKoBAjkIFeH7romcF96i+pVFYk7K2SBQ1pZvaxV+epSBXJ+n0C66esUhyz6FF+5FbdQVm77C5fzGFcEZpKA==",
+ "license": "MIT",
+ "dependencies": {
+ "@stablelib/binary": "^1.0.1",
+ "@stablelib/hash": "^1.0.1",
+ "@stablelib/wipe": "^1.0.1"
+ }
+ },
"node_modules/@stablelib/bytes": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@stablelib/bytes/-/bytes-1.0.1.tgz",
@@ -5994,6 +6241,19 @@
"@stablelib/bytes": "^1.0.1"
}
},
+ "node_modules/@stablelib/nacl": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@stablelib/nacl/-/nacl-1.0.4.tgz",
+ "integrity": "sha512-PJ2U/MrkXSKUM8C4qFs87WeCNxri7KQwR8Cdwm9q2sweGuAtTvOJGuW0F3N+zn+ySLPJA98SYWSSpogMJ1gCmw==",
+ "license": "MIT",
+ "dependencies": {
+ "@stablelib/poly1305": "^1.0.1",
+ "@stablelib/random": "^1.0.2",
+ "@stablelib/wipe": "^1.0.1",
+ "@stablelib/x25519": "^1.0.3",
+ "@stablelib/xsalsa20": "^1.0.2"
+ }
+ },
"node_modules/@stablelib/poly1305": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@stablelib/poly1305/-/poly1305-1.0.1.tgz",
@@ -6012,6 +6272,17 @@
"@stablelib/wipe": "^1.0.1"
}
},
+ "node_modules/@stablelib/salsa20": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@stablelib/salsa20/-/salsa20-1.0.2.tgz",
+ "integrity": "sha512-nfjKzw0KTKrrKBasEP+j7UP4I8Xudom8lVZIBCp0kQNARXq72IlSic0oabg2FC1NU68L4RdHrNJDd8bFwrphYA==",
+ "license": "MIT",
+ "dependencies": {
+ "@stablelib/binary": "^1.0.1",
+ "@stablelib/constant-time": "^1.0.1",
+ "@stablelib/wipe": "^1.0.1"
+ }
+ },
"node_modules/@stablelib/sha256": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@stablelib/sha256/-/sha256-1.0.1.tgz",
@@ -6032,6 +6303,12 @@
"@stablelib/wipe": "^1.0.1"
}
},
+ "node_modules/@stablelib/utf8": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@stablelib/utf8/-/utf8-1.0.2.tgz",
+ "integrity": "sha512-sDL1aB2U8FIpj7SjQJMxbOFIFkKvDKQGPHSrYejHZhtLNSK3qHe6ZIfa0woWkOiaJsdYslFzrc0VWXJZHmSIQQ==",
+ "license": "MIT"
+ },
"node_modules/@stablelib/wipe": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@stablelib/wipe/-/wipe-1.0.1.tgz",
@@ -6047,6 +6324,17 @@
"@stablelib/wipe": "^1.0.1"
}
},
+ "node_modules/@stablelib/xsalsa20": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@stablelib/xsalsa20/-/xsalsa20-1.0.2.tgz",
+ "integrity": "sha512-7XdBGbcNgBShmuhDXv1G1WPVCkjZdkb1oPMzSidO7Fve0MHntH6TjFkj5bfLI+aRE+61weO076vYpP/jmaAYog==",
+ "license": "MIT",
+ "dependencies": {
+ "@stablelib/binary": "^1.0.1",
+ "@stablelib/salsa20": "^1.0.2",
+ "@stablelib/wipe": "^1.0.1"
+ }
+ },
"node_modules/@stencil/core": {
"version": "4.18.3",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.18.3.tgz",
@@ -6288,6 +6576,15 @@
"@babel/types": "^7.20.7"
}
},
+ "node_modules/@types/bn.js": {
+ "version": "4.11.6",
+ "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz",
+ "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
"node_modules/@types/body-parser": {
"version": "1.19.5",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
@@ -6488,6 +6785,12 @@
"@types/lodash": "*"
}
},
+ "node_modules/@types/long": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
+ "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==",
+ "license": "MIT"
+ },
"node_modules/@types/mime": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
@@ -9208,6 +9511,17 @@
"node": ">=8"
}
},
+ "node_modules/airgap-coin-lib": {
+ "version": "0.9.14",
+ "resolved": "https://registry.npmjs.org/airgap-coin-lib/-/airgap-coin-lib-0.9.14.tgz",
+ "integrity": "sha512-aio4kXiQo615T7qV2LT7ASfowTJMRdFsrdxG41jKDl5YUgx1KlWTye7mNBjDP8KVWn7WuZ3a8KNY/rO+9LUGaw==",
+ "license": "MIT",
+ "dependencies": {
+ "@polkadot/util": "2.0.1",
+ "@polkadot/wasm-crypto": "0.20.1",
+ "libsodium-wrappers": "0.7.6"
+ }
+ },
"node_modules/ajv": {
"version": "8.16.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.16.0.tgz",
@@ -10971,6 +11285,12 @@
"integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==",
"dev": true
},
+ "node_modules/cbor-sync": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/cbor-sync/-/cbor-sync-1.0.4.tgz",
+ "integrity": "sha512-GWlXN4wiz0vdWWXBU71Dvc1q3aBo0HytqwAZnXF1wOwjqNnDWA1vZ1gDMFLlqohak31VQzmhiYfiCX5QSSfagA==",
+ "license": "MIT"
+ },
"node_modules/chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
@@ -12529,6 +12849,15 @@
"typescript": ">=4"
}
},
+ "node_modules/crc": {
+ "version": "3.8.0",
+ "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz",
+ "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==",
+ "license": "MIT",
+ "dependencies": {
+ "buffer": "^5.1.0"
+ }
+ },
"node_modules/crc-32": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
@@ -12540,6 +12869,30 @@
"node": ">=0.8"
}
},
+ "node_modules/crc/node_modules/buffer": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+ "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.1.13"
+ }
+ },
"node_modules/crc32-stream": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz",
@@ -18135,6 +18488,15 @@
"node": ">= 12"
}
},
+ "node_modules/ip-regex": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz",
+ "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/ipaddr.js": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz",
@@ -21505,6 +21867,12 @@
"js-yaml": "bin/js-yaml.js"
}
},
+ "node_modules/jsbi": {
+ "version": "3.2.5",
+ "resolved": "https://registry.npmjs.org/jsbi/-/jsbi-3.2.5.tgz",
+ "integrity": "sha512-aBE4n43IPvjaddScbvWRA2YlTzKEynHzu7MqOyTipdHucf/VxS63ViCjxYRg86M8Rxwbt/GfzHl1kKERkt45fQ==",
+ "license": "Apache-2.0"
+ },
"node_modules/jsbn": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz",
@@ -21984,6 +22352,21 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/libsodium": {
+ "version": "0.7.6",
+ "resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.6.tgz",
+ "integrity": "sha512-hPb/04sEuLcTRdWDtd+xH3RXBihpmbPCsKW/Jtf4PsvdyKh+D6z2D2gvp/5BfoxseP+0FCOg66kE+0oGUE/loQ==",
+ "license": "ISC"
+ },
+ "node_modules/libsodium-wrappers": {
+ "version": "0.7.6",
+ "resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.6.tgz",
+ "integrity": "sha512-OUO2CWW5bHdLr6hkKLHIKI4raEkZrf3QHkhXsJ1yCh6MZ3JDA7jFD3kCATNquuGSG6MjjPHQIQms0y0gBDzjQg==",
+ "license": "ISC",
+ "dependencies": {
+ "libsodium": "0.7.6"
+ }
+ },
"node_modules/lilconfig": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
@@ -22521,6 +22904,12 @@
"node": ">=8"
}
},
+ "node_modules/long": {
+ "version": "5.2.3",
+ "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz",
+ "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==",
+ "license": "Apache-2.0"
+ },
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -23048,6 +23437,15 @@
"integrity": "sha512-23g5BFj4zdQL/b6tor7Ji+QY4pEfNH784BMslY9Qb0UnJWRAt+lQGLYmRaM0KDBwIG23ffEBELhZDP2rhi9f/Q==",
"dev": true
},
+ "node_modules/moment": {
+ "version": "2.30.1",
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
+ "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/mri": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
@@ -25531,6 +25929,38 @@
"integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==",
"dev": true
},
+ "node_modules/protobufjs": {
+ "version": "6.11.4",
+ "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz",
+ "integrity": "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==",
+ "hasInstallScript": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@protobufjs/aspromise": "^1.1.2",
+ "@protobufjs/base64": "^1.1.2",
+ "@protobufjs/codegen": "^2.0.4",
+ "@protobufjs/eventemitter": "^1.1.0",
+ "@protobufjs/fetch": "^1.1.0",
+ "@protobufjs/float": "^1.0.2",
+ "@protobufjs/inquire": "^1.1.0",
+ "@protobufjs/path": "^1.1.2",
+ "@protobufjs/pool": "^1.1.0",
+ "@protobufjs/utf8": "^1.1.0",
+ "@types/long": "^4.0.1",
+ "@types/node": ">=13.7.0",
+ "long": "^4.0.0"
+ },
+ "bin": {
+ "pbjs": "bin/pbjs",
+ "pbts": "bin/pbts"
+ }
+ },
+ "node_modules/protobufjs/node_modules/long": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
+ "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==",
+ "license": "Apache-2.0"
+ },
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
diff --git a/src/composables/airGap.ts b/src/composables/airGap.ts
index d942d5aef7..e18ef35890 100644
--- a/src/composables/airGap.ts
+++ b/src/composables/airGap.ts
@@ -12,11 +12,11 @@ import {
import { SerializerV3, IACMessageDefinitionObjectV3 } from '@airgap/serializer';
import type { IAccountRaw } from '@/types';
-import { handleUnknownError } from '@/utils';
+import { handleUnknownError, watchUntilTruthy } from '@/utils';
import { ACCOUNT_AIR_GAP_WALLET, MOBILE_SCHEMA, PROTOCOLS } from '@/constants';
const isComposableInitialized = ref(false);
-let serializer: SerializerV3;
+const serializer = ref();
export function useAirGap() {
(async () => {
@@ -29,17 +29,18 @@ export function useAirGap() {
serializerV3Companion.schemas.forEach((schema) => {
SerializerV3.addSchema(schema.type, schema.schema, MainProtocolSymbols.AE);
});
- serializer = SerializerV3.getInstance();
+ serializer.value = SerializerV3.getInstance();
})();
/**
* Encodes an array of IACMessageDefinitionObjectV3 objects into a UR string.
*/
async function encodeIACMessageDefinitionObjects(data: IACMessageDefinitionObjectV3[]) {
+ await watchUntilTruthy(serializer);
try {
- const serializedData = await serializer.serialize(data);
+ const serializedData = await serializer.value?.serialize(data);
- const dataUint8Array = bs58check.decode(serializedData);
+ const dataUint8Array = bs58check.decode(serializedData!);
const ur = UR.fromBuffer(Buffer.from(dataUint8Array));
// Set the chunk sizes for single-chunk and multi-chunk encoding.
@@ -76,7 +77,8 @@ export function useAirGap() {
}
async function deserializeData(data: string) {
- return serializer.deserialize(data);
+ await watchUntilTruthy(serializer);
+ return serializer.value?.deserialize(data);
}
/**
diff --git a/src/constants/common.ts b/src/constants/common.ts
index b2c02ed6b7..c4b0ccf63c 100644
--- a/src/constants/common.ts
+++ b/src/constants/common.ts
@@ -358,6 +358,8 @@ export const POPUP_ACTIONS = {
reject: 'reject',
} as const;
+export const AIRGAP_SIGNED_TRANSACTION_MESSAGE_TYPE = 'airgap-signed-transaction';
+
export const PERMISSION_DEFAULTS: IPermission = {
host: '',
name: '',
diff --git a/src/lib/initPolyfills.js b/src/lib/initPolyfills.js
index 6fd335759a..a038a5bdc8 100644
--- a/src/lib/initPolyfills.js
+++ b/src/lib/initPolyfills.js
@@ -1,6 +1,10 @@
if (process.env.IS_EXTENSION) {
import('webextension-polyfill').then((webExtensionPolyfill) => {
- window.browser = webExtensionPolyfill;
+ try {
+ window.browser = webExtensionPolyfill;
+ } catch (error) {
+ browser = webExtensionPolyfill;
+ }
});
} else {
window.browser = {
diff --git a/src/popup/components/Modals/ConfirmRawSign.vue b/src/popup/components/Modals/ConfirmRawSign.vue
index bff4c1e372..63f880437b 100644
--- a/src/popup/components/Modals/ConfirmRawSign.vue
+++ b/src/popup/components/Modals/ConfirmRawSign.vue
@@ -8,7 +8,7 @@
import { computed, defineComponent, onUnmounted } from 'vue';
-import { PROTOCOLS } from '@/constants';
+import {
+ AIRGAP_SIGNED_TRANSACTION_MESSAGE_TYPE,
+ MODAL_AIR_GAP_SIGN_TRANSACTION,
+ PROTOCOLS,
+ RUNNING_IN_POPUP,
+} from '@/constants';
import { RejectedByUserError } from '@/lib/errors';
-import { useAccounts, usePopupProps } from '@/composables';
+import { useAccounts, useModals, usePopupProps } from '@/composables';
import Modal from '../Modal.vue';
import TransactionInfo from '../TransactionInfo.vue';
@@ -81,12 +86,26 @@ export default defineComponent({
setup() {
const { popupProps, sender, setPopupProps } = usePopupProps();
const { getLastActiveProtocolAccount } = useAccounts();
+ const { openModal } = useModals();
const activeAccount = getLastActiveProtocolAccount(PROTOCOLS.aeternity);
const dataAsString = computed((): string => popupProps.value?.txBase64?.toString() || '');
- function confirm() {
+ async function confirm() {
+ if (RUNNING_IN_POPUP && activeAccount?.type === 'airgap') {
+ const signgedTransaction = await openModal(
+ MODAL_AIR_GAP_SIGN_TRANSACTION,
+ { txRaw: popupProps.value?.txBase64 },
+ );
+ if (signgedTransaction) {
+ browser.runtime.sendMessage({
+ type: AIRGAP_SIGNED_TRANSACTION_MESSAGE_TYPE,
+ payload: signgedTransaction,
+ target: 'offscreen',
+ });
+ }
+ }
popupProps.value?.resolve();
}
diff --git a/src/popup/components/Modals/ConfirmTransactionSign.vue b/src/popup/components/Modals/ConfirmTransactionSign.vue
index 8eb3f5150c..b99d590f2b 100644
--- a/src/popup/components/Modals/ConfirmTransactionSign.vue
+++ b/src/popup/components/Modals/ConfirmTransactionSign.vue
@@ -165,7 +165,7 @@
:disabled="!!error || verifying"
:icon="verifying ? AnimatedSpinner : null"
:text="verifying ? $t('common.verifying') : $t('common.confirm')"
- @click="popupProps?.resolve()"
+ @click="confirm()"
/>
@@ -203,7 +203,10 @@ import { AeDecodedCallData } from '@/protocols/aeternity/types';
import { tg } from '@/popup/plugins/i18n';
import { RejectedByUserError } from '@/lib/errors';
import {
+ AIRGAP_SIGNED_TRANSACTION_MESSAGE_TYPE,
+ MODAL_AIR_GAP_SIGN_TRANSACTION,
PROTOCOLS,
+ RUNNING_IN_POPUP,
SUPERHERO_CHAT_URLS,
TX_DIRECTION,
} from '@/constants';
@@ -217,6 +220,7 @@ import {
useAccounts,
useAeSdk,
useFungibleTokens,
+ useModals,
usePopupProps,
useTransactionData,
} from '@/composables';
@@ -272,6 +276,7 @@ export default defineComponent({
const { getLastActiveProtocolAccount } = useAccounts();
const { popupProps, setPopupProps } = usePopupProps();
const { getProtocolAvailableTokens, getTxAssetSymbol } = useFungibleTokens();
+ const { openModal } = useModals();
const protocol = popupProps.value?.protocol || PROTOCOLS.aeternity;
const adapter = ProtocolAdapterFactory.getAdapter(protocol);
@@ -410,6 +415,23 @@ export default defineComponent({
return (idx === 0) ? t('pages.signTransaction.from') : t('pages.signTransaction.to');
}
+ async function confirm() {
+ if (RUNNING_IN_POPUP && activeAccount?.type === 'airgap') {
+ const signgedTransaction = await openModal(
+ MODAL_AIR_GAP_SIGN_TRANSACTION,
+ { txRaw: popupProps.value?.txBase64 },
+ );
+ if (signgedTransaction) {
+ browser.runtime.sendMessage({
+ type: AIRGAP_SIGNED_TRANSACTION_MESSAGE_TYPE,
+ payload: signgedTransaction,
+ target: 'offscreen',
+ });
+ }
+ }
+ popupProps.value?.resolve();
+ }
+
function cancel() {
popupProps.value?.reject(new RejectedByUserError());
}
@@ -530,6 +552,7 @@ export default defineComponent({
PAYLOAD_FIELD,
PROTOCOLS,
appName,
+ confirm,
cancel,
decodedCallData,
decodedPayload,
diff --git a/src/protocols/aeternity/libs/AeAccountHdWallet.ts b/src/protocols/aeternity/libs/AeAccountHdWallet.ts
index b6532e1ddd..4eda28baae 100644
--- a/src/protocols/aeternity/libs/AeAccountHdWallet.ts
+++ b/src/protocols/aeternity/libs/AeAccountHdWallet.ts
@@ -18,8 +18,13 @@ import { useModals } from '@/composables/modals';
import { useAccounts } from '@/composables/accounts';
import { useAeMiddleware } from '@/protocols/aeternity/composables';
import { usePermissions } from '@/composables/permissions';
-import { MODAL_AIR_GAP_SIGN_TRANSACTION, PROTOCOLS } from '@/constants';
-import { handleUnknownError } from '@/utils';
+import {
+ AIRGAP_SIGNED_TRANSACTION_MESSAGE_TYPE,
+ IS_OFFSCREEN_TAB,
+ MODAL_AIR_GAP_SIGN_TRANSACTION,
+ PROTOCOLS,
+} from '@/constants';
+import { handleUnknownError, isAirgapAccount } from '@/utils';
interface InternalOptions {
fromAccount?: Encoded.AccountAddress;
@@ -33,30 +38,42 @@ const TAGS_TO_SIGN_WITHOUT_PERMISSION: Tag[] = [Tag.SpendTx, Tag.PayingForTx];
export class AeAccountHdWallet extends MemoryAccount {
override readonly address: Encoded.AccountAddress;
- readonly isAirgap: boolean = false;
-
nodeNetworkId: Ref
;
constructor(nodeNetworkId: Ref) {
super(Buffer.alloc(64));
- const { getLastActiveProtocolAccount } = useAccounts();
- const aeAccount = getLastActiveProtocolAccount(PROTOCOLS.aeternity);
+ const aeAccount = this.getAccount();
this.address = aeAccount!.address as Encoded.AccountAddress;
- this.isAirgap = aeAccount?.type === 'airgap';
this.nodeNetworkId = nodeNetworkId;
}
override async signTransaction(
txBase64: Encoded.Transaction,
- options: Parameters[1],
+ options: Parameters[1] & InternalOptions,
): Promise {
if (!this.nodeNetworkId.value) {
throw new Error('Not connected to any network');
}
- if (this.isAirgap) {
- const { openModal } = useModals();
- return openModal(MODAL_AIR_GAP_SIGN_TRANSACTION, { txRaw: txBase64 });
+ const account = this.getAccount(options?.fromAccount);
+ let signedTx: Promise | undefined;
+ if (isAirgapAccount(account!)) {
+ // If the tab is offscreen, we need to listen for the signed transaction
+ // which will be sent from the confirmation modal
+ if (IS_OFFSCREEN_TAB) {
+ signedTx = new Promise((resolve) => {
+ const handleMessage = async (msg: any) => {
+ if (msg.type === AIRGAP_SIGNED_TRANSACTION_MESSAGE_TYPE) {
+ browser.runtime.onMessage.removeListener(handleMessage);
+ resolve(msg.payload);
+ }
+ };
+ browser.runtime.onMessage.addListener(handleMessage);
+ });
+ } else {
+ const { openModal } = useModals();
+ return openModal(MODAL_AIR_GAP_SIGN_TRANSACTION, { txRaw: txBase64 });
+ }
}
const tx = unpackTx(txBase64) as any as ITx;
@@ -72,6 +89,10 @@ export class AeAccountHdWallet extends MemoryAccount {
}
}
+ if (isAirgapAccount(account!) && IS_OFFSCREEN_TAB && signedTx) {
+ return signedTx;
+ }
+
return super.signTransaction(txBase64, {
...options, // Mainly to pass the `fromAccount` property
aeppOrigin: undefined,
@@ -81,9 +102,10 @@ export class AeAccountHdWallet extends MemoryAccount {
override async signMessage(
message: string,
- options: Parameters[1],
+ options: Parameters[1] & InternalOptions,
): Promise {
- if (this.isAirgap) {
+ const account = this.getAccount(options?.fromAccount);
+ if (isAirgapAccount(account!)) {
throw new Error('AirGap signMessage not implemented yet');
}
@@ -204,7 +226,8 @@ export class AeAccountHdWallet extends MemoryAccount {
data: string | Uint8Array,
options?: Record & InternalOptions,
): Promise {
- if (this.isAirgap) {
+ const account = this.getAccount(options?.fromAccount);
+ if (isAirgapAccount(account!)) {
throw new Error('AirGap sign not implemented yet');
}
if (options?.aeppOrigin) {
@@ -218,15 +241,17 @@ export class AeAccountHdWallet extends MemoryAccount {
throw new RpcRejectedByUserError('Rejected by user');
}
}
- const { getLastActiveProtocolAccount, getAccountByAddress } = useAccounts();
- const account = (options?.fromAccount)
- ? getAccountByAddress(options.fromAccount)
- : getLastActiveProtocolAccount(PROTOCOLS.aeternity);
-
if (account && account.secretKey && account.protocol === PROTOCOLS.aeternity) {
return sign(data, account.secretKey);
}
throw new Error('Unsupported protocol');
}
+
+ getAccount(fromAccount?: Encoded.AccountAddress) {
+ const { getLastActiveProtocolAccount, getAccountByAddress } = useAccounts();
+ return fromAccount
+ ? getAccountByAddress(fromAccount)
+ : getLastActiveProtocolAccount(PROTOCOLS.aeternity);
+ }
}
From 67c26363ac72e1f3b574a94570c6b204892ccdbc Mon Sep 17 00:00:00 2001
From: Martin Kaintas
Date: Wed, 15 May 2024 13:27:20 +0300
Subject: [PATCH 28/55] feat(airgap): implement no qr flow & new design
---
src/constants/common.ts | 5 +-
src/popup/components/ModalHeader.vue | 10 +-
...irmImport.vue => AirGapImportAccounts.vue} | 96 ++++--
.../Modals/AirGapSignTransaction.vue | 219 +++++++++---
src/popup/components/Modals/QrCodeReader.vue | 2 -
.../components/Modals/TransferReceiveBase.vue | 2 +-
.../components/Modals/TransferSendBase.vue | 24 +-
src/popup/components/MultiFragmentsQrCode.vue | 79 -----
src/popup/components/QrCode.vue | 101 +++++-
.../components/TransferQRCodeGenerator.vue | 41 ++-
.../TransferSend/TransferReviewBase.vue | 2 +
src/popup/components/form/FormInputWithQr.vue | 80 +++++
src/popup/components/form/FormTextarea.vue | 4 +-
src/popup/locales/en.json | 37 +-
src/popup/router/modals.ts | 8 +-
.../components/TransferRawTxReview.vue | 222 ------------
.../aeternity/components/TransferReview.vue | 35 +-
.../components/TransferSignedTxReview.vue | 322 ++++++++++++++++++
.../aeternity/views/AccountCreateModal.vue | 27 +-
.../aeternity/views/TransferSendModal.vue | 34 +-
src/types/index.ts | 2 +-
21 files changed, 826 insertions(+), 526 deletions(-)
rename src/popup/components/Modals/{AirGapConfirmImport.vue => AirGapImportAccounts.vue} (56%)
delete mode 100644 src/popup/components/MultiFragmentsQrCode.vue
create mode 100644 src/popup/components/form/FormInputWithQr.vue
delete mode 100644 src/protocols/aeternity/components/TransferRawTxReview.vue
create mode 100644 src/protocols/aeternity/components/TransferSignedTxReview.vue
diff --git a/src/constants/common.ts b/src/constants/common.ts
index c4b0ccf63c..3618f01308 100644
--- a/src/constants/common.ts
+++ b/src/constants/common.ts
@@ -329,8 +329,7 @@ export const MODAL_WALLET_CONNECT = 'wallet-connect';
export const MODAL_CLAIM_GIFT_CARD = 'claim-gift-card';
export const MODAL_SECURE_LOGIN = 'secure-login';
export const MODAL_ENABLE_SECURE_LOGIN = 'enable-secure-login';
-export const MODAL_AIR_GAP_CONFIRM_IMPORT = 'air-gap-confirm-import';
-export const MODAL_AIR_GAP_TRANSACTION_QR = 'air-gap-transaction-qr';
+export const MODAL_AIR_GAP_IMPORT_ACCOUNTS = 'air-gap-import-accounts';
export const MODAL_AIR_GAP_SIGN_TRANSACTION = 'air-gap-sign-transaction';
export const POPUP_TYPE_CONNECT = 'connectConfirm';
@@ -415,7 +414,7 @@ export const TRANSFER_SEND_STEPS = {
form: 'form',
review: 'review',
reviewTip: 'tip',
- reviewRawTx: 'rawTx',
+ airGapSign: 'airGapSign',
} as const;
export const DEFAULT_LOCALE = 'en-US';
diff --git a/src/popup/components/ModalHeader.vue b/src/popup/components/ModalHeader.vue
index 486f7eaedb..3883c5b503 100644
--- a/src/popup/components/ModalHeader.vue
+++ b/src/popup/components/ModalHeader.vue
@@ -1,5 +1,8 @@
-