@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
JavaScript
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
};