@me.jango/signa 0.0.0-stage → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +81 -2
- package/core/controller.d.ts +43 -0
- package/core/index.d.ts +79 -0
- package/core/index.js +347 -0
- package/index.d.ts +1607 -0
- package/index.js +3074 -0
- package/package.json +20 -4
- package/react/index.d.ts +34 -0
- package/react/index.js +195 -0
- package/wagmi/index.d.ts +26 -0
- package/wagmi/index.js +97 -0
package/package.json
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@me.jango/signa",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Sign in to an app with a Signa passkey account or an external wallet, and review passkey payments",
|
|
5
|
+
"repository": { "type": "git", "url": "https://github.com/mejango/signa.git", "directory": "wallet-client" },
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"main": "./index.js",
|
|
9
|
+
"types": "./index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": { "types": "./index.d.ts", "import": "./index.js" },
|
|
12
|
+
"./core": { "types": "./core/index.d.ts", "import": "./core/index.js" },
|
|
13
|
+
"./wagmi": { "types": "./wagmi/index.d.ts", "import": "./wagmi/index.js" },
|
|
14
|
+
"./react": { "types": "./react/index.d.ts", "import": "./react/index.js" }
|
|
15
|
+
},
|
|
16
|
+
"files": ["index.js", "index.d.ts", "core/", "wagmi/", "react/", "README.md"],
|
|
17
|
+
"publishConfig": { "access": "public" },
|
|
18
|
+
"engines": { "node": ">=22" },
|
|
19
|
+
"dependencies": { "viem": "^2.37.5" },
|
|
20
|
+
"peerDependencies": { "react": "^18.3.1 || ^19.0.0", "wagmi": "^2.10.2 || ^3.0.0" },
|
|
21
|
+
"peerDependenciesMeta": { "react": { "optional": true }, "wagmi": { "optional": true } }
|
|
22
|
+
}
|
package/react/index.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { ConnectController, ConnectOption } from '../core/index.js';
|
|
4
|
+
|
|
5
|
+
type JBConnectModalProps = {
|
|
6
|
+
open: boolean;
|
|
7
|
+
controller: ConnectController;
|
|
8
|
+
/** Called for Escape, the backdrop and the cancel button, after the controller is cancelled. */
|
|
9
|
+
onClose(): void;
|
|
10
|
+
/** Which option is the passkey account; it renders as the primary button. */
|
|
11
|
+
passkeyId?: string;
|
|
12
|
+
/** The primary button's text. Defaults to the platform's prompt: Touch ID, Face ID, Windows Hello, or a passkey. */
|
|
13
|
+
passkeyLabel?: string;
|
|
14
|
+
title?: ReactNode;
|
|
15
|
+
/** Renders a pairing URI (a QR code, a deep link) once an option publishes one. */
|
|
16
|
+
renderHandoff?(uri: string, option: ConnectOption): ReactNode;
|
|
17
|
+
/** A mark for an option without an icon. Defaults to the option's initial. */
|
|
18
|
+
renderIcon?(option: ConnectOption): ReactNode;
|
|
19
|
+
/** Extra footer actions, laid out on the cancel button's baseline. */
|
|
20
|
+
footer?: ReactNode;
|
|
21
|
+
/** App content shown under the wallet tiles while idle, such as mobile wallet links. */
|
|
22
|
+
children?: ReactNode;
|
|
23
|
+
className?: string;
|
|
24
|
+
};
|
|
25
|
+
declare function JBConnectModal(props: JBConnectModalProps): react.JSX.Element;
|
|
26
|
+
|
|
27
|
+
/** Themed through custom properties so a Tailwind app and a plain-CSS app both look native. */
|
|
28
|
+
declare const connectModalCss = "\n.jb-connect{--jb-connect-bg:#fff;--jb-connect-fg:#1a1a1a;--jb-connect-muted:#6b6b6b;--jb-connect-line:#dcdcdc;--jb-connect-accent:#1a1a1a;--jb-connect-accent-fg:#fff;--jb-connect-radius:16px;--jb-connect-tile:44px;--jb-connect-font:inherit;--jb-connect-pad:32px;--jb-connect-backdrop:rgba(0,0,0,.45);--jb-connect-width:440px;\n box-sizing:border-box;width:min(var(--jb-connect-width),calc(100vw - 24px));max-height:calc(100dvh - 32px);padding:var(--jb-connect-pad);margin:auto;border:1px solid var(--jb-connect-line);border-radius:var(--jb-connect-radius);background:var(--jb-connect-bg);color:var(--jb-connect-fg);font:16px/1.4 var(--jb-connect-font);overscroll-behavior:contain}\n.jb-connect::backdrop{background:var(--jb-connect-backdrop)}\n.jb-connect *{box-sizing:border-box}\n.jb-connect h2{margin:0 0 20px;font-size:28px;font-weight:600;line-height:1.1}\n.jb-connect button{font:inherit;color:inherit;cursor:pointer}\n.jb-connect button:disabled{cursor:default;opacity:.55}\n.jb-connect .jb-connect-primary{display:block;width:100%;min-height:50px;padding:14px 20px;border:0;border-radius:calc(var(--jb-connect-radius) / 2);background:var(--jb-connect-accent);color:var(--jb-connect-accent-fg);font-weight:600}\n.jb-connect .jb-connect-powered{margin:8px 0 0;font-size:13px;color:var(--jb-connect-muted);text-align:center}\n.jb-connect .jb-connect-frame{display:block;width:calc(100% + 2 * var(--jb-connect-pad));height:14rem;margin:0 calc(-1 * var(--jb-connect-pad));border:1px solid var(--jb-connect-line);border-width:1px 0;background:var(--jb-connect-bg);transition:height .2s ease}\n@media (prefers-reduced-motion:reduce){.jb-connect .jb-connect-frame{transition:none}}\n.jb-connect .jb-connect-divider{margin:24px 0 10px;font-size:13px;color:var(--jb-connect-muted)}\n.jb-connect .jb-connect-tiles{display:flex;gap:10px;min-height:var(--jb-connect-tile);padding:4px;margin:-4px;overflow-x:auto}\n.jb-connect .jb-connect-tile{display:grid;place-items:center;flex:0 0 var(--jb-connect-tile);width:var(--jb-connect-tile);height:var(--jb-connect-tile);padding:8px;border:1px solid var(--jb-connect-line);border-radius:9px;background:var(--jb-connect-bg)}\n.jb-connect .jb-connect-tile img,.jb-connect .jb-connect-tile svg{width:22px;height:22px;object-fit:contain}\n.jb-connect .jb-connect-tile span{font-size:12px;font-weight:600;line-height:1}\n.jb-connect .jb-connect-status,.jb-connect .jb-connect-error{margin:16px 0 0;font-size:13px}\n.jb-connect .jb-connect-status{color:var(--jb-connect-muted)}\n.jb-connect .jb-connect-error{padding:12px 15px;border-left:3px solid #c8755e;background:#fae9e4;color:#873b2e;overflow-wrap:anywhere}\n.jb-connect .jb-connect-handoff{margin-top:16px}\n.jb-connect .jb-connect-footer{display:flex;align-items:baseline;justify-content:flex-end;gap:16px;margin-top:20px}\n.jb-connect .jb-connect-text{min-height:auto;padding:4px 0;margin:-4px 0;border:0;background:none;font-size:14px;text-decoration:underline;text-underline-offset:3px}\n@media (max-width:540px){.jb-connect{--jb-connect-pad:20px;padding:24px var(--jb-connect-pad)}.jb-connect h2{font-size:24px}}\n";
|
|
29
|
+
|
|
30
|
+
/** Names the prompt the person is about to see; "passkey" is the fallback when the platform is unknown. */
|
|
31
|
+
declare function passkeyLabel(userAgent: string): string;
|
|
32
|
+
|
|
33
|
+
export { JBConnectModal, connectModalCss, passkeyLabel };
|
|
34
|
+
export type { JBConnectModalProps };
|
package/react/index.js
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// wallet-client/src/react/JBConnectModal.tsx
|
|
4
|
+
import {
|
|
5
|
+
useEffect,
|
|
6
|
+
useRef,
|
|
7
|
+
useState,
|
|
8
|
+
useSyncExternalStore
|
|
9
|
+
} from "react";
|
|
10
|
+
|
|
11
|
+
// wallet-client/src/react/passkeyLabel.ts
|
|
12
|
+
function passkeyLabel(userAgent) {
|
|
13
|
+
if (/iPhone/.test(userAgent)) return "Continue with Face ID";
|
|
14
|
+
if (/Macintosh|iPad/.test(userAgent)) return "Continue with Touch ID";
|
|
15
|
+
if (/Windows/.test(userAgent)) return "Continue with Windows Hello";
|
|
16
|
+
return "Continue with a passkey";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// wallet-client/src/react/styles.ts
|
|
20
|
+
var connectModalCss = `
|
|
21
|
+
.jb-connect{--jb-connect-bg:#fff;--jb-connect-fg:#1a1a1a;--jb-connect-muted:#6b6b6b;--jb-connect-line:#dcdcdc;--jb-connect-accent:#1a1a1a;--jb-connect-accent-fg:#fff;--jb-connect-radius:16px;--jb-connect-tile:44px;--jb-connect-font:inherit;--jb-connect-pad:32px;--jb-connect-backdrop:rgba(0,0,0,.45);--jb-connect-width:440px;
|
|
22
|
+
box-sizing:border-box;width:min(var(--jb-connect-width),calc(100vw - 24px));max-height:calc(100dvh - 32px);padding:var(--jb-connect-pad);margin:auto;border:1px solid var(--jb-connect-line);border-radius:var(--jb-connect-radius);background:var(--jb-connect-bg);color:var(--jb-connect-fg);font:16px/1.4 var(--jb-connect-font);overscroll-behavior:contain}
|
|
23
|
+
.jb-connect::backdrop{background:var(--jb-connect-backdrop)}
|
|
24
|
+
.jb-connect *{box-sizing:border-box}
|
|
25
|
+
.jb-connect h2{margin:0 0 20px;font-size:28px;font-weight:600;line-height:1.1}
|
|
26
|
+
.jb-connect button{font:inherit;color:inherit;cursor:pointer}
|
|
27
|
+
.jb-connect button:disabled{cursor:default;opacity:.55}
|
|
28
|
+
.jb-connect .jb-connect-primary{display:block;width:100%;min-height:50px;padding:14px 20px;border:0;border-radius:calc(var(--jb-connect-radius) / 2);background:var(--jb-connect-accent);color:var(--jb-connect-accent-fg);font-weight:600}
|
|
29
|
+
.jb-connect .jb-connect-powered{margin:8px 0 0;font-size:13px;color:var(--jb-connect-muted);text-align:center}
|
|
30
|
+
.jb-connect .jb-connect-frame{display:block;width:calc(100% + 2 * var(--jb-connect-pad));height:14rem;margin:0 calc(-1 * var(--jb-connect-pad));border:1px solid var(--jb-connect-line);border-width:1px 0;background:var(--jb-connect-bg);transition:height .2s ease}
|
|
31
|
+
@media (prefers-reduced-motion:reduce){.jb-connect .jb-connect-frame{transition:none}}
|
|
32
|
+
.jb-connect .jb-connect-divider{margin:24px 0 10px;font-size:13px;color:var(--jb-connect-muted)}
|
|
33
|
+
.jb-connect .jb-connect-tiles{display:flex;gap:10px;min-height:var(--jb-connect-tile);padding:4px;margin:-4px;overflow-x:auto}
|
|
34
|
+
.jb-connect .jb-connect-tile{display:grid;place-items:center;flex:0 0 var(--jb-connect-tile);width:var(--jb-connect-tile);height:var(--jb-connect-tile);padding:8px;border:1px solid var(--jb-connect-line);border-radius:9px;background:var(--jb-connect-bg)}
|
|
35
|
+
.jb-connect .jb-connect-tile img,.jb-connect .jb-connect-tile svg{width:22px;height:22px;object-fit:contain}
|
|
36
|
+
.jb-connect .jb-connect-tile span{font-size:12px;font-weight:600;line-height:1}
|
|
37
|
+
.jb-connect .jb-connect-status,.jb-connect .jb-connect-error{margin:16px 0 0;font-size:13px}
|
|
38
|
+
.jb-connect .jb-connect-status{color:var(--jb-connect-muted)}
|
|
39
|
+
.jb-connect .jb-connect-error{padding:12px 15px;border-left:3px solid #c8755e;background:#fae9e4;color:#873b2e;overflow-wrap:anywhere}
|
|
40
|
+
.jb-connect .jb-connect-handoff{margin-top:16px}
|
|
41
|
+
.jb-connect .jb-connect-footer{display:flex;align-items:baseline;justify-content:flex-end;gap:16px;margin-top:20px}
|
|
42
|
+
.jb-connect .jb-connect-text{min-height:auto;padding:4px 0;margin:-4px 0;border:0;background:none;font-size:14px;text-decoration:underline;text-underline-offset:3px}
|
|
43
|
+
@media (max-width:540px){.jb-connect{--jb-connect-pad:20px;padding:24px var(--jb-connect-pad)}.jb-connect h2{font-size:24px}}
|
|
44
|
+
`;
|
|
45
|
+
|
|
46
|
+
// wallet-client/src/react/JBConnectModal.tsx
|
|
47
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
48
|
+
function JBConnectModal(props) {
|
|
49
|
+
const { controller, open, onClose, passkeyId = "juicebox-center" } = props;
|
|
50
|
+
const state = useSyncExternalStore(
|
|
51
|
+
controller.subscribe,
|
|
52
|
+
controller.getState,
|
|
53
|
+
controller.getState
|
|
54
|
+
);
|
|
55
|
+
const dialog = useRef(null);
|
|
56
|
+
const [platformLabel, setPlatformLabel] = useState("Continue with a passkey");
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
setPlatformLabel(passkeyLabel(navigator.userAgent));
|
|
59
|
+
}, []);
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
const element = dialog.current;
|
|
62
|
+
if (open && !element.open) {
|
|
63
|
+
element.showModal();
|
|
64
|
+
element.querySelector("h2")?.focus();
|
|
65
|
+
} else if (!open && element.open) {
|
|
66
|
+
element.close();
|
|
67
|
+
controller.cancel();
|
|
68
|
+
}
|
|
69
|
+
}, [open, controller]);
|
|
70
|
+
const cancel = () => {
|
|
71
|
+
controller.cancel();
|
|
72
|
+
onClose();
|
|
73
|
+
};
|
|
74
|
+
const frame = useRef(null);
|
|
75
|
+
const [frameHeight, setFrameHeight] = useState();
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
if (!state.frameName) return;
|
|
78
|
+
setFrameHeight(void 0);
|
|
79
|
+
const sized = (event) => {
|
|
80
|
+
const data = event.data;
|
|
81
|
+
if (!frame.current?.contentWindow || event.source !== frame.current.contentWindow || data?.type !== "juicebox-center:size" || typeof data.height !== "number" || !Number.isFinite(data.height))
|
|
82
|
+
return;
|
|
83
|
+
setFrameHeight(Math.min(1200, Math.max(160, Math.ceil(data.height))) + 2);
|
|
84
|
+
if (dialog.current)
|
|
85
|
+
frame.current.contentWindow.postMessage(
|
|
86
|
+
{ type: "juicebox-center:theme", theme: themeOf(dialog.current) },
|
|
87
|
+
// Design tokens only; the page inside checks its framer, this dialog does not learn the issuer.
|
|
88
|
+
"*"
|
|
89
|
+
);
|
|
90
|
+
};
|
|
91
|
+
window.addEventListener("message", sized);
|
|
92
|
+
return () => window.removeEventListener("message", sized);
|
|
93
|
+
}, [state.frameName]);
|
|
94
|
+
const passkey = controller.options.find((option) => option.id === passkeyId);
|
|
95
|
+
const wallets = controller.options.filter(
|
|
96
|
+
(option) => option.id !== passkeyId
|
|
97
|
+
);
|
|
98
|
+
const pending = state.pending ? controller.options.find((option) => option.id === state.pending) : void 0;
|
|
99
|
+
return /* @__PURE__ */ jsxs(
|
|
100
|
+
"dialog",
|
|
101
|
+
{
|
|
102
|
+
ref: dialog,
|
|
103
|
+
className: "jb-connect" + (props.className ? " " + props.className : ""),
|
|
104
|
+
"aria-label": typeof props.title === "string" ? props.title : "Sign in",
|
|
105
|
+
onCancel: (event) => {
|
|
106
|
+
event.preventDefault();
|
|
107
|
+
cancel();
|
|
108
|
+
},
|
|
109
|
+
onMouseDown: (event) => {
|
|
110
|
+
if (event.target !== event.currentTarget) return;
|
|
111
|
+
const box = event.currentTarget.getBoundingClientRect();
|
|
112
|
+
if (event.clientX < box.left || event.clientX > box.right || event.clientY < box.top || event.clientY > box.bottom)
|
|
113
|
+
cancel();
|
|
114
|
+
},
|
|
115
|
+
children: [
|
|
116
|
+
/* @__PURE__ */ jsx("style", { children: connectModalCss }),
|
|
117
|
+
/* @__PURE__ */ jsx("h2", { tabIndex: -1, children: props.title ?? "Sign in" }),
|
|
118
|
+
pending ? /* @__PURE__ */ jsx("div", { role: "status", "aria-label": "Connection", children: state.frameName ? /* @__PURE__ */ jsx(
|
|
119
|
+
"iframe",
|
|
120
|
+
{
|
|
121
|
+
ref: frame,
|
|
122
|
+
name: state.frameName,
|
|
123
|
+
className: "jb-connect-frame",
|
|
124
|
+
title: "Signa",
|
|
125
|
+
allow: `publickey-credentials-get ${state.frameOrigin}; publickey-credentials-create ${state.frameOrigin}`,
|
|
126
|
+
referrerPolicy: "no-referrer",
|
|
127
|
+
style: frameHeight ? { height: frameHeight } : void 0
|
|
128
|
+
}
|
|
129
|
+
) : state.handoffUri && props.renderHandoff ? /* @__PURE__ */ jsx("div", { className: "jb-connect-handoff", children: props.renderHandoff(state.handoffUri, pending) }) : /* @__PURE__ */ jsx("p", { className: "jb-connect-status", children: pending === passkey ? "Continuing at Signa\u2026" : `Opening ${pending.name}\u2026` }) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
130
|
+
passkey && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
131
|
+
/* @__PURE__ */ jsx(
|
|
132
|
+
"button",
|
|
133
|
+
{
|
|
134
|
+
type: "button",
|
|
135
|
+
className: "jb-connect-primary",
|
|
136
|
+
disabled: passkey.disabled,
|
|
137
|
+
onClick: () => void controller.choose(passkey.id),
|
|
138
|
+
children: props.passkeyLabel ?? platformLabel
|
|
139
|
+
}
|
|
140
|
+
),
|
|
141
|
+
/* @__PURE__ */ jsx("p", { className: "jb-connect-powered", children: "Powered by Signa" })
|
|
142
|
+
] }),
|
|
143
|
+
wallets.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
144
|
+
/* @__PURE__ */ jsx("p", { className: "jb-connect-divider", children: passkey ? "or connect a wallet" : "Connect a wallet" }),
|
|
145
|
+
/* @__PURE__ */ jsx("div", { className: "jb-connect-tiles", children: wallets.map((option) => /* @__PURE__ */ jsx(
|
|
146
|
+
"button",
|
|
147
|
+
{
|
|
148
|
+
type: "button",
|
|
149
|
+
className: "jb-connect-tile",
|
|
150
|
+
title: option.name,
|
|
151
|
+
"aria-label": option.name,
|
|
152
|
+
disabled: option.disabled,
|
|
153
|
+
onClick: () => void controller.choose(option.id),
|
|
154
|
+
children: option.icon ? /* @__PURE__ */ jsx("img", { src: option.icon, alt: "" }) : props.renderIcon?.(option) ?? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: option.name.slice(0, 1) })
|
|
155
|
+
},
|
|
156
|
+
option.id
|
|
157
|
+
)) })
|
|
158
|
+
] }),
|
|
159
|
+
props.children
|
|
160
|
+
] }),
|
|
161
|
+
state.error && /* @__PURE__ */ jsx("p", { role: "alert", className: "jb-connect-error", children: state.error }),
|
|
162
|
+
/* @__PURE__ */ jsxs("div", { className: "jb-connect-footer", children: [
|
|
163
|
+
props.footer,
|
|
164
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "jb-connect-text", onClick: cancel, children: "Cancel" })
|
|
165
|
+
] })
|
|
166
|
+
]
|
|
167
|
+
}
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
function themeOf(dialog) {
|
|
171
|
+
const style = getComputedStyle(dialog);
|
|
172
|
+
const token = (name) => style.getPropertyValue(name).trim();
|
|
173
|
+
const theme = {};
|
|
174
|
+
for (const [key, name] of [
|
|
175
|
+
["background", "--jb-connect-bg"],
|
|
176
|
+
["foreground", "--jb-connect-fg"],
|
|
177
|
+
["muted", "--jb-connect-muted"],
|
|
178
|
+
["line", "--jb-connect-line"],
|
|
179
|
+
["accent", "--jb-connect-accent"],
|
|
180
|
+
["accentForeground", "--jb-connect-accent-fg"],
|
|
181
|
+
["radius", "--jb-connect-radius"],
|
|
182
|
+
["inset", "--jb-connect-pad"]
|
|
183
|
+
]) {
|
|
184
|
+
const value = token(name);
|
|
185
|
+
if (value) theme[key] = value;
|
|
186
|
+
}
|
|
187
|
+
const font = style.fontFamily;
|
|
188
|
+
if (font) theme.font = font;
|
|
189
|
+
return theme;
|
|
190
|
+
}
|
|
191
|
+
export {
|
|
192
|
+
JBConnectModal,
|
|
193
|
+
connectModalCss,
|
|
194
|
+
passkeyLabel
|
|
195
|
+
};
|
package/wagmi/index.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { CreateConnectorFn } from 'wagmi';
|
|
2
|
+
import { Address } from 'viem';
|
|
3
|
+
|
|
4
|
+
/** The slice of a Signa wallet client the connector needs. Signing never crosses this seam. */
|
|
5
|
+
type CenterAccountWallet = {
|
|
6
|
+
restoreConnection(): {
|
|
7
|
+
address: Address;
|
|
8
|
+
chainId: 8453;
|
|
9
|
+
accountId: string;
|
|
10
|
+
expiresAt: number;
|
|
11
|
+
} | null;
|
|
12
|
+
disconnect(): void;
|
|
13
|
+
};
|
|
14
|
+
type CenterAccountConnectorOptions = {
|
|
15
|
+
wallet(): Promise<CenterAccountWallet>;
|
|
16
|
+
read(input: {
|
|
17
|
+
method: string;
|
|
18
|
+
params?: unknown[];
|
|
19
|
+
}): Promise<unknown>;
|
|
20
|
+
};
|
|
21
|
+
/** A read-only wagmi connector for a Signa passkey account on Base. The connection's
|
|
22
|
+
* request key cannot sign or send arbitrary operations; payments go through Signa's review flow. */
|
|
23
|
+
declare function centerAccountConnector(options: CenterAccountConnectorOptions): CreateConnectorFn;
|
|
24
|
+
|
|
25
|
+
export { centerAccountConnector };
|
|
26
|
+
export type { CenterAccountConnectorOptions, CenterAccountWallet };
|
package/wagmi/index.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// wallet-client/src/wagmi/connector.ts
|
|
4
|
+
import { createConnector } from "wagmi";
|
|
5
|
+
import { base } from "wagmi/chains";
|
|
6
|
+
var reads = /* @__PURE__ */ new Set([
|
|
7
|
+
"eth_call",
|
|
8
|
+
"eth_getBalance",
|
|
9
|
+
"eth_getCode",
|
|
10
|
+
"eth_getStorageAt",
|
|
11
|
+
"eth_getTransactionCount",
|
|
12
|
+
"eth_getTransactionReceipt",
|
|
13
|
+
"eth_getTransactionByHash",
|
|
14
|
+
"eth_getBlockByNumber",
|
|
15
|
+
"eth_getBlockByHash",
|
|
16
|
+
"eth_blockNumber",
|
|
17
|
+
"eth_getLogs"
|
|
18
|
+
]);
|
|
19
|
+
var unsupported = (chain = false) => Object.assign(
|
|
20
|
+
new Error(
|
|
21
|
+
chain ? "This Signa account uses Base. Connect an external wallet for other networks." : "This action needs its own review at Signa. Use a payment review, or connect an external wallet."
|
|
22
|
+
),
|
|
23
|
+
{ code: chain ? 4902 : 4200 }
|
|
24
|
+
);
|
|
25
|
+
function centerAccountConnector(options) {
|
|
26
|
+
return createConnector((config) => {
|
|
27
|
+
const connection = async () => {
|
|
28
|
+
const value = (await options.wallet()).restoreConnection();
|
|
29
|
+
if (!value || value.chainId !== 8453 || value.accountId !== "eip155:8453:" + value.address.toLowerCase() || value.expiresAt <= Math.floor(Date.now() / 1e3))
|
|
30
|
+
return null;
|
|
31
|
+
return value;
|
|
32
|
+
};
|
|
33
|
+
const provider = {
|
|
34
|
+
async request(input) {
|
|
35
|
+
const current = await connection();
|
|
36
|
+
if (input.method === "eth_chainId") return "0x2105";
|
|
37
|
+
if (input.method === "eth_accounts" || input.method === "eth_requestAccounts")
|
|
38
|
+
return current ? [current.address] : [];
|
|
39
|
+
if (input.method === "wallet_switchEthereumChain") {
|
|
40
|
+
const requested = input.params?.[0];
|
|
41
|
+
if (requested?.chainId !== "0x2105") throw unsupported(true);
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
if (reads.has(input.method)) return options.read(input);
|
|
45
|
+
throw unsupported();
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
return {
|
|
49
|
+
id: "juicebox-center",
|
|
50
|
+
name: "Signa account",
|
|
51
|
+
type: "juicebox-center",
|
|
52
|
+
async connect(parameters) {
|
|
53
|
+
if (parameters?.chainId !== void 0 && parameters.chainId !== 8453)
|
|
54
|
+
throw unsupported(true);
|
|
55
|
+
const current = await connection();
|
|
56
|
+
if (!current)
|
|
57
|
+
throw new Error("Sign in with your Signa account first.");
|
|
58
|
+
const accounts = parameters?.withCapabilities ? [{ address: current.address, capabilities: {} }] : [current.address];
|
|
59
|
+
return { accounts, chainId: 8453 };
|
|
60
|
+
},
|
|
61
|
+
async disconnect() {
|
|
62
|
+
(await options.wallet()).disconnect();
|
|
63
|
+
},
|
|
64
|
+
async getAccounts() {
|
|
65
|
+
const current = await connection();
|
|
66
|
+
return current ? [current.address] : [];
|
|
67
|
+
},
|
|
68
|
+
async getChainId() {
|
|
69
|
+
return 8453;
|
|
70
|
+
},
|
|
71
|
+
async getProvider() {
|
|
72
|
+
return provider;
|
|
73
|
+
},
|
|
74
|
+
async isAuthorized() {
|
|
75
|
+
return !!await connection();
|
|
76
|
+
},
|
|
77
|
+
async switchChain({ chainId }) {
|
|
78
|
+
if (chainId !== 8453) throw unsupported(true);
|
|
79
|
+
config.emitter.emit("change", { chainId });
|
|
80
|
+
return base;
|
|
81
|
+
},
|
|
82
|
+
onAccountsChanged(accounts) {
|
|
83
|
+
if (!accounts.length) config.emitter.emit("disconnect");
|
|
84
|
+
else config.emitter.emit("change", { accounts });
|
|
85
|
+
},
|
|
86
|
+
onChainChanged(chainId) {
|
|
87
|
+
if (Number(chainId) !== 8453) config.emitter.emit("disconnect");
|
|
88
|
+
},
|
|
89
|
+
onDisconnect() {
|
|
90
|
+
config.emitter.emit("disconnect");
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
export {
|
|
96
|
+
centerAccountConnector
|
|
97
|
+
};
|