diff --git a/near-wallets/package.json b/near-wallets/package.json index d466030..8c1401e 100644 --- a/near-wallets/package.json +++ b/near-wallets/package.json @@ -11,9 +11,8 @@ "homepage": "https://github.com/hot-dao/near-selector", "scripts": { "postinstall": "patch-package", - "build:wallets": "yarn build:hotwallet && yarn build:mnw && yarn build:meteor && yarn build:wallet-connect && yarn build:nightly && yarn build:okx", + "build:wallets": "yarn build:mnw && yarn build:meteor && yarn build:wallet-connect && yarn build:nightly && yarn build:okx", "build:wallet-connect": "PACKAGE=wallet-connect vite build", - "build:hotwallet": "PACKAGE=hotwallet vite build", "build:nightly": "PACKAGE=nightly vite build", "build:meteor": "PACKAGE=meteor vite build", "build:okx": "PACKAGE=okx vite build", diff --git a/near-wallets/src/hotwallet/index.ts b/near-wallets/src/hotwallet/index.ts deleted file mode 100644 index f97bb1b..0000000 --- a/near-wallets/src/hotwallet/index.ts +++ /dev/null @@ -1,207 +0,0 @@ -import { baseEncode } from "@near-js/utils"; -import { QRCode } from "@here-wallet/core/qrcode-strategy"; -import crypto from "crypto"; - -import { head, bodyMobile, bodyDesktop } from "./view"; -import { ConnectorAction } from "../utils/action"; -import type { SignInParams, SignInAndSignMessageParams } from "../utils/types"; - -const isMobile = () => { - return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); -}; - -const logoImage = new Image(); -logoImage.src = "https://hot-labs.org/hot-widget/icon.svg"; - -const renderUI = () => { - const root = document.createElement("div"); - root.style.height = "100%"; - document.body.appendChild(root); - document.head.innerHTML = head; - - if (isMobile()) root.innerHTML = bodyMobile; - else root.innerHTML = bodyDesktop; -}; - -export const proxyApi = "https://h4n.app"; - -export const uuid4 = () => { - return window.crypto.randomUUID(); -}; - -export const wait = (timeout: number) => { - return new Promise((resolve) => setTimeout(resolve, timeout)); -}; - -export class RequestFailed extends Error { - name = "RequestFailed"; - constructor(readonly payload: any) { - super(); - } -} - -class HOT { - static shared = new HOT(); - - async getTimestamp() { - const { ts } = await fetch("https://api0.herewallet.app/api/v1/web/time").then((res) => res.json()); - const seconds = BigInt(ts) / 10n ** 12n; - return Number(seconds) * 1000; - } - - async getResponse(id: string) { - const res = await fetch(`${proxyApi}/${id}/response`, { - headers: { "content-type": "application/json" }, - method: "GET", - }); - - if (res.ok === false) throw Error(await res.text()); - const { data } = await res.json(); - return JSON.parse(data); - } - - async computeRequestId(request: object) { - const origin = window.selector.location; - const timestamp = await this.getTimestamp().catch(() => Date.now()); - - const query = baseEncode( - JSON.stringify({ - ...request, - deadline: timestamp + 60_000, - id: uuid4(), - $hot: true, - origin, - }) - ); - - const hashsum = crypto.createHash("sha1").update(query).digest("hex"); - return { requestId: hashsum, query }; - } - - async createRequest(request: object, signal?: AbortSignal) { - const { query, requestId } = await this.computeRequestId(request); - const res = await fetch(`${proxyApi}/${requestId}/request`, { - body: JSON.stringify({ data: query }), - headers: { "content-type": "application/json" }, - method: "POST", - signal, - }); - - if (res.ok === false) throw Error(await res.text()); - return requestId; - } - - async request(method: string, request: any): Promise { - renderUI(); - const qr = document.querySelector(".qr-code"); - if (qr) qr.innerHTML = ""; - - window.selector.ui.showIframe(); - const requestId = await this.createRequest({ method, request }); - const link = `hotcall-${requestId}`; - const qrcode = new QRCode({ - value: `https://app.hot-labs.org/link?${link}`, - logo: logoImage, - size: 140, - radius: 0.8, - ecLevel: "H", - - fill: { - type: "linear-gradient", - position: [0, 0, 1, 1], - colorStops: [ - [0, "#fff"], - [0.34, "#fff"], - [1, "#fff"], - ], - }, - - withLogo: true, - imageEcCover: 0.3, - quiet: 1, - }); - - qrcode.render(); - qr?.appendChild(qrcode.canvas); - - // @ts-ignore - window.openTelegram = () => window.selector.open(`https://t.me/hot_wallet/app?startapp=${link}`); // @ts-ignore - window.openExtension = () => window.selector.open(`https://download.hot-labs.org?hotconnector`); // @ts-ignore - window.openMobile = () => window.selector.open(`hotwallet://${link}`); - - const poolResponse = async () => { - await wait(3000); - const data: any = await this.getResponse(requestId).catch(() => null); - if (data == null) return await poolResponse(); - if (data.success) return data.payload; - throw new RequestFailed(data.payload); - }; - - const result = await poolResponse(); - return result; - } -} - -class NearWallet { - getAccounts = async (data: any) => { - if (data.network === "testnet") throw "HOT Wallet not supported on testnet"; - const hotAccount = await window.selector.storage.get("hot-account"); - if (hotAccount) return [JSON.parse(hotAccount)]; - return []; - }; - - signIn = async (data: SignInParams) => { - if (data.network === "testnet") throw "HOT Wallet not supported on testnet"; - const result = await HOT.shared.request("near:signIn", { - addFunctionCallKey: data.addFunctionCallKey, - }); - window.selector.storage.set("hot-account", JSON.stringify(result)); - return [result]; - }; - - signOut = async (data: any) => { - if (data.network === "testnet") throw "HOT Wallet not supported on testnet"; - await window.selector.storage.remove("hot-account"); - }; - - signMessage = async (payload: any) => { - if (payload.network === "testnet") throw "HOT Wallet not supported on testnet"; - const res = await HOT.shared.request("near:signMessage", payload); - return res; - }; - - signAndSendTransaction = async (payload: { network: string; receiverId: string; actions: ConnectorAction[] }) => { - if (payload.network === "testnet") throw "HOT Wallet not supported on testnet"; - const { transactions } = await HOT.shared.request("near:signAndSendTransactions", { transactions: [payload] }); - return transactions[0]; - }; - - signAndSendTransactions = async (payload: { network: string; transactions: { receiverId: string; actions: ConnectorAction[] }[] }) => { - if (payload.network === "testnet") throw "HOT Wallet not supported on testnet"; - const { transactions } = await HOT.shared.request("near:signAndSendTransactions", { transactions: payload.transactions }); - return transactions; - }; - - signDelegateActions = async (data: any) => { - if (data.network === "testnet") throw "HOT Wallet not supported on testnet"; - const result = await HOT.shared.request("near:signDelegateActions", { - delegateActions: data.delegateActions, - }); - return result; - }; - - signInAndSignMessage = async (data: SignInAndSignMessageParams) => { - if (data.network === "testnet") throw "HOT Wallet not supported on testnet"; - const result = await HOT.shared.request("near:signInAndSignMessage", { - messageParams: { - message: data.messageParams.message, - recipient: data.messageParams.recipient, - nonce: Array.from(data.messageParams.nonce), - }, - }); - window.selector.storage.set("hot-account", JSON.stringify({ accountId: result.accountId, publicKey: result.publicKey })); - return [result]; - }; -} - -window.selector.ready(new NearWallet()); diff --git a/near-wallets/src/hotwallet/styles.ts b/near-wallets/src/hotwallet/styles.ts deleted file mode 100644 index 9755d25..0000000 --- a/near-wallets/src/hotwallet/styles.ts +++ /dev/null @@ -1,277 +0,0 @@ -export const styles = /* css */ ` -body, -html { - margin: 0; - padding: 0; - background: transparent; - background: #1d1f20; - width: 100%; - height: 100%; -} - -a { - font-style: normal; - font-weight: 600; - font-size: 16px; - line-height: 22px; - text-decoration-line: underline; - color: #fd84e3; -} - -@supports (font-variation-settings: normal) { - :root { - font-family: InterVariable, sans-serif; - } -} - -*::-webkit-scrollbar { - display: none; -} - -* { - box-sizing: border-box; - --Stroke: #c7bab8; - --Elevation-0: #f3ebea; - --Elevation-1: #ebdedc; - --Elevation-2: #d9cdcb; - --Black-Primary: #2c3034; - --Black-Secondary: #6b6661; - font-family: "Inter" -apple-system BlinkMacSystemFont "Segoe UI" "Roboto" "Oxygen" "Ubuntu" "Cantarell" "Fira Sans" "Droid Sans" - "Helvetica Neue" sans-serif; - -webkit-tap-highlight-color: transparent; - -webkit-tap-highlight-color: transparent; -} - -input::-webkit-outer-spin-button, -input::-webkit-inner-spin-button { - -webkit-appearance: none; - margin: 0; -} - -.logo { - width: 70px; - height: 70px; - object-fit: cover; - border-radius: 10px; - margin: 0 auto; - margin-bottom: 16px; - margin-top: auto; -} - -.popup { - width: 100%; - height: 100%; - border-radius: 16px; - background: var(--surface-common-default, #1d1f20); - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; - padding: 24px; -} - -.title { - color: var(--text-primary, #ebdedc); - font-feature-settings: "liga" off; - font-family: "Cabinet Grotesk"; - font-size: 24px; - font-style: normal; - font-weight: 800; - line-height: normal; - margin: 0; -} -.title span { - color: var(--text-orange, #e9c363); - font-feature-settings: "liga" off; - font-family: "Cabinet Grotesk"; - font-size: 24px; - font-style: normal; - font-weight: 800; - line-height: normal; -} - -.qr-code { - background: url("https://hot-labs.org/hot-widget/qr.svg"); - background-size: cover; - background-position: 0 -14px; - width: 280px; - height: 280px; - display: flex; - justify-content: center; - align-items: center; - position: relative; -} -.qr-code canvas { - position: absolute; - top: 70px; - left: 70px; -} - -.divider { - color: var(--text-secondary, #ada5a4); - font-family: Inter; - font-size: 18px; - font-style: normal; - font-weight: 700; - line-height: 22px; - display: flex; - align-items: center; - gap: 12px; - width: 100%; - margin-bottom: 16px; - margin-top: -8px; -} -.divider::before, -.divider::after { - height: 1px; - background: var(--border-lowest, rgba(255, 255, 255, 0.07)); - content: ""; - display: block; - flex: 1; -} - -.button { - display: flex; - padding: 12px 16px; - justify-content: center; - align-items: center; - gap: 8px; - align-self: stretch; - border-radius: 16px; - border: 1px solid var(--border-high, rgba(255, 255, 255, 0.25)); - background: var(--controls-teriary-3, #ffedb2); - box-shadow: 4px 4px 0px 0px var(--controls-primary-dark-dark, #2c3034), - 5px 5px 0px 0px var(--controls-shadow-stroke, #3d3f46); - color: var(--text-dark-dark, #2c3034); - text-align: center; - font-feature-settings: "liga" off, "calt" off; - font-family: Inter; - font-size: 16px; - font-style: normal; - font-weight: 600; - line-height: 20px; - cursor: pointer; - ouline: none; -} - -.button.simple { - background: var(--text-primary, #ebdedc); - border: 1px solid var(--text-primary, #ebdedc); - color: var(--text-dark-dark, #2c3034); - border-radius: 12px; - box-shadow: none; -} - -.button.simple.reverse { - background: transparent; - color: var(--text-primary, #ebdedc); -} - -.get-button { - display: flex; - align-items: center; - justify-content: space-between; - padding: 8px 12px; - border-radius: 12px; - background: var(--surface-common-container--low, #262729); - cursor: pointer; - width: 100%; - text-decoration: none; - margin-top: auto; -} - -.get-button-text { - font-size: 14px; - font-weight: 400 !important; - line-height: 18px; - margin: 0; -} - -.get-button-green { - display: flex; - align-items: center; - justify-content: center; - padding: 2px 10px; - gap: 4px; - border-radius: 99px; - border: 1px solid var(--text-green, #77c7bd); - color: var(--text-green, #77c7bd); -} - -.h4 { - font-family: "Cabinet Grotesk"; - font-style: normal; - font-weight: 800; - font-size: 20px; - line-height: 25px; - color: #2c3034; - margin: 0; -} - -.close-button { - position: absolute; - top: 32px; - right: 32px; - height: 32px; - width: 32px; - padding: 0; - font-family: "Inter"; - font-style: normal; - font-weight: 600; - font-size: 16px; - color: #2c3034; - border: none; - margin: 0; - outline: none; - cursor: pointer; - height: 32px; - background: var(--surface-common-container--low, #262729); - border-radius: 50px; - justify-content: center; - align-items: center; - display: flex; - cursor: pointer; - transition: 0.2s opacity; -} -.close-button:hover { - opacity: 0.7; -} -@media (max-width: 640px) { - .close-button { - right: 16px; - top: 16px; - } -} - -.text { - color: var(--text-secondary, #ada5a4); - text-align: center; - font-family: Inter; - font-size: 16px; - font-style: normal; - font-weight: 500; - line-height: 20px; - margin: 0; -} - -.text.description { - margin-top: 6px; - color: var(--text-primary, #ebdedc); - font-weight: 400; -} - -.text a { - color: var(--text-blue, #6385ff); - font-family: Inter; - font-size: 16px; - font-style: normal; - font-weight: 600; - line-height: 20px; - text-decoration-line: underline; - text-decoration-style: solid; - text-decoration-skip-ink: auto; - text-decoration-thickness: auto; - text-underline-offset: auto; - text-underline-position: from-font; -} -`; diff --git a/near-wallets/src/hotwallet/view.ts b/near-wallets/src/hotwallet/view.ts deleted file mode 100644 index 0080eb2..0000000 --- a/near-wallets/src/hotwallet/view.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { styles } from "./styles"; - -export const head = /* html */ ` - - - - - - - - NEAR Connector -`; - -export const bodyMobile = /* html */ ` - -`; - -export const bodyDesktop = /* html */ ` - -`; diff --git a/repository/manifest.json b/repository/manifest.json index e651a27..0ca4dc9 100644 --- a/repository/manifest.json +++ b/repository/manifest.json @@ -8,7 +8,7 @@ "description": "Secure Multichain wallet. Manage assets, refuel gas, and mine $HOT on any device with HOT Wallet", "website": "https://hot-labs.org/wallet", "version": "1.0.0", - "executor": "https://raw.githubusercontent.com/hot-dao/near-selector/refs/heads/main/repository/hotwallet.js", + "executor": "https://raw.githubusercontent.com/hot-dao/near-connector-executor/refs/heads/main/hotwallet.js", "type": "sandbox", "platform": {