diff --git a/.gitignore b/.gitignore
index 8bdcabe..aa4f431 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,6 +13,7 @@ dist
dist-ssr
coverage
*.local
+mx-xportalhub-extension-*/**
/cypress/videos/
/cypress/screenshots/
diff --git a/manifest.json b/manifest.json
index 4173f70..efb025e 100644
--- a/manifest.json
+++ b/manifest.json
@@ -2,7 +2,7 @@
"manifest_version": 3,
"name": "XPortal Hub test",
"description": "Test your xPortal Hub integration. Only for development purposes!!",
- "version": "1.0.0",
+ "version": "1.1.0",
"permissions": ["activeTab", "storage"],
"action": {
"default_popup": "index.html",
diff --git a/package-lock.json b/package-lock.json
index cdeb483..1fcec66 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,18 +1,19 @@
{
"name": "mx-xportalhub-chrome",
- "version": "0.0.0",
+ "version": "1.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "mx-xportalhub-chrome",
- "version": "0.0.0",
+ "version": "1.1.0",
"dependencies": {
"@multiversx/sdk-core": "^12.2.0",
"@multiversx/sdk-native-auth-client": "^1.0.2",
"@multiversx/sdk-network-providers": "^1.4.0",
"@multiversx/sdk-wallet": "^4.1.0",
"buffer": "^6.0.3",
+ "readable-stream": "^4.4.0",
"sass": "^1.62.1",
"vue": "^3.2.47"
},
@@ -552,9 +553,9 @@
}
},
"node_modules/@multiversx/sdk-core": {
- "version": "12.2.0",
- "resolved": "https://registry.npmjs.org/@multiversx/sdk-core/-/sdk-core-12.2.0.tgz",
- "integrity": "sha512-PbHLlG9+UWiYY71NGPZ5bjrvaxFvwYU6NQxeU2Pnp99Csm1O42pySq5NVqaKhcZh3F6JDkUiNhvcjnKI3Lt+EQ==",
+ "version": "12.2.1",
+ "resolved": "https://registry.npmjs.org/@multiversx/sdk-core/-/sdk-core-12.2.1.tgz",
+ "integrity": "sha512-jnVwr7ljZ3AD5rN/lifrNq5uZU7CenNVqnCzu8Ks1fVJ/TOZM6VUkVXoSvceweXY0vw7FyH6FBVRUQqWPWbZzg==",
"dependencies": {
"@multiversx/sdk-transaction-decoder": "1.0.2",
"bech32": "1.1.4",
@@ -608,9 +609,9 @@
"integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg=="
},
"node_modules/@multiversx/sdk-wallet": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/@multiversx/sdk-wallet/-/sdk-wallet-4.1.0.tgz",
- "integrity": "sha512-XjWWx/2LSFBJy6+YypLwOrOci+S0aYsawGVpYh61oNP0LAlh4HoDCFx2fzqqmOtmA4QTNrjkEaypneSdAaujUQ==",
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@multiversx/sdk-wallet/-/sdk-wallet-4.2.0.tgz",
+ "integrity": "sha512-EjSb9AnqMcpmDjZ7ebkUpOzpTfxj1plTuVXwZ6AaqJsdpxMfrE2izbPy18+bg5xFlr8V27wYZcW8zOhkBR50BA==",
"dependencies": {
"@multiversx/sdk-bls-wasm": "0.3.5",
"@noble/ed25519": "1.7.3",
@@ -1265,6 +1266,17 @@
"integrity": "sha512-CqTpxOlUCPWRNUPZDxT5v2NnHXA4oox612iUGnmTUGQFhZ1Gkj8kirtl/2wcF6MqX7+PqqicZzOCBKKfIn0dww==",
"dev": true
},
+ "node_modules/abort-controller": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
+ "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+ "dependencies": {
+ "event-target-shim": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=6.5"
+ }
+ },
"node_modules/acorn": {
"version": "8.8.2",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
@@ -2360,6 +2372,22 @@
"node": ">=0.10.0"
}
},
+ "node_modules/event-target-shim": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
+ "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/events": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+ "engines": {
+ "node": ">=0.8.x"
+ }
+ },
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -2816,6 +2844,19 @@
"node": ">=4"
}
},
+ "node_modules/hash-base/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/he": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
@@ -3287,6 +3328,19 @@
"node": ">=10.0.0"
}
},
+ "node_modules/keccak/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/levn": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
@@ -4025,6 +4079,14 @@
"node": ">=6.0.0"
}
},
+ "node_modules/process": {
+ "version": "0.11.10",
+ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+ "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
+ "engines": {
+ "node": ">= 0.6.0"
+ }
+ },
"node_modules/protobufjs": {
"version": "6.11.3",
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz",
@@ -4133,16 +4195,17 @@
}
},
"node_modules/readable-stream": {
- "version": "3.6.2",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
- "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.4.0.tgz",
+ "integrity": "sha512-kDMOq0qLtxV9f/SQv522h8cxZBqNZXuXNyjyezmfAAuribMyVXziljpQ/uQhfE1XLg2/TLTW2DsnoE4VAi/krg==",
"dependencies": {
- "inherits": "^2.0.3",
- "string_decoder": "^1.1.1",
- "util-deprecate": "^1.0.1"
+ "abort-controller": "^3.0.0",
+ "buffer": "^6.0.3",
+ "events": "^3.3.0",
+ "process": "^0.11.10"
},
"engines": {
- "node": ">= 6"
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/readdirp": {
diff --git a/package.json b/package.json
index 7442705..b05e7a4 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "mx-xportalhub-chrome",
- "version": "1.0.0",
+ "version": "1.1.0",
"type": "module",
"private": true,
"scripts": {
@@ -18,6 +18,7 @@
"@multiversx/sdk-network-providers": "^1.4.0",
"@multiversx/sdk-wallet": "^4.1.0",
"buffer": "^6.0.3",
+ "readable-stream": "^4.4.0",
"sass": "^1.62.1",
"vue": "^3.2.47"
},
diff --git a/publish/mx-xportalhub-extension-1.0.0.zip b/publish/mx-xportalhub-extension-1.0.0.zip
deleted file mode 100644
index 71703a4..0000000
Binary files a/publish/mx-xportalhub-extension-1.0.0.zip and /dev/null differ
diff --git a/src/App.vue b/src/App.vue
index ccac315..71d8803 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -12,6 +12,7 @@
Logout
+
Login in the current tab
@@ -23,6 +24,7 @@ import SignTransactions from "@/components/SignTransactions.vue";
import {useWallet} from "@/WalletManager";
import { onMounted, ref} from "vue";
import ObfuscatedAddress from "@/components/ObfuscatedAddress.vue";
+import TokenConfiguration from "@/components/TokenConfiguration.vue";
const { address, transactions, updateWallet, load, logout, generateNativeToken, transactionDetail } = useWallet();
async function onWalletChange(wallet: string) {
@@ -39,10 +41,10 @@ async function login() {
console.log("Tab", tabs)
const [tab] = tabs;
if(!tab?.url) return;
- const separator = tab.url.includes("?") ? "&" : "?";
- const url = `${tab.url}${separator}accessToken=${token}`;
- console.log("Update url", url);
- chrome.tabs.update(tab.id, { url });
+ const currentUrl = new URL(tab.url);
+ currentUrl.searchParams.set("accessToken", token);
+ console.log("currentUrl", currentUrl.toString());
+ chrome.tabs.update(tab.id, { url: currentUrl.toString() });
});
}
@@ -125,9 +127,11 @@ onMounted(() => {
.login {
display: flex;
+ flex-direction: column;
margin-top: 40px;
font-size: 1.5rem;
height: 40%;
+ width: 100%;
align-items: center;
justify-content: center;
}
diff --git a/src/WalletManager.ts b/src/WalletManager.ts
index a6afaf7..0c238bb 100644
--- a/src/WalletManager.ts
+++ b/src/WalletManager.ts
@@ -1,10 +1,11 @@
import {UserSigner} from "@multiversx/sdk-wallet";
import type {UserAddress} from "@multiversx/sdk-wallet/out/userAddress";
import {NativeAuthClient} from "@multiversx/sdk-native-auth-client";
-import {computed, reactive, ref} from "vue";
+import {computed, reactive, ref, watch} from "vue";
import {Transaction} from "@multiversx/sdk-core";
import {Signature} from "@multiversx/sdk-wallet/out/signature";
-import type {IPlainTransactionObject} from "@multiversx/sdk-core/out";
+import type {IPlainTransactionObject} from "@multiversx/sdk-core";
+import {Address, SignableMessage} from "@multiversx/sdk-core";
export type SignResult = {
transactions?: IPlainTransactionObject[],
@@ -13,35 +14,78 @@ export type SignResult = {
export const useWallet = () => {
const address = ref(undefined);
const transactions = reactive([]);
+ const ttl = ref();
+ const origin = ref();
let signer: UserSigner | undefined;
const updateWallet = async (wallet: string)=> {
+ if(!wallet) {
+ return;
+ }
signer = UserSigner.fromPem(wallet);
address.value = signer.getAddress();
}
+ watch(ttl, async (newTtl, oldValue) => {
+ if(!newTtl || newTtl === oldValue) {
+ return;
+ }
+ console.log("new ttl", newTtl);
+ await chrome.storage.local.set({ttl: newTtl})
+ });
+ watch(origin, async (newOrigin, oldValue) => {
+ if(!newOrigin || newOrigin === oldValue) {
+ return;
+ }
+ console.log("new origin", newOrigin)
+ await chrome.storage.local.set({origin: newOrigin})
+ });
+
const load = async () => {
- const result = await chrome.storage.local.get(["wallet", "transactions"]);
+ const result = await chrome.storage.local.get(["wallet", "transactions", "ttl", "origin"]);
if(result.transactions) {
transactions.splice(0);
result.transactions
.map((transaction: any) => Transaction.fromPlainObject(transaction))
.forEach((transaction: Transaction) => transactions.push(transaction));
}
+ await loadTokenConfig();
await updateWallet(result.wallet);
}
+ const loadTokenConfig = async () => {
+ const result = await chrome.storage.local.get(["ttl", "origin"]);
+ if(result.ttl) {
+ console.log("setting ttl", result.ttl);
+ ttl.value = result.ttl;
+ } else {
+ console.log("setting default ttl", 86400);
+ ttl.value = 86400;
+ }
+ if(result.origin) {
+ console.log("setting origin", result.origin);
+ origin.value = result.origin;
+ } else {
+ console.log("setting default origin", "https://api.multiversx.com");
+ origin.value = "https://api.multiversx.com";
+ }
+ }
const saveWallet = async (wallet: string) => {
await updateWallet(wallet);
await chrome.storage.local.set({wallet});
}
-
-
- const sign = async (message: Buffer) => {
+ const sign = async (bech32Address: string, message: string) => {
if (!signer) {
throw new Error("Wallet not loaded");
}
- return signer.sign(message);
+ const address = new Address(bech32Address);
+ const signableMessage = new SignableMessage({
+ address,
+ message: Buffer.from(message, 'utf8'),
+ });
+
+ const cryptoMessage = Buffer.from(signableMessage.serializeForSigning().toString('hex'), "hex");
+ return signer.sign(cryptoMessage);
}
const signTransactions = async (transactions: Transaction[]): Promise => {
@@ -75,17 +119,21 @@ export const useWallet = () => {
}
}
- const generateNativeToken = async (apiUrl:string = "https://api.multiversx.com") => {
+ const generateNativeToken = async () => {
if (!signer || !address.value) {
throw new Error("Wallet not loaded");
}
- const client = new NativeAuthClient({ origin: apiUrl });
+ const tokenConfig = await chrome.storage.local.get(["ttl", "origin"]);
+ const ttl = tokenConfig.ttl || 86400;
+ const origin = tokenConfig.origin || "https://api.multiversx.com";
+ const client = new NativeAuthClient({ origin, expirySeconds: ttl });
+
const bech32Address = address.value.bech32();
const init = await client.initialize({ timestamp: Date.now() });
console.log("init", init);
console.log("address", bech32Address);
- const message = Buffer.from(`${bech32Address}${init}`);
- const signature = await sign(message);
+ const message = `${address.value}${init}`;
+ const signature = await sign(bech32Address, message);
const hexSignature = signature.toString('hex');
console.log("stringSignature", hexSignature);
return client.getToken(bech32Address, init, hexSignature);
@@ -118,5 +166,5 @@ export const useWallet = () => {
}
})
- return { address, transactions, updateWallet, load, saveWallet, sign, generateNativeToken, logout, transactionDetail, signTransactions};
+ return { address, transactions, ttl, origin, updateWallet, load, saveWallet, sign, generateNativeToken, logout, transactionDetail, signTransactions, loadTokenConfig};
}
\ No newline at end of file
diff --git a/src/XPortalHubSimulator.ts b/src/XPortalHubSimulator.ts
deleted file mode 100644
index 2d179e2..0000000
--- a/src/XPortalHubSimulator.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-import { UserSigner } from "@multiversx/sdk-wallet";
-import { Transaction } from "@multiversx/sdk-core";
-import {Signature} from "@multiversx/sdk-wallet/out/signature";
-
-let listenerAdded = false;
-const useXportalHub = async () => {
- if(listenerAdded) return;
-
- const headers = new Headers();
- headers.append('Content-Type','text/plain; charset=UTF-8');
- const walletPem = await fetch("/wallet.pem", { headers });
- const walletPemText = await walletPem.text();
- console.log("walletPem", walletPemText);
- const signer = UserSigner.fromPem(walletPemText);
-
- window.addEventListener("message", async (event) => {
- if(!event.data) return;
-
- try {
- const eventData = JSON.parse(event.data);
- if(!eventData?.type || eventData.type !== 'SIGN_TRANSACTIONS_REQUEST') {
- return;
- }
- const transactions = [];
- for(let i = 0; i < eventData.message.length; i++) {
- const transaction = Transaction.fromPlainObject(eventData.message[i]);
- const serializedTransaction = transaction.serializeForSigning(transaction.getSender());
- const transactionSignature = await signer.sign(serializedTransaction);
- transaction.applySignature(new Signature(transactionSignature), transaction.getSender());
- transactions.push(transaction.toPlainObject());
- }
- console.log("Sending message to xPortal Hub", transactions)
- } catch(err) {
- //Silent catch JSON parse error
- console.log("Error parsing message from xPortal Hub", err)
- }
- });
- listenerAdded = true;
-}
-export default useXportalHub;
\ No newline at end of file
diff --git a/src/components/TokenConfiguration.vue b/src/components/TokenConfiguration.vue
new file mode 100644
index 0000000..5e6e58a
--- /dev/null
+++ b/src/components/TokenConfiguration.vue
@@ -0,0 +1,58 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main.ts b/src/main.ts
index 01433bc..c8772ec 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,4 +1,4 @@
import { createApp } from 'vue'
-import App from './App.vue'
+import App from '@/App.vue'
createApp(App).mount('#app')
diff --git a/vite.config.ts b/vite.config.ts
index aeace51..329f36c 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -10,7 +10,8 @@ export default defineConfig({
plugins: [vue(), crx({ manifest })],
resolve: {
alias: {
- '@': fileURLToPath(new URL('./src', import.meta.url))
+ '@': fileURLToPath(new URL('./src', import.meta.url)),
+ buffer: 'buffer',
}
},
optimizeDeps: {