UNPKG

@airmoney-degn/controller-sdk

Version:

SDK for controlling AirMoney devices, providing button screen management, key event handling, and device communication capabilities

693 lines (692 loc) 23.4 kB
var C = Object.defineProperty; var N = (t, a, e) => a in t ? C(t, a, { enumerable: !0, configurable: !0, writable: !0, value: e }) : t[a] = e; var s = (t, a, e) => N(t, typeof a != "symbol" ? a + "" : a, e); class f { constructor(a) { s(this, "baseURL"); s(this, "serviceName"); s(this, "createEndpoint", (a) => [this.baseURL, a].join("/")); s(this, "createPayload", (a, e) => ({ id: (/* @__PURE__ */ new Date()).getTime().toString(), jsonrpc: "2.0", method: a, params: e })); this.baseURL = a, this.serviceName = this.constructor.name; } async request(a) { return await (await fetch(this.createEndpoint(""), { method: "POST", body: JSON.stringify(a), headers: { Accept: "application/json", "Content-Type": "application/json" } })).json(); } } const l = (t) => { if (typeof t == "bigint" || typeof t == "number") return `0x${t.toString(16)}`; if (typeof t == "string") return t.startsWith("0x") ? t : `0x${t}`; throw new Error("Invalid input"); }, I = (t) => { const a = {}; return t.to && (a.to = l(t.to)), t.value && (a.value = l(t.value)), t.gasLimit && (a.gasLimit = l(t.gasLimit)), t.gasPrice && (a.gasPrice = l(t.gasPrice)), t.maxFeePerGas && (a.maxFeePerGas = l(t.maxFeePerGas)), t.maxPriorityFeePerGas && (a.maxPriorityFeePerGas = l(t.maxPriorityFeePerGas)), t.nonce && (a.nonce = l(t.nonce)), t.chainId && (a.chainId = l(t.chainId)), t.data && (a.data = l(t.data)), t.type && (a.type = l(t.type)), t.gas && (a.gas = l(t.gas)), t.accessList && (a.accessList = t.accessList.map((e) => ({ address: e.address ? l(e.address) : null, storageKeys: e.storageKeys.map((i) => l(i)) }))), a; }; class _ extends f { constructor() { super("http://localhost:5050"); // EVM Operations s(this, "signEvmMessage", async (e) => { const i = this.createPayload("signEvmMessage", [e]); return await this.request(i); }); s(this, "signEvmTransaction", async (e) => { const i = this.createPayload("signEvmTransaction", [e]); return await this.request(i); }); s(this, "signGeneralEvmTransaction", async (e) => { const i = this.createPayload("signGeneralEvmTransaction", [e]); return await this.request(i); }); s(this, "signEip712TypedData", async (e) => { const i = this.createPayload("signEip712TypedData", [e]); return await this.request(i); }); s(this, "verifyEip1271Signature", async (e) => { const i = this.createPayload("verifyEip1271Signature", [ e.rpcUrl, e.contractAddress, e.message, e.signature ]); return await this.request(i); }); // Solana Operations s(this, "signSolanaMessage", async (e) => { const i = this.createPayload("signSolanaMessage", [e]); return await this.request(i); }); s(this, "signSolanaTransaction", async (e) => { const i = this.createPayload("signSolanaTransaction", [e]); return await this.request(i); }); s(this, "signEVMRawTransaction", async (e) => { const i = I(e.rawTransaction), n = typeof e.chain_id == "number" ? l(e.chain_id) : e.chain_id; return await this.signGeneralEvmTransaction({ address: e.address, typed_tx: i, chain_id: n, pin: e.pin }); }); s(this, "getDefaultEvmWallet", async () => { const e = this.createPayload("getDefaultEvmWallet", []); return await this.request(e); }); s(this, "getDefaultSvmWallet", async () => { const e = this.createPayload("getDefaultSvmWallet", []); return await this.request(e); }); // Bitcoin Operations s(this, "signBitcoinMessage", async (e) => { const i = this.createPayload("signBitcoinMessage", [e]); return await this.request(i); }); s(this, "verifyBitcoinSignature", async (e) => { const i = this.createPayload("verifyBitcoinSignature", [e.address, e.message, e.signature]); return await this.request(i); }); s(this, "signBitcoinTransaction", async (e) => { const i = this.createPayload("signBitcoinTransaction", [e]); return await this.request(i); }); s(this, "getDefaultBitcoinWallet", async () => { const e = this.createPayload("getDefaultBitcoinWallet", []); return await this.request(e); }); // Device Status & PIN Operations s(this, "getDeviceStatus", async () => { const e = this.createPayload("getDeviceStatus", []); return await this.request(e); }); s(this, "verifyPin", async (e) => { const i = this.createPayload("verifyPin", [e]); return await this.request(i); }); } // Universal Wallet Operations } const q = (t) => "result" in t, w = (t) => "error" in t; var D = /* @__PURE__ */ ((t) => (t.Right = "right", t.Left = "left", t))(D || {}), o = /* @__PURE__ */ ((t) => (t.CoreUnhandled = "CoreUnhandled", t.DisplayInvalidInput = "DisplayInvalidInput", t.DisplayAssetLoad = "DisplayAssetLoad", t.DisplayHardware = "DisplayHardware", t.NetworkCommand = "NetworkCommand", t.NetworkAuth = "NetworkAuth", t.NetworkConfig = "NetworkConfig", t.HapticInvalidAction = "HapticInvalidAction", t.HapticHardware = "HapticHardware", t.BluetoothControl = "BluetoothControl", t.BluetoothData = "BluetoothData", t.BrightnessFailure = "BrightnessFailure", t.AudioFailure = "AudioFailure", t.TelemetryRead = "TelemetryRead", t.TelemetryRender = "TelemetryRender", t.SessionParse = "SessionParse", t.SessionStorage = "SessionStorage", t.Unknown = "Unknown", t))(o || {}); const h = "app-launcher", G = () => { fetch("http://127.0.0.1:7070/background", { method: "GET" }).catch((t) => { console.error("Failed to return to launcher:", t); }); }, H = () => { fetch("http://127.0.0.1:7070/reload", { method: "GET" }).catch((t) => { console.error("Failed to reload web app:", t); }); }, A = () => window.location.protocol === "file:", x = () => window.location.hostname.endsWith(".internal"), z = (t = h) => { window.location.href = v(t); }, R = (t = h, a) => [v(t), a].filter(Boolean).join("/"), v = (t = h) => `http://${t}.internal`, W = (t = h) => R(t, "dapp-logo.png"); let y = null; const T = async () => { if (y) return y; try { const t = await fetch("/metadata.json"); if (!t.ok) throw new Error("Failed to fetch metadata.json"); const a = await t.json(); return y = a, a; } catch (t) { throw new Error(`Failed to load metadata: ${t instanceof Error ? t.message : "Unknown error"}`); } }, L = async () => { try { if (A()) return "file"; try { return (await T()).name; } catch { throw new Error("Could not determine application identifier in unbundled environment"); } } catch { if (window.AIRMONEY_APP_ID === void 0) throw new Error("Neither metadata.json nor AIRMONEY_APP_ID is available"); return window.AIRMONEY_APP_ID; } }, m = { enabled: !1, time: 40 }, S = (t) => t === "setImage" || t === "setAnimate", u = class u extends f { constructor(e) { super("http://localhost:4040"); s(this, "appName", ""); s(this, "isInitialized", !1); s(this, "throttleConfigs", {}); s(this, "createThrottledMethod", (e, i) => async (...n) => { const r = this.throttleConfigs[i]; if (!r || !r.enabled) return e.apply(this, n); const c = n[0].id.toString(); return new Promise((b) => { if (r.lastArgs.set(c, n), !r.timeouts.has(c)) { const P = setTimeout(async () => { try { const p = r.lastArgs.get(c); if (p) { const E = await e.apply(this, p); b(E); } } finally { r.timeouts.delete(c), r.lastArgs.delete(c); } }, r.time); r.timeouts.set(c, P); } }); }); s(this, "initialize", async () => { if (!this.isInitialized && !this.appName) { try { this.appName = await L(); } catch { if (window.AIRMONEY_APP_ID === void 0) throw new Error("Neither identifier from metadata.json nor AIRMONEY_APP_ID is available"); this.appName = window.AIRMONEY_APP_ID; } if (!this.appName) throw new Error("Neither identifier from metadata.json nor AIRMONEY_APP_ID is available"); this.isInitialized = !0; } }); s(this, "ensureInitialized", async () => { this.isInitialized || await this.initialize(); }); s(this, "setImage", this.createThrottledMethod( async (e) => { await this.ensureInitialized(); const i = this.createPayload("setImage", [ this.createImagePathParam(e.imageName), e.id ]); return await this.request(i); }, "setImage" )); s(this, "setAnimate", this.createThrottledMethod( async (e) => { await this.ensureInitialized(); const i = this.createPayload("setAnimate", [ this.createImagePathParam(e.imageName), e.id ]); return await this.request(i); }, "setAnimate" )); s(this, "createImagePathParam", (e) => { if (!this.appName) throw new Error("App name is required"); return [this.appName, e].filter(Boolean).join("/"); }); e && Object.entries(e).forEach((i) => { const n = i[0], r = i[1]; S(n) && (this.throttleConfigs[n] = { enabled: r.throttleEnabled ?? m.enabled, time: r.throttleTime ?? m.time, timeouts: /* @__PURE__ */ new Map(), lastArgs: /* @__PURE__ */ new Map() }); }); } async request(e) { const i = await super.request(e); if (w(i)) { const n = u.ERROR_CODE_MAP.get(i.error.code); return { ...i, type: (n == null ? void 0 : n.type) ?? o.Unknown, userMessage: (n == null ? void 0 : n.userMessage) || i.error.message || "Unknown error" }; } return i; } }; s(u, "ERROR_CODE_MAP", /* @__PURE__ */ new Map([ // Core/System [ 1e3, { type: o.CoreUnhandled, userMessage: "An unexpected system error occurred. Please try again." } ], // Display (buttons & side OLED) [ 2001, { type: o.DisplayInvalidInput, userMessage: "The parameter is invalid. Please check your input and try again." } ], [ 2002, { type: o.DisplayAssetLoad, userMessage: "Unable to load or decode the image file. Please check that the image exists and is in a supported format." } ], [ 2003, { type: o.DisplayHardware, userMessage: "Display hardware error. The display mutex or SPI communication failed. Please try again." } ], // Network/Wi-Fi [ 3001, { type: o.NetworkCommand, userMessage: "Network or system command execution failed. Please try again." } ], [ 3002, { type: o.NetworkAuth, userMessage: "Wi-Fi authentication timed out. Please check your password and try again." } ], [ 3003, { type: o.NetworkConfig, userMessage: "Failed to write network configuration. Please try again." } ], // Haptics [ 4001, { type: o.HapticInvalidAction, userMessage: "Invalid haptic action name. Please check your input and try again." } ], [ 4002, { type: o.HapticHardware, userMessage: "Haptic hardware error occurred. Please try again." } ], // Bluetooth/Pairing [ 5001, { type: o.BluetoothControl, userMessage: "Pairing service control failed. Please try again." } ], [ 5002, { type: o.BluetoothData, userMessage: "Failed to read or parse Bluetooth device data. Please try again." } ], // Audio/Brightness [ 6001, { type: o.BrightnessFailure, userMessage: "Failed to adjust brightness. Hardware interaction error or missing device. Please try again." } ], [ 6002, { type: o.AudioFailure, userMessage: "Audio operation failed. Hardware interaction error or missing ALSA device. Please try again." } ], // Telemetry [ 7001, { type: o.TelemetryRead, userMessage: "Failed to read telemetry data. Power supply paths are unreadable. Please try again." } ], [ 7002, { type: o.TelemetryRender, userMessage: "Failed to render telemetry data. Error composing status payload. Please try again." } ], // Session & MPC [ 8001, { type: o.SessionParse, userMessage: "Failed to parse session data. Invalid JSON format. Please try again." } ], [ 8002, { type: o.SessionStorage, userMessage: "Session storage error. Filesystem read or write failed. Please try again." } ] ])); let g = u; const U = new g({ setImage: { throttleEnabled: !0 }, setAnimate: { throttleEnabled: !0 } }), k = (t) => async (...a) => { const e = await t(...a); if (w(e)) throw new Error(e.error.message || "AMService error occurred"); return e; }, Y = k, $ = k; var d = /* @__PURE__ */ ((t) => (t.LeftButton = "ArrowLeft", t.RightButton = "ArrowRight", t.CounterClockwiseRotary = "]", t.ClockwiseRotary = "[", t.RotaryButton = "Enter", t.SideButton = "ArrowUp", t.MuteSwitch = "ArrowDown", t))(d || {}); class O { constructor(a) { s(this, "config", { threshold: 300, combinations: void 0, doubleClicks: void 0, debug: !1 }); s(this, "activeKeys", {}); s(this, "timers", {}); s(this, "callbacks", []); s(this, "window", window); s(this, "keyUpEventName", "keyup"); s(this, "keyDownEventName", "keydown"); s(this, "getSortedCombinations", (a) => { var i; return (i = ((n) => this.config.combinations && Object.entries(this.config.combinations).filter(([, r]) => r.includes(n)))(a)) == null ? void 0 : i.sort(([, n], [, r]) => r.length - n.length); }); s(this, "isCombinationComplete", (a, e) => a[1].every((i) => this.activeKeys[i] === e)); s(this, "getDoubleClick", (a) => this.config.doubleClicks && Object.entries(this.config.doubleClicks).find(([, e]) => e === a)); s(this, "debugLog", (...a) => { this.config.debug && console.log(...a); }); s(this, "getKeyFromEvent", (a) => { let e = null; return a instanceof CustomEvent && a.detail && a.detail.key && (e = a.detail.key), a instanceof KeyboardEvent && (e = a.key), !e || !Object.values(d).includes(e) ? null : e; }); s(this, "handleCombination", (a) => { a[1].map((e) => { this.activeKeys[e] = "pressed", clearTimeout(this.timers[e]); }), this.notifyCallbacks({ source: "air-money", type: "key-event", subType: "combinationdown", data: { keys: a[1], name: a[0] } }), this.debugLog("---> COMBINATION DOWN", a); }); s(this, "onKeyDown", (a) => { const e = this.getKeyFromEvent(a); if (e) switch (a.preventDefault(), this.activeKeys[e]) { case "pressed": case "clicked": case "end": return; case "over": { Object.entries(this.activeKeys).filter(([, i]) => i === "over").forEach(([i]) => { this.notifyCallbacks({ source: "air-money", type: "key-event", subType: "longpressdown", data: { key: i } }), this.debugLog("---> LONG PRESS DOWN", i); }); return; } case void 0: { const i = this.getSortedCombinations(e), n = i == null ? void 0 : i[0]; if (this.activeKeys[e] = "available", n && this.isCombinationComplete(n, "available")) { this.handleCombination(n); return; } else i != null && i.length && i.length > 1 && (this.timers[e] = setTimeout(() => { const r = i.find( (c) => this.isCombinationComplete(c, "available") ); r && this.handleCombination(r); }, this.config.threshold)); this.timers[e] = setTimeout(() => { this.notifyCallbacks({ source: "air-money", type: "key-event", subType: "longpressdown", data: { key: e } }), this.debugLog("---> LONG PRESS DOWN", e), this.activeKeys[e] = "over"; }, this.config.threshold); break; } } }); s(this, "onKeyUp", (a) => { const e = this.getKeyFromEvent(a); if (e) switch (a.preventDefault(), this.activeKeys[e]) { case "clicked": { const i = this.getDoubleClick(e); i && (this.notifyCallbacks({ source: "air-money", type: "key-event", subType: "doubleclick", data: { key: e, name: i[0] } }), this.debugLog("---> DOUBLE CLICK", i), clearTimeout(this.timers[e]), delete this.activeKeys[e]); break; } case "over": { this.notifyCallbacks({ source: "air-money", type: "key-event", subType: "longpressup", data: { key: e } }), this.debugLog("---> LONG PRESS UP", e), clearTimeout(this.timers[e]), delete this.activeKeys[e]; break; } case "available": { const i = this.getDoubleClick(e), n = this.getSortedCombinations(e), r = n == null ? void 0 : n.find( (c) => this.isCombinationComplete(c, "available") ); r ? this.handleCombination(r) : i ? (this.activeKeys[e] = "clicked", clearTimeout(this.timers[e]), this.timers[e] = setTimeout(() => { this.notifyCallbacks({ source: "air-money", type: "key-event", subType: "press", data: { key: e } }), this.debugLog("---> PRESS", e), delete this.activeKeys[e]; }, this.config.threshold)) : (this.notifyCallbacks({ source: "air-money", type: "key-event", subType: "press", data: { key: e } }), this.debugLog("---> PRESS", e), clearTimeout(this.timers[e]), delete this.activeKeys[e]); break; } case "pressed": { const i = this.getSortedCombinations(e), n = i == null ? void 0 : i.find( (r) => this.isCombinationComplete(r, "pressed") ); n && (this.notifyCallbacks({ source: "air-money", type: "key-event", subType: "combinationup", data: { keys: n[1], name: n[0] } }), this.debugLog("---> COMBINATION UP", n), n == null || n[1].map((r) => { delete this.activeKeys[r], clearTimeout(this.activeKeys[r]); })); break; } } }); window && (this.window = window), this.updateConfig(a), this.keyUpEventName = (a == null ? void 0 : a.keyUpEventName) || this.keyUpEventName, this.keyDownEventName = (a == null ? void 0 : a.keyDownEventName) || this.keyDownEventName; } updateConfig(a) { Object.assign(this.config, a); } notifyCallbacks(a) { this.callbacks.forEach((e) => e(a)); } on(a) { this.callbacks.push(a), this.callbacks.length === 1 && (this.window.addEventListener(this.keyDownEventName, this.onKeyDown), this.window.addEventListener(this.keyUpEventName, this.onKeyUp)); } off(a) { a ? this.callbacks = this.callbacks.filter((e) => e !== a) : this.callbacks = [], this.callbacks.length === 0 && (this.window.removeEventListener("keydown", this.onKeyDown), this.window.removeEventListener("keyup", this.onKeyUp)); } getConfig() { return this.config; } } class B { constructor(a) { s(this, "keyEvent"); s(this, "triggerConfigs", /* @__PURE__ */ new Map()); s(this, "listenerIdCounter", 0); s(this, "isInitialized", !1); s(this, "debug", !1); s(this, "initialize", () => { if (this.isInitialized) return; const a = (e) => { this.handlePriorityEvent(e); }; this.keyEvent.on(a), this.isInitialized = !0; }); /** * Subscribe method - registers a group of triggers with a config object. * If config.id is missing, a unique id will be generated and assigned. */ s(this, "subscribe", (a) => { this.initialize(); const e = `listener_${++this.listenerIdCounter}`; return this.triggerConfigs.set(e, { ...a, id: e }), e; }); s(this, "handlePriorityEvent", (a) => { const e = Array.from(this.triggerConfigs.values()).sort((n, r) => r.priority - n.priority), i = []; for (const n of e) for (const r of n.triggers) i.push(Object.assign(r, { name: n.name })); this.debug && console.log( "---> ALL TRIGGERS", i.map((n) => n.name) ); for (const n of i) if (n.condition(a)) { this.debug && console.log("---> TRIGGER", n.name), n.trigger(a); break; } }); s(this, "unsubscribe", (a) => (this.triggerConfigs.delete(a), !0)); s(this, "unsubscribeAll", () => { this.triggerConfigs.clear(); }); s(this, "getKeyEventInstance", () => this.keyEvent); s(this, "updateConfig", (a) => { this.keyEvent.updateConfig(a); }); s(this, "getConfig", () => this.keyEvent.getConfig()); s(this, "destroy", () => { this.unsubscribeAll(), this.keyEvent.off(), this.isInitialized = !1; }); this.keyEvent = a.instance, this.debug = a.debug ?? !1; } } const M = (t = {}) => { const { combinations: a, ...e } = t; return new O({ combinations: { back: [d.LeftButton, d.RightButton], backToHome: [d.LeftButton, d.RightButton, d.SideButton], ...a }, ...e }); }, V = (t = {}) => { const { instance: a, ...e } = t; return new B({ instance: a ?? M(e), ...e }); }, J = (t) => !!(t === "true" || Number(t)), Q = (t) => t !== void 0 ? /^[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)$/.test(t) : !1, F = (t) => t.every((a) => a !== void 0), X = (t) => { if (F(t)) return t; }; export { d as AMKey, D as AMServiceScreen, o as AMServiceUserErrorType, h as APP_LAUNCHER_NAME, _ as AirMoneyCryptoService, O as AirMoneyKeyEvent, B as AirMoneyKeyEventManager, g as AirMoneyService, U as airmoneyService, $ as airmoneyServiceErrorWrapper, G as backToHome, M as createDefaultKeyEvent, V as createDefaultKeyEventManager, Y as cryptoServiceErrorWrapper, R as displayAsset, k as errorWrapper, v as getAppLink, W as getAppLogo, L as getAppName, z as goToApp, w as isAMServiceErrorResponse, q as isAMServiceSuccessResponse, A as isFileProtocol, x as isInDevice, F as isParamsValid, Q as isValidNumber, I as normalizeEVMTransaction, H as reloadWebApp, X as serializeParams, J as toBoolean, l as toHexString };