UNPKG

@cowprotocol/widget-react

Version:

CoW Swap Widget Library. Allows you to easily embed a CoW Swap widget on your React application.

486 lines (485 loc) 17.1 kB
var G = Object.defineProperty; var Q = (e, n, t) => n in e ? G(e, n, { enumerable: !0, configurable: !0, writable: !0, value: t }) : e[n] = t; var _ = (e, n, t) => (Q(e, typeof n != "symbol" ? n + "" : n, t), t); import { jsxs as $, jsx as A } from "react/jsx-runtime"; import { useState as j, useRef as y, useCallback as B, useEffect as O } from "react"; class M { constructor(n) { this.key = n; } postMessageToWindow(n, t, r) { const p = typeof r == "object" ? r : {}, h = { key: this.key, method: t, ...p }; n.postMessage( h, "*" // TODO: Change to CoW specific origin in production. https://github.com/cowprotocol/cowswap/issues/3828 ); } listenToMessageFromWindow(n, t, r) { const p = (h) => { !q(h.data) || h.data.key !== this.key || h.data.method !== t || r(h.data); }; return n.addEventListener("message", p), p; } stopListeningToMessageFromWindow(n, t, r) { n.removeEventListener("message", r); } stopListeningWindowListener(n, t) { n.removeEventListener("message", t); } } function q(e) { return typeof e == "object" && e !== null && "key" in e && "method" in e && typeof e.key == "string" && typeof e.method == "string"; } var C = /* @__PURE__ */ ((e) => (e.PROVIDER_RPC_REQUEST = "PROVIDER_RPC_REQUEST", e.PROVIDER_RPC_RESPONSE = "PROVIDER_RPC_RESPONSE", e.PROVIDER_ON_EVENT = "PROVIDER_ON_EVENT", e))(C || {}); const I = new M( "cowSwapIframeRpcProviderTransport" ), J = ["connect", "disconnect", "close", "chainChanged", "accountsChanged"]; class z { /** * Creates an instance of IframeRpcProviderBridge. * @param iframeWidow - The iFrame window that will post up general RPC messages and to which the IframeRpcProviderBridge will forward the RPC result. * Also it will receive some special RPC events coming from the wallet, like connect/chainChanged,accountChanged */ constructor(n) { /** * The Ethereum provider instance. * When is null the JSON-RPC bridge is disconnected from the Ethereum provider. * */ _(this, "ethereumProvider", null); /** Stored JSON-RPC requests, to queue them when disconnected. */ _(this, "requestWaitingForConnection", {}); _(this, "processRpcCallFromWindow", ({ rpcRequest: n }) => { if (!this.ethereumProvider) { n.id && (this.requestWaitingForConnection[n.id] = n); return; } this.processRpcRequest(n); }); this.iframeWidow = n; } /** * Disconnects the JSON-RPC bridge from the Ethereum provider. */ disconnect() { this.ethereumProvider = null, I.stopListeningToMessageFromWindow( window, C.PROVIDER_RPC_REQUEST, this.processRpcCallFromWindow ); } /** * Handles the 'connect' event and sets up event listeners for Ethereum provider events. * @param newProvider - The Ethereum provider to connect. */ onConnect(n) { this.ethereumProvider ? this.disconnect() : I.listenToMessageFromWindow( window, C.PROVIDER_RPC_REQUEST, this.processRpcCallFromWindow ), this.ethereumProvider = n, this.processPendingRequests(), J.forEach((t) => { n.on(t, (r) => this.onProviderEvent(t, r)); }); } processPendingRequests() { Object.keys(this.requestWaitingForConnection).forEach((n) => { this.processRpcRequest(this.requestWaitingForConnection[n]); }), this.requestWaitingForConnection = {}; } /** * Processes a JSON-RPC request and sends appropriate response or error via the content window. * @param request - The JSON-RPC request to be processed. */ processRpcRequest(n) { const { id: t, jsonrpc: r, method: p } = n; if (!this.ethereumProvider || !t) return; (p === "enable" ? this.ethereumProvider.enable() : this.ethereumProvider.request({ ...n, id: t })).then( (w) => this.forwardRpcResponseToIframe({ rpcResponse: { jsonrpc: r, id: t, result: w } }) ).catch( (w) => this.forwardRpcResponseToIframe({ rpcResponse: { jsonrpc: r, id: t, error: w } }) ); } onProviderEvent(n, t) { I.postMessageToWindow(this.iframeWidow, C.PROVIDER_ON_EVENT, { event: n, params: t }); } /** * Forward a JSON-RPC message to the content window. */ forwardRpcResponseToIframe(n) { I.postMessageToWindow( this.iframeWidow, C.PROVIDER_RPC_RESPONSE, n ); } } var Y = { exports: {} }; (function(e) { var n = Object.prototype.hasOwnProperty, t = "~"; function r() { } Object.create && (r.prototype = /* @__PURE__ */ Object.create(null), new r().__proto__ || (t = !1)); function p(c, i, s) { this.fn = c, this.context = i, this.once = s || !1; } function h(c, i, s, o, d) { if (typeof s != "function") throw new TypeError("The listener must be a function"); var u = new p(s, o || c, d), l = t ? t + i : i; return c._events[l] ? c._events[l].fn ? c._events[l] = [c._events[l], u] : c._events[l].push(u) : (c._events[l] = u, c._eventsCount++), c; } function w(c, i) { --c._eventsCount === 0 ? c._events = new r() : delete c._events[i]; } function f() { this._events = new r(), this._eventsCount = 0; } f.prototype.eventNames = function() { var i = [], s, o; if (this._eventsCount === 0) return i; for (o in s = this._events) n.call(s, o) && i.push(t ? o.slice(1) : o); return Object.getOwnPropertySymbols ? i.concat(Object.getOwnPropertySymbols(s)) : i; }, f.prototype.listeners = function(i) { var s = t ? t + i : i, o = this._events[s]; if (!o) return []; if (o.fn) return [o.fn]; for (var d = 0, u = o.length, l = new Array(u); d < u; d++) l[d] = o[d].fn; return l; }, f.prototype.listenerCount = function(i) { var s = t ? t + i : i, o = this._events[s]; return o ? o.fn ? 1 : o.length : 0; }, f.prototype.emit = function(i, s, o, d, u, l) { var m = t ? t + i : i; if (!this._events[m]) return !1; var a = this._events[m], v = arguments.length, T, E; if (a.fn) { switch (a.once && this.removeListener(i, a.fn, void 0, !0), v) { case 1: return a.fn.call(a.context), !0; case 2: return a.fn.call(a.context, s), !0; case 3: return a.fn.call(a.context, s, o), !0; case 4: return a.fn.call(a.context, s, o, d), !0; case 5: return a.fn.call(a.context, s, o, d, u), !0; case 6: return a.fn.call(a.context, s, o, d, u, l), !0; } for (E = 1, T = new Array(v - 1); E < v; E++) T[E - 1] = arguments[E]; a.fn.apply(a.context, T); } else { var H = a.length, P; for (E = 0; E < H; E++) switch (a[E].once && this.removeListener(i, a[E].fn, void 0, !0), v) { case 1: a[E].fn.call(a[E].context); break; case 2: a[E].fn.call(a[E].context, s); break; case 3: a[E].fn.call(a[E].context, s, o); break; case 4: a[E].fn.call(a[E].context, s, o, d); break; default: if (!T) for (P = 1, T = new Array(v - 1); P < v; P++) T[P - 1] = arguments[P]; a[E].fn.apply(a[E].context, T); } } return !0; }, f.prototype.on = function(i, s, o) { return h(this, i, s, o, !1); }, f.prototype.once = function(i, s, o) { return h(this, i, s, o, !0); }, f.prototype.removeListener = function(i, s, o, d) { var u = t ? t + i : i; if (!this._events[u]) return this; if (!s) return w(this, u), this; var l = this._events[u]; if (l.fn) l.fn === s && (!d || l.once) && (!o || l.context === o) && w(this, u); else { for (var m = 0, a = [], v = l.length; m < v; m++) (l[m].fn !== s || d && !l[m].once || o && l[m].context !== o) && a.push(l[m]); a.length ? this._events[u] = a.length === 1 ? a[0] : a : w(this, u); } return this; }, f.prototype.removeAllListeners = function(i) { var s; return i ? (s = t ? t + i : i, this._events[s] && w(this, s)) : (this._events = new r(), this._eventsCount = 0), this; }, f.prototype.off = f.prototype.removeListener, f.prototype.addListener = f.prototype.on, f.prefixed = t, f.EventEmitter = f, e.exports = f; })(Y); class K { constructor() { _(this, "subscriptions", {}); } on(n) { const { event: t, handler: r } = n; return this.subscriptions[t] || (this.subscriptions[t] = []), this.subscriptions[t].push(r), n; } off(n) { const { event: t, handler: r } = n; return this.subscriptions[t] && (this.subscriptions[t] = this.subscriptions[t].filter((p) => p !== r)), n; } emit(n, t) { this.subscriptions[n] && this.subscriptions[n].forEach((r) => r(t)); } } var R = /* @__PURE__ */ ((e) => (e.ACTIVATE = "ACTIVATE", e.UPDATE_HEIGHT = "UPDATE_HEIGHT", e.SET_FULL_HEIGHT = "SET_FULL_HEIGHT", e.EMIT_COW_EVENT = "EMIT_COW_EVENT", e.PROVIDER_RPC_REQUEST = "PROVIDER_RPC_REQUEST", e.INTERCEPT_WINDOW_OPEN = "INTERCEPT_WINDOW_OPEN", e))(R || {}), S = /* @__PURE__ */ ((e) => (e.UPDATE_PARAMS = "UPDATE_PARAMS", e.UPDATE_APP_DATA = "UPDATE_APP_DATA", e.PROVIDER_RPC_RESPONSE = "PROVIDER_RPC_RESPONSE", e.PROVIDER_ON_EVENT = "PROVIDER_ON_EVENT", e))(S || {}), W = /* @__PURE__ */ ((e) => (e.SWAP = "swap", e.LIMIT = "limit", e.ADVANCED = "advanced", e.YIELD = "yield", e))(W || {}); const _e = [ "primary", "background", "paper", "text", "danger", "warning", "alert", "info", "success" ], g = new M("cowSwapWidget"); class X { constructor(n, t = []) { _(this, "eventEmitter", new K()); _(this, "listeners", []); _(this, "widgetListener"); this.contentWindow = n, this.updateListeners(t), this.widgetListener = g.listenToMessageFromWindow( this.contentWindow, R.EMIT_COW_EVENT, (r) => this.eventEmitter.emit(r.event, r.payload) ); } stopListeningIframe() { g.stopListeningWindowListener(this.contentWindow, this.widgetListener); } updateListeners(n) { for (const t of this.listeners) this.eventEmitter.off(t); this.listeners = n || []; for (const t of this.listeners) this.eventEmitter.on(t); } } class Z { constructor(n, t) { _(this, "forwardSdkMessage"); this.appWindow = n, this.iframeWidow = t, this.forwardSdkMessage = (r) => { ee(r.data) && r.origin !== window.location.origin && (te(r.data) ? this.appWindow.parent.postMessage(r.data, "*") : ne(r.data) && this.iframeWidow.postMessage(r.data, "*")); }, this.startListening(); } startListening() { this.appWindow.addEventListener("message", this.forwardSdkMessage); } stopListening() { this.appWindow.removeEventListener("message", this.forwardSdkMessage); } } function ee(e) { return typeof e == "object" && e !== null && "id" in e && typeof e.id == "string"; } function te(e) { return "method" in e && typeof e.method == "string" && "params" in e && "env" in e && typeof e.env == "object" && e.env !== null && "sdkVersion" in e.env; } function ne(e) { return "success" in e && typeof e.success == "boolean" && "version" in e && typeof e.version == "string"; } function re(e) { return !!(e && typeof e == "object"); } const L = "_"; function se(e) { const n = typeof e.baseUrl == "string" ? e.baseUrl : "https://swap.cow.fi", t = F(e); return n + "/#" + t + "?" + x(e); } function F(e) { const { chainId: n = 1, sell: t, buy: r, tradeType: p = W.SWAP } = e, h = [(t == null ? void 0 : t.asset) || L, (r == null ? void 0 : r.asset) || L].map(encodeURIComponent).join("/"); return `/${n}/widget/${p}/${h}`; } function x(e) { const n = new URLSearchParams(); return ie(oe(n, e), e); } function oe(e, n) { const { sell: t, buy: r } = n; return t != null && t.amount && e.append("sellAmount", t.amount), r != null && r.amount && e.append("buyAmount", r.amount), e; } function ie(e, n) { const t = n.theme; return t ? (re(t) ? (e.append("palette", encodeURIComponent(JSON.stringify(t))), e.append("theme", t.baseTheme)) : (e.append("palette", "null"), e.append("theme", t)), e) : (e.append("palette", "null"), e); } const k = "640px", ae = "450px", ce = 20; function D(e, n) { const { params: t, provider: r, listeners: p } = n; let h = r, w = t; const f = ue(t); e.innerHTML = "", e.appendChild(f); const { contentWindow: c } = f; if (!c) throw console.error("Iframe does not contain a window", f), new Error("Iframe does not contain a window!"); const i = []; i.push(fe(c, t.appCode)), i.push(...pe(f, t.height, t.maxHeight)), i.push(de()); const s = new X(window, p); let o = b(c, null, h); f.addEventListener("load", () => N(c, w, h)); const d = new Z(window, c); return { updateParams: (u) => { w = u, N(c, w, h); }, updateListeners: (u) => s.updateListeners(u), updateProvider: (u) => { h = u, o = b(c, o, u); }, destroy: () => { o.disconnect(), s.stopListeningIframe(), i.forEach((u) => window.removeEventListener("message", u)), d.stopListening(), e.removeChild(f); } }; } function b(e, n, t) { n && n.disconnect(); const r = n || new z(e); return t && r.onConnect(t), r; } function ue(e) { const { width: n = ae, height: t = k } = e, r = document.createElement("iframe"); return r.src = se(e), r.width = n, r.height = t, r.style.border = "0", r.allow = "clipboard-read; clipboard-write", r; } function N(e, n, t) { const r = !!t, p = F(n), h = x(n).toString(), { theme: w, ...f } = n; g.postMessageToWindow(e, S.UPDATE_PARAMS, { urlParams: { pathname: p, search: h }, appParams: f, hasProvider: r }); } function fe(e, n) { return g.listenToMessageFromWindow(window, R.ACTIVATE, () => { g.postMessageToWindow(e, S.UPDATE_APP_DATA, { metaData: n ? { appCode: n } : void 0 }); }); } function de() { return g.listenToMessageFromWindow( window, R.INTERCEPT_WINDOW_OPEN, ({ href: e, rel: n, target: t }) => { const r = e.toString(); if (!r.startsWith("http") && r.match(/^[a-zA-Z0-9]+:\/\//)) { window.open(r, t, n); return; } } ); } function pe(e, n = k, t) { return [ g.listenToMessageFromWindow(window, R.UPDATE_HEIGHT, (r) => { const p = r.height ? r.height + ce : void 0; e.style.height = p ? `${t ? Math.min(p, t) : p}px` : n; }), g.listenToMessageFromWindow(window, R.SET_FULL_HEIGHT, ({ isUpToSmall: r }) => { e.style.height = r ? n : `${t || document.body.offsetHeight}px`; }) ]; } const le = Object.values(W); function ve(e, n, t) { if (U(e)) { const r = e[t]; return V(r) ? r[n] : r; } if (V(e)) { const r = e[n]; return U(r) ? r[t] : r; } return e; } function U(e) { return typeof e != "object" ? !1 : Object.keys(e).every((n) => le.includes(n)); } const he = /^\d+$/; function V(e) { return typeof e != "object" ? !1 : Object.keys(e).every((n) => typeof n == "number" || he.test(n)); } function ge(e) { const { params: n, provider: t, listeners: r } = e, [p, h] = j(null), w = y(null), f = y(t), c = y(r), i = y(null), s = y(null), o = B((d, u) => { try { console.log(`[WIDGET] ${d}`), u(); } catch (l) { const m = `Error ${d.toLowerCase()}`; console.error(`[WIDGET] ${m}`, l), h({ message: m, error: l }); } }, []); return O(() => () => { w.current = null, f.current = void 0, c.current = void 0; const d = s.current; d && (o("💥 Destroy widget", () => d.destroy()), s.current = null); }, []), O(() => { if (!i.current || JSON.stringify(w.current) === JSON.stringify(n)) return; const d = i.current, u = s.current; w.current = n, u === null ? o("Creating a new widget", () => { s.current = D(d, { params: n, provider: f.current, listeners: r }), c.current = r; }) : o("Updating the widget", () => u.updateParams(n)); }, [n, o]), O(() => { if (!s.current || f.current === t) return; f.current = t; const d = i.current; d && o("Updating the provider", () => { var u; (u = s.current) == null || u.destroy(), s.current = D(d, { params: n, provider: f.current, listeners: r }); }); }, [t, o]), O(() => { if (!s.current || c.current === r) return; const d = s.current; o("Updating the listeners", () => d.updateListeners(r)); }, [r, o]), p ? /* @__PURE__ */ $("div", { style: { color: "#ff3a3a" }, children: [ p.message, p.error.message && /* @__PURE__ */ A("pre", { style: { whiteSpace: "pre-wrap", fontSize: "0.75em" }, children: p.error.message }) ] }) : /* @__PURE__ */ A("div", { ref: i, style: { width: "100%" } }); } export { ge as CowSwapWidget, W as TradeType, _e as WIDGET_PALETTE_COLORS, R as WidgetMethodsEmit, S as WidgetMethodsListen, D as createCowSwapWidget, re as isCowSwapWidgetPalette, V as isPerNetworkConfig, U as isPerTradeTypeConfig, ve as resolveFlexibleConfig, g as widgetIframeTransport };