@polkahub/multisig 0.2.0 → 0.3.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.
@@ -0,0 +1,230 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import { useModalContext, usePlugin, usePolkaHubContext, useAvailableAccounts, AddressIdentity } from '@polkahub/context';
3
+ import { defaultSerialize, addrEq } from '@polkahub/plugin';
4
+ import { proxyProviderId } from '@polkahub/proxy';
5
+ import { InlineAddressInput, Input, Button, AlertBox, AccountPicker } from '@polkahub/ui-components';
6
+ import { useState, useMemo, useEffect } from 'react';
7
+ import { from, filter, map, switchMap, EMPTY, merge, firstValueFrom } from 'rxjs';
8
+ import { multisigProviderId } from './provider.js';
9
+
10
+ const AddIndexedMultisig = ({ getMultisigDetails }) => {
11
+ const { popContent } = useModalContext();
12
+ const multisigProvider = usePlugin(multisigProviderId);
13
+ const proxyProvider = usePlugin(proxyProviderId);
14
+ const { polkaHub } = usePolkaHubContext();
15
+ const [multisigAddress, setMultisigAddress] = useState(
16
+ null
17
+ );
18
+ const [name, setName] = useState("");
19
+ const [selectedAccount, setSelectedAccount] = useState(null);
20
+ return /* @__PURE__ */ jsxs(
21
+ "form",
22
+ {
23
+ className: "space-y-2",
24
+ onSubmit: async (evt) => {
25
+ evt.preventDefault();
26
+ if (!multisigAddress || !selectedAccount) return null;
27
+ const plugins = polkaHub.plugins$.getValue();
28
+ const parentProvider = plugins.find(
29
+ (p) => p.id === selectedAccount.provider
30
+ );
31
+ if (!parentProvider)
32
+ throw new Error(
33
+ `Parent provider ${selectedAccount.provider} not found`
34
+ );
35
+ const details = selectedAccount.multisig.result;
36
+ const serializeFn = parentProvider.serialize ?? defaultSerialize;
37
+ if (selectedAccount.multisig.proxy) {
38
+ const multisigAccount = await multisigProvider.addMultisig({
39
+ signatories: details.addresses,
40
+ threshold: details.threshold,
41
+ parentSigner: serializeFn(selectedAccount)
42
+ });
43
+ await proxyProvider?.addProxy({
44
+ real: multisigAddress,
45
+ parentSigner: (multisigProvider.serialize ?? defaultSerialize)(
46
+ multisigAccount
47
+ ),
48
+ name: name.trim() ? name.trim() : void 0
49
+ });
50
+ } else {
51
+ multisigProvider?.addMultisig({
52
+ signatories: details.addresses,
53
+ threshold: details.threshold,
54
+ parentSigner: serializeFn(selectedAccount),
55
+ name: name.trim() ? name.trim() : void 0
56
+ });
57
+ }
58
+ popContent();
59
+ },
60
+ children: [
61
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
62
+ /* @__PURE__ */ jsx("h3", { className: "font-medium text-muted-foreground", children: "Insert Multisig Address" }),
63
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
64
+ /* @__PURE__ */ jsx(
65
+ InlineAddressInput,
66
+ {
67
+ value: multisigAddress,
68
+ onChange: setMultisigAddress,
69
+ className: "max-w-auto shrink-[2]"
70
+ }
71
+ ),
72
+ /* @__PURE__ */ jsx(
73
+ Input,
74
+ {
75
+ name: "account-name",
76
+ value: name,
77
+ onChange: (evt) => setName(evt.target.value),
78
+ placeholder: "Name (optional)",
79
+ className: "shrink-[3]"
80
+ }
81
+ )
82
+ ] })
83
+ ] }),
84
+ multisigAddress ? /* @__PURE__ */ jsx(
85
+ IndexedMultisigInfo,
86
+ {
87
+ value: selectedAccount,
88
+ onChange: setSelectedAccount,
89
+ address: multisigAddress,
90
+ getMultisigDetails
91
+ }
92
+ ) : null,
93
+ /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx(Button, { disabled: !multisigAddress || !selectedAccount, children: "Add Multisig" }) })
94
+ ]
95
+ }
96
+ );
97
+ };
98
+ const IndexedMultisigInfo = ({ value, onChange, address, getMultisigDetails }) => {
99
+ const proxyProvider = usePlugin(proxyProviderId);
100
+ const multisigDetails = useAsync(() => {
101
+ const directMultisig$ = from(getMultisigDetails(address)).pipe(
102
+ filter((v) => !!v),
103
+ map((result) => ({
104
+ proxy: void 0,
105
+ address,
106
+ result
107
+ }))
108
+ );
109
+ const proxyMultisig$ = proxyProvider ? from(proxyProvider.getDelegates(address)).pipe(
110
+ switchMap((res) => {
111
+ if (!res) return EMPTY;
112
+ const addresses = [...new Set(res.map((v) => v.delegate))];
113
+ return merge(
114
+ ...addresses.map(
115
+ (addr) => from(getMultisigDetails(addr)).pipe(
116
+ filter((v) => !!v),
117
+ map((result) => ({
118
+ proxy: address,
119
+ address: addr,
120
+ result
121
+ }))
122
+ )
123
+ )
124
+ );
125
+ })
126
+ ) : EMPTY;
127
+ return firstValueFrom(merge(directMultisig$, proxyMultisig$), {
128
+ defaultValue: null
129
+ });
130
+ }, [address]);
131
+ const availableAccounts = useAvailableAccounts();
132
+ const availableSigners = useMemo(
133
+ () => Object.entries(availableAccounts).map(([name, accounts]) => ({
134
+ name,
135
+ accounts: accounts.filter((acc) => !!acc.signer)
136
+ })).filter(({ accounts }) => accounts.length > 0),
137
+ [availableAccounts]
138
+ );
139
+ if (multisigDetails.type === "loading") return null;
140
+ if (multisigDetails.value == null)
141
+ return /* @__PURE__ */ jsx(AlertBox, { variant: "error", children: "Multisig details not found. Try manual input." });
142
+ const details = multisigDetails.value;
143
+ const notice = details.proxy ? /* @__PURE__ */ jsxs(AlertBox, { children: [
144
+ /* @__PURE__ */ jsxs("p", { children: [
145
+ "The address you entered was detected as a ",
146
+ /* @__PURE__ */ jsx("strong", { children: "proxy" }),
147
+ ", not a multisig."
148
+ ] }),
149
+ /* @__PURE__ */ jsxs("p", { children: [
150
+ "Both signers will be created, but your entered address will appear under the ",
151
+ /* @__PURE__ */ jsx("strong", { children: "Proxies" }),
152
+ " group instead of ",
153
+ /* @__PURE__ */ jsx("strong", { children: "Multisigs" }),
154
+ "."
155
+ ] })
156
+ ] }) : null;
157
+ const selectableSigners = availableSigners.map(({ name, accounts }) => ({
158
+ name,
159
+ accounts: accounts.filter(
160
+ (acc) => details.result.addresses.some((addr) => addrEq(acc.address, addr))
161
+ )
162
+ })).filter(({ accounts }) => accounts.length > 0);
163
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
164
+ notice,
165
+ /* @__PURE__ */ jsxs("div", { children: [
166
+ /* @__PURE__ */ jsxs("h3", { className: "font-medium text-muted-foreground", children: [
167
+ "Multisig signatories (threshold ",
168
+ details.result.threshold,
169
+ ")"
170
+ ] }),
171
+ /* @__PURE__ */ jsx("ul", { children: details.result.addresses.map((addr) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(AddressIdentity, { addr }) }, addr)) })
172
+ ] }),
173
+ /* @__PURE__ */ jsxs("div", { children: [
174
+ /* @__PURE__ */ jsx("h3", { className: "font-medium text-muted-foreground", children: "Select your signer" }),
175
+ selectableSigners.length ? /* @__PURE__ */ jsx(
176
+ AccountPicker,
177
+ {
178
+ value,
179
+ onChange: (value2) => onChange(
180
+ value2 ? {
181
+ ...value2,
182
+ multisig: details
183
+ } : null
184
+ ),
185
+ groups: selectableSigners,
186
+ className: "max-w-auto",
187
+ disableClear: true,
188
+ renderAddress: (account) => /* @__PURE__ */ jsx(
189
+ AddressIdentity,
190
+ {
191
+ addr: account.address,
192
+ name: account?.name,
193
+ copyable: false
194
+ }
195
+ )
196
+ }
197
+ ) : /* @__PURE__ */ jsxs(AlertBox, { variant: "error", children: [
198
+ /* @__PURE__ */ jsx("p", { children: "None of the signatories in this multisig match your configured signers." }),
199
+ /* @__PURE__ */ jsx("p", { children: "Please configure a signer account first." })
200
+ ] })
201
+ ] })
202
+ ] });
203
+ };
204
+ const useAsync = (fn, deps) => {
205
+ const [value, setValue] = useState({
206
+ type: "loading"
207
+ });
208
+ useEffect(() => {
209
+ let cancelled = false;
210
+ setValue({ type: "loading" });
211
+ fn().then(
212
+ (value2) => {
213
+ if (cancelled) return;
214
+ setValue({ type: "result", value: value2 });
215
+ },
216
+ (ex) => {
217
+ if (cancelled) return;
218
+ console.error(ex);
219
+ setValue({ type: "error" });
220
+ }
221
+ );
222
+ return () => {
223
+ cancelled = true;
224
+ };
225
+ }, deps);
226
+ return value;
227
+ };
228
+
229
+ export { AddIndexedMultisig };
230
+ //# sourceMappingURL=AddIndexedMultisig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AddIndexedMultisig.js","sources":["../src/AddIndexedMultisig.tsx"],"sourcesContent":["import {\n AddressIdentity,\n useAvailableAccounts,\n useModalContext,\n usePlugin,\n usePolkaHubContext,\n} from \"@polkahub/context\";\nimport {\n Account,\n AccountAddress,\n addrEq,\n defaultSerialize,\n} from \"@polkahub/plugin\";\nimport { ProxyProvider, proxyProviderId } from \"@polkahub/proxy\";\nimport {\n AccountPicker,\n AlertBox,\n Button,\n InlineAddressInput,\n Input,\n} from \"@polkahub/ui-components\";\nimport { useEffect, useMemo, useState, type FC } from \"react\";\nimport {\n EMPTY,\n filter,\n firstValueFrom,\n from,\n map,\n merge,\n switchMap,\n} from \"rxjs\";\nimport { MultisigProvider, multisigProviderId } from \"./provider\";\n\ntype AccountWithMultisig = Account & {\n multisig: {\n proxy?: AccountAddress;\n address: AccountAddress;\n result: {\n addresses: AccountAddress[];\n threshold: number;\n };\n };\n};\n\nexport type GetMultisigDetails = (\n address: AccountAddress\n) => Promise<{ addresses: AccountAddress[]; threshold: number } | null>;\n\nexport const AddIndexedMultisig: FC<{\n getMultisigDetails: GetMultisigDetails;\n}> = ({ getMultisigDetails }) => {\n const { popContent } = useModalContext();\n const multisigProvider = usePlugin<MultisigProvider>(multisigProviderId);\n const proxyProvider = usePlugin<ProxyProvider>(proxyProviderId);\n const { polkaHub } = usePolkaHubContext();\n const [multisigAddress, setMultisigAddress] = useState<AccountAddress | null>(\n null\n );\n const [name, setName] = useState(\"\");\n const [selectedAccount, setSelectedAccount] =\n useState<AccountWithMultisig | null>(null);\n\n return (\n <form\n className=\"space-y-2\"\n onSubmit={async (evt) => {\n evt.preventDefault();\n if (!multisigAddress || !selectedAccount) return null;\n\n const plugins = polkaHub.plugins$.getValue();\n const parentProvider = plugins.find(\n (p) => p.id === selectedAccount.provider\n );\n if (!parentProvider)\n throw new Error(\n `Parent provider ${selectedAccount.provider} not found`\n );\n\n const details = selectedAccount.multisig.result;\n const serializeFn = parentProvider.serialize ?? defaultSerialize;\n\n if (selectedAccount.multisig.proxy) {\n const multisigAccount = await multisigProvider!.addMultisig({\n signatories: details.addresses,\n threshold: details.threshold,\n parentSigner: serializeFn(selectedAccount),\n });\n\n await proxyProvider?.addProxy({\n real: multisigAddress,\n parentSigner: (multisigProvider!.serialize ?? defaultSerialize)(\n multisigAccount\n ),\n name: name.trim() ? name.trim() : undefined,\n });\n } else {\n multisigProvider?.addMultisig({\n signatories: details.addresses,\n threshold: details.threshold,\n parentSigner: serializeFn(selectedAccount),\n name: name.trim() ? name.trim() : undefined,\n });\n }\n\n popContent();\n }}\n >\n <div className=\"space-y-2\">\n <h3 className=\"font-medium text-muted-foreground\">\n Insert Multisig Address\n </h3>\n <div className=\"flex gap-2\">\n <InlineAddressInput\n value={multisigAddress}\n onChange={setMultisigAddress}\n className=\"max-w-auto shrink-[2]\"\n />\n <Input\n name=\"account-name\"\n value={name}\n onChange={(evt) => setName(evt.target.value)}\n placeholder=\"Name (optional)\"\n className=\"shrink-[3]\"\n />\n </div>\n </div>\n {multisigAddress ? (\n <IndexedMultisigInfo\n value={selectedAccount}\n onChange={setSelectedAccount}\n address={multisigAddress}\n getMultisigDetails={getMultisigDetails}\n />\n ) : null}\n <div className=\"flex justify-end\">\n <Button disabled={!multisigAddress || !selectedAccount}>\n Add Multisig\n </Button>\n </div>\n </form>\n );\n};\n\nconst IndexedMultisigInfo: FC<{\n value: AccountWithMultisig | null;\n onChange: (value: AccountWithMultisig | null) => void;\n address: AccountAddress;\n getMultisigDetails: GetMultisigDetails;\n}> = ({ value, onChange, address, getMultisigDetails }) => {\n const proxyProvider = usePlugin<ProxyProvider>(proxyProviderId);\n const multisigDetails = useAsync(() => {\n const directMultisig$ = from(getMultisigDetails(address)).pipe(\n filter((v) => !!v),\n map((result) => ({\n proxy: undefined,\n address,\n result,\n }))\n );\n const proxyMultisig$ = proxyProvider\n ? from(proxyProvider.getDelegates(address)).pipe(\n switchMap((res) => {\n if (!res) return EMPTY;\n const addresses = [...new Set(res.map((v) => v.delegate))];\n return merge(\n ...addresses.map((addr) =>\n from(getMultisigDetails(addr)).pipe(\n filter((v) => !!v),\n map((result) => ({\n proxy: address,\n address: addr,\n result,\n }))\n )\n )\n );\n })\n )\n : EMPTY;\n\n // This covers the most common scenario of a pure proxy, but TODO might fail for other scenarios: Multiple delegators, or a multisig that's also a proxy.\n return firstValueFrom(merge(directMultisig$, proxyMultisig$), {\n defaultValue: null,\n });\n }, [address]);\n const availableAccounts = useAvailableAccounts();\n const availableSigners = useMemo(\n () =>\n Object.entries(availableAccounts)\n .map(([name, accounts]) => ({\n name,\n accounts: accounts.filter((acc) => !!acc.signer),\n }))\n .filter(({ accounts }) => accounts.length > 0),\n [availableAccounts]\n );\n\n if (multisigDetails.type === \"loading\") return null;\n if (multisigDetails.value == null)\n return (\n <AlertBox variant=\"error\">\n Multisig details not found. Try manual input.\n </AlertBox>\n );\n\n const details = multisigDetails.value;\n const notice = details.proxy ? (\n <AlertBox>\n <p>\n The address you entered was detected as a <strong>proxy</strong>, not a\n multisig.\n </p>\n <p>\n Both signers will be created, but your entered address will appear under\n the <strong>Proxies</strong> group instead of <strong>Multisigs</strong>\n .\n </p>\n </AlertBox>\n ) : null;\n\n const selectableSigners = availableSigners\n .map(({ name, accounts }) => ({\n name,\n accounts: accounts.filter((acc) =>\n details.result.addresses.some((addr) => addrEq(acc.address, addr))\n ),\n }))\n .filter(({ accounts }) => accounts.length > 0);\n\n return (\n <div className=\"space-y-2\">\n {notice}\n <div>\n <h3 className=\"font-medium text-muted-foreground\">\n Multisig signatories (threshold {details.result.threshold})\n </h3>\n <ul>\n {details.result.addresses.map((addr) => (\n <li key={addr}>\n <AddressIdentity addr={addr} />\n </li>\n ))}\n </ul>\n </div>\n <div>\n <h3 className=\"font-medium text-muted-foreground\">\n Select your signer\n </h3>\n {selectableSigners.length ? (\n <AccountPicker\n value={value}\n onChange={(value) =>\n onChange(\n value\n ? {\n ...value,\n multisig: details,\n }\n : null\n )\n }\n groups={selectableSigners}\n className=\"max-w-auto\"\n disableClear\n renderAddress={(account) => (\n <AddressIdentity\n addr={account.address}\n name={account?.name}\n copyable={false}\n />\n )}\n />\n ) : (\n <AlertBox variant=\"error\">\n <p>\n None of the signatories in this multisig match your configured\n signers.\n </p>\n <p>Please configure a signer account first.</p>\n </AlertBox>\n )}\n </div>\n </div>\n );\n};\n\nconst useAsync = <T,>(fn: () => Promise<T>, deps: unknown[]) => {\n const [value, setValue] = useState<\n | {\n type: \"loading\" | \"error\";\n value?: never;\n }\n | {\n type: \"result\";\n value: T;\n }\n >({\n type: \"loading\",\n });\n\n useEffect(() => {\n let cancelled = false;\n\n setValue({ type: \"loading\" });\n fn().then(\n (value) => {\n if (cancelled) return;\n setValue({ type: \"result\", value });\n },\n (ex) => {\n if (cancelled) return;\n console.error(ex);\n setValue({ type: \"error\" });\n }\n );\n\n return () => {\n cancelled = true;\n };\n }, deps);\n\n return value;\n};\n"],"names":["value"],"mappings":"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"}
@@ -0,0 +1,190 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import { useModalContext, usePlugin, usePolkaHubContext, useAvailableAccounts, AddressIdentity } from '@polkahub/context';
3
+ import { Button, Slider, Input, AccountPicker, AddressInput as AddressInput$1 } from '@polkahub/ui-components';
4
+ import { Trash2 } from 'lucide-react';
5
+ import { AccountId, getMultisigAccountId } from '@polkadot-api/substrate-bindings';
6
+ import { addrEq, defaultSerialize } from '@polkahub/plugin';
7
+ import { useState, useMemo } from 'react';
8
+ import { multisigProviderId } from './provider.js';
9
+
10
+ const AddManualMultisig = () => {
11
+ const { popContent } = useModalContext();
12
+ const multisigProvider = usePlugin(multisigProviderId);
13
+ const { polkaHub } = usePolkaHubContext();
14
+ const [name, setName] = useState("");
15
+ const [selectedAccount, setSelectedAccount] = useState(null);
16
+ const [signatories, setSignatories] = useState([]);
17
+ const [threshold, setThreshold] = useState(2);
18
+ const availableAccounts = useAvailableAccounts();
19
+ const availableSigners = useMemo(
20
+ () => Object.entries(availableAccounts).map(([name2, accounts]) => ({
21
+ name: name2,
22
+ accounts: accounts.filter((acc) => !!acc.signer).filter(
23
+ (acc) => signatories.some((addr) => addrEq(addr, acc.address))
24
+ )
25
+ })).filter(({ accounts }) => accounts.length > 0),
26
+ [availableAccounts, signatories]
27
+ );
28
+ const multisigAddress = useMemo(() => {
29
+ if (threshold > signatories.length) return null;
30
+ const [enc, dec] = AccountId();
31
+ try {
32
+ return dec(
33
+ getMultisigAccountId({
34
+ threshold,
35
+ signatories: signatories.map(enc)
36
+ })
37
+ );
38
+ } catch (ex) {
39
+ console.error(ex);
40
+ return null;
41
+ }
42
+ }, [signatories, threshold]);
43
+ return /* @__PURE__ */ jsxs(
44
+ "form",
45
+ {
46
+ className: "space-y-4",
47
+ onSubmit: async (evt) => {
48
+ evt.preventDefault();
49
+ if (!multisigAddress || !selectedAccount) return null;
50
+ const plugins = polkaHub.plugins$.getValue();
51
+ const parentProvider = plugins.find(
52
+ (p) => p.id === selectedAccount.provider
53
+ );
54
+ if (!parentProvider)
55
+ throw new Error(
56
+ `Parent provider ${selectedAccount.provider} not found`
57
+ );
58
+ const serializeFn = parentProvider.serialize ?? defaultSerialize;
59
+ multisigProvider?.addMultisig({
60
+ signatories,
61
+ threshold,
62
+ parentSigner: serializeFn(selectedAccount),
63
+ name: name.trim() ? name.trim() : void 0
64
+ });
65
+ popContent();
66
+ },
67
+ children: [
68
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
69
+ /* @__PURE__ */ jsx("h3", { className: "font-medium text-muted-foreground", children: "Add signatories" }),
70
+ /* @__PURE__ */ jsxs("ul", { children: [
71
+ signatories.map((sig) => /* @__PURE__ */ jsxs("li", { className: "flex gap-2 items-center", children: [
72
+ /* @__PURE__ */ jsx(
73
+ Button,
74
+ {
75
+ variant: "outline",
76
+ className: "text-destructive",
77
+ type: "button",
78
+ onClick: () => {
79
+ setThreshold(
80
+ (thr) => Math.max(2, Math.min(thr, signatories.length - 1))
81
+ );
82
+ setSignatories((v) => v.filter((s) => s !== sig));
83
+ },
84
+ children: /* @__PURE__ */ jsx(Trash2, {})
85
+ }
86
+ ),
87
+ /* @__PURE__ */ jsx(AddressIdentity, { addr: sig })
88
+ ] }, sig)),
89
+ /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
90
+ AddressInput,
91
+ {
92
+ onChange: (value) => {
93
+ if (signatories.includes(value)) return;
94
+ setSignatories((s) => [...s, value]);
95
+ }
96
+ }
97
+ ) })
98
+ ] }),
99
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
100
+ /* @__PURE__ */ jsxs("div", { className: "text-sm whitespace-nowrap tabular-nums", children: [
101
+ "Threshold: ",
102
+ threshold
103
+ ] }),
104
+ /* @__PURE__ */ jsx(
105
+ Slider,
106
+ {
107
+ value: [threshold],
108
+ onValueChange: ([threshold2]) => setThreshold(threshold2),
109
+ disabled: signatories.length <= 2,
110
+ min: 2,
111
+ max: Math.max(2, signatories.length)
112
+ }
113
+ )
114
+ ] })
115
+ ] }),
116
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
117
+ /* @__PURE__ */ jsx("h3", { className: "font-medium text-muted-foreground", children: "Resulting multisig" }),
118
+ multisigAddress ? /* @__PURE__ */ jsx(AddressIdentity, { addr: multisigAddress }) : /* @__PURE__ */ jsx("div", { className: "w-8 h-8 rounded-full bg-muted" }),
119
+ /* @__PURE__ */ jsx(
120
+ Input,
121
+ {
122
+ name: "account-name",
123
+ disabled: !multisigAddress,
124
+ value: name,
125
+ onChange: (evt) => setName(evt.target.value),
126
+ placeholder: "Name (optional)"
127
+ }
128
+ )
129
+ ] }),
130
+ /* @__PURE__ */ jsxs("div", { children: [
131
+ /* @__PURE__ */ jsx("h3", { className: "font-medium text-muted-foreground", children: "Select your signer" }),
132
+ /* @__PURE__ */ jsx(
133
+ AccountPicker,
134
+ {
135
+ value: selectedAccount,
136
+ onChange: setSelectedAccount,
137
+ groups: availableSigners,
138
+ className: "max-w-auto",
139
+ disableClear: true,
140
+ renderAddress: (account) => /* @__PURE__ */ jsx(
141
+ AddressIdentity,
142
+ {
143
+ addr: account.address,
144
+ name: account?.name,
145
+ copyable: false
146
+ }
147
+ )
148
+ }
149
+ )
150
+ ] }),
151
+ /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx(Button, { disabled: !multisigAddress || !selectedAccount, children: "Add Multisig" }) })
152
+ ]
153
+ }
154
+ );
155
+ };
156
+ const AddressInput = ({ ...props }) => {
157
+ const availableAccounts = useAvailableAccounts();
158
+ const hints = useMemo(() => {
159
+ const addressToAccounts = {};
160
+ Object.values(availableAccounts).flat().forEach((acc) => {
161
+ var _a;
162
+ addressToAccounts[_a = acc.address] ?? (addressToAccounts[_a] = []);
163
+ addressToAccounts[acc.address].push(acc);
164
+ });
165
+ return Object.values(addressToAccounts).map(
166
+ (group) => group.reduce(
167
+ (acc, v) => (v.name?.length ?? 0) > (acc.name?.length ?? 0) ? v : acc
168
+ )
169
+ );
170
+ }, [availableAccounts]);
171
+ return /* @__PURE__ */ jsx(
172
+ AddressInput$1,
173
+ {
174
+ hinted: Object.values(hints).flat(),
175
+ triggerClassName: "h-9",
176
+ renderAddress: (account) => typeof account === "string" ? /* @__PURE__ */ jsx(AddressIdentity, { addr: account, copyable: false }) : /* @__PURE__ */ jsx(
177
+ AddressIdentity,
178
+ {
179
+ addr: account.address,
180
+ name: account?.name,
181
+ copyable: false
182
+ }
183
+ ),
184
+ ...props
185
+ }
186
+ );
187
+ };
188
+
189
+ export { AddManualMultisig };
190
+ //# sourceMappingURL=AddManualMultisig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AddManualMultisig.js","sources":["../src/AddManualMultisig.tsx"],"sourcesContent":["import {\n AddressIdentity,\n useModalContext,\n usePlugin,\n usePolkaHubContext,\n} from \"@polkahub/context\";\nimport {\n AccountPicker,\n AddressInput as AddressInputComponent,\n Button,\n Input,\n Slider,\n} from \"@polkahub/ui-components\";\nimport { Trash2 } from \"lucide-react\";\nimport { type FC } from \"react\";\nimport {\n AccountId,\n getMultisigAccountId,\n} from \"@polkadot-api/substrate-bindings\";\nimport { useAvailableAccounts } from \"@polkahub/context\";\nimport {\n Account,\n AccountAddress,\n addrEq,\n defaultSerialize,\n} from \"@polkahub/plugin\";\nimport { useMemo, useState } from \"react\";\nimport { MultisigProvider, multisigProviderId } from \"./provider\";\n\nexport const AddManualMultisig: FC = () => {\n const { popContent } = useModalContext();\n const multisigProvider = usePlugin<MultisigProvider>(multisigProviderId);\n const { polkaHub } = usePolkaHubContext();\n const [name, setName] = useState(\"\");\n const [selectedAccount, setSelectedAccount] = useState<Account | null>(null);\n const [signatories, setSignatories] = useState<AccountAddress[]>([]);\n const [threshold, setThreshold] = useState<number>(2);\n const availableAccounts = useAvailableAccounts();\n const availableSigners = useMemo(\n () =>\n Object.entries(availableAccounts)\n .map(([name, accounts]) => ({\n name,\n accounts: accounts\n .filter((acc) => !!acc.signer)\n .filter((acc) =>\n signatories.some((addr) => addrEq(addr, acc.address))\n ),\n }))\n .filter(({ accounts }) => accounts.length > 0),\n [availableAccounts, signatories]\n );\n\n const multisigAddress = useMemo(() => {\n if (threshold > signatories.length) return null;\n const [enc, dec] = AccountId();\n\n try {\n return dec(\n getMultisigAccountId({\n threshold,\n signatories: signatories.map(enc),\n })\n );\n } catch (ex) {\n console.error(ex);\n return null;\n }\n }, [signatories, threshold]);\n\n return (\n <form\n className=\"space-y-4\"\n onSubmit={async (evt) => {\n evt.preventDefault();\n if (!multisigAddress || !selectedAccount) return null;\n\n const plugins = polkaHub.plugins$.getValue();\n const parentProvider = plugins.find(\n (p) => p.id === selectedAccount.provider\n );\n if (!parentProvider)\n throw new Error(\n `Parent provider ${selectedAccount.provider} not found`\n );\n\n const serializeFn = parentProvider.serialize ?? defaultSerialize;\n\n multisigProvider?.addMultisig({\n signatories,\n threshold,\n parentSigner: serializeFn(selectedAccount),\n name: name.trim() ? name.trim() : undefined,\n });\n\n popContent();\n }}\n >\n <div className=\"space-y-2\">\n <h3 className=\"font-medium text-muted-foreground\">Add signatories</h3>\n <ul>\n {signatories.map((sig) => (\n <li key={sig} className=\"flex gap-2 items-center\">\n <Button\n variant=\"outline\"\n className=\"text-destructive\"\n type=\"button\"\n onClick={() => {\n setThreshold((thr) =>\n Math.max(2, Math.min(thr, signatories.length - 1))\n );\n setSignatories((v) => v.filter((s) => s !== sig));\n }}\n >\n <Trash2 />\n </Button>\n <AddressIdentity addr={sig} />\n </li>\n ))}\n <li>\n <AddressInput\n onChange={(value) => {\n if (signatories.includes(value!)) return;\n setSignatories((s) => [...s, value!]);\n }}\n />\n </li>\n </ul>\n <div className=\"flex items-center gap-4\">\n <div className=\"text-sm whitespace-nowrap tabular-nums\">\n Threshold: {threshold}\n </div>\n <Slider\n value={[threshold]}\n onValueChange={([threshold]) => setThreshold(threshold)}\n disabled={signatories.length <= 2}\n min={2}\n max={Math.max(2, signatories.length)}\n />\n </div>\n </div>\n <div className=\"space-y-2\">\n <h3 className=\"font-medium text-muted-foreground\">\n Resulting multisig\n </h3>\n {multisigAddress ? (\n <AddressIdentity addr={multisigAddress} />\n ) : (\n <div className=\"w-8 h-8 rounded-full bg-muted\" />\n )}\n <Input\n name=\"account-name\"\n disabled={!multisigAddress}\n value={name}\n onChange={(evt) => setName(evt.target.value)}\n placeholder=\"Name (optional)\"\n />\n </div>\n <div>\n <h3 className=\"font-medium text-muted-foreground\">\n Select your signer\n </h3>\n {\n <AccountPicker\n value={selectedAccount}\n onChange={setSelectedAccount}\n groups={availableSigners}\n className=\"max-w-auto\"\n disableClear\n renderAddress={(account) => (\n <AddressIdentity\n addr={account.address}\n name={account?.name}\n copyable={false}\n />\n )}\n />\n }\n </div>\n <div className=\"flex justify-end\">\n <Button disabled={!multisigAddress || !selectedAccount}>\n Add Multisig\n </Button>\n </div>\n </form>\n );\n};\n\nconst AddressInput: FC<{\n value?: AccountAddress | null;\n onChange?: (value: AccountAddress | null) => void;\n}> = ({ ...props }) => {\n const availableAccounts = useAvailableAccounts();\n\n const hints = useMemo(() => {\n const addressToAccounts: Record<AccountAddress, Account[]> = {};\n Object.values(availableAccounts)\n .flat()\n .forEach((acc) => {\n addressToAccounts[acc.address] ??= [];\n addressToAccounts[acc.address].push(acc);\n });\n\n return Object.values(addressToAccounts).map((group) =>\n group.reduce((acc, v) =>\n (v.name?.length ?? 0) > (acc.name?.length ?? 0) ? v : acc\n )\n );\n }, [availableAccounts]);\n\n return (\n <AddressInputComponent\n hinted={Object.values(hints).flat()}\n triggerClassName=\"h-9\"\n renderAddress={(account: Account | string) =>\n typeof account === \"string\" ? (\n <AddressIdentity addr={account} copyable={false} />\n ) : (\n <AddressIdentity\n addr={account.address}\n name={account?.name}\n copyable={false}\n />\n )\n }\n {...props}\n />\n );\n};\n"],"names":["name","threshold","AddressInputComponent"],"mappings":"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"}
@@ -0,0 +1,98 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { ModalContext, usePlugin, useModalContext, AddressIdentity, AddressBalance } from '@polkahub/context';
3
+ import { SourceButton, Button } from '@polkahub/ui-components';
4
+ import { useStateObservable } from '@react-rxjs/core';
5
+ import { Users, Trash2, CirclePlus } from 'lucide-react';
6
+ import { useContext } from 'react';
7
+ import { AddIndexedMultisig } from './AddIndexedMultisig.js';
8
+ import { AddManualMultisig } from './AddManualMultisig.js';
9
+ import { multisigProviderId } from './provider.js';
10
+ import { useSetSelectedAccount } from '@polkahub/select-account';
11
+
12
+ const ManageMultisig = (props) => {
13
+ const { pushContent } = useContext(ModalContext);
14
+ const multisigProvider = usePlugin(multisigProviderId);
15
+ return /* @__PURE__ */ jsx(
16
+ SourceButton,
17
+ {
18
+ label: "Multisig",
19
+ onClick: () => pushContent({
20
+ title: "Multisig accounts",
21
+ element: /* @__PURE__ */ jsx(ManageAddresses, { ...props })
22
+ }),
23
+ disabled: !multisigProvider,
24
+ children: /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Users, { className: "size-10" }) })
25
+ }
26
+ );
27
+ };
28
+ const ManageAddresses = ({
29
+ getMultisigDetails
30
+ }) => {
31
+ const { pushContent } = useModalContext();
32
+ const multisigProvider = usePlugin(multisigProviderId);
33
+ const multisigAccounts = useStateObservable(multisigProvider.accounts$);
34
+ const setAccount = useSetSelectedAccount();
35
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
36
+ multisigAccounts.length ? /* @__PURE__ */ jsxs("div", { children: [
37
+ /* @__PURE__ */ jsx("h3", { className: "font-medium text-muted-foreground", children: "Added addresses" }),
38
+ /* @__PURE__ */ jsx("ul", { className: "space-y-2", children: multisigAccounts.map((account, i) => /* @__PURE__ */ jsxs("li", { className: "flex gap-2 items-center", children: [
39
+ /* @__PURE__ */ jsx(
40
+ Button,
41
+ {
42
+ variant: "outline",
43
+ className: "text-destructive",
44
+ type: "button",
45
+ onClick: () => multisigProvider.removeMultisig(account.address),
46
+ children: /* @__PURE__ */ jsx(Trash2, {})
47
+ }
48
+ ),
49
+ /* @__PURE__ */ jsx(AddressIdentity, { addr: account.address }),
50
+ /* @__PURE__ */ jsx("div", { className: "grow" }),
51
+ /* @__PURE__ */ jsx(AddressBalance, { addr: account.address }),
52
+ setAccount ? /* @__PURE__ */ jsx(
53
+ Button,
54
+ {
55
+ variant: "secondary",
56
+ onClick: () => {
57
+ setAccount(account);
58
+ },
59
+ children: "Select"
60
+ }
61
+ ) : null
62
+ ] }, i)) })
63
+ ] }) : null,
64
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2", children: [
65
+ getMultisigDetails ? /* @__PURE__ */ jsxs(
66
+ Button,
67
+ {
68
+ type: "button",
69
+ onClick: () => pushContent({
70
+ title: "Add Multisig",
71
+ element: /* @__PURE__ */ jsx(AddIndexedMultisig, { getMultisigDetails })
72
+ }),
73
+ children: [
74
+ /* @__PURE__ */ jsx(CirclePlus, {}),
75
+ "Indexed"
76
+ ]
77
+ }
78
+ ) : null,
79
+ /* @__PURE__ */ jsxs(
80
+ Button,
81
+ {
82
+ type: "button",
83
+ onClick: () => pushContent({
84
+ title: "Add Multisig",
85
+ element: /* @__PURE__ */ jsx(AddManualMultisig, {})
86
+ }),
87
+ children: [
88
+ /* @__PURE__ */ jsx(CirclePlus, {}),
89
+ getMultisigDetails ? "Manual" : "Multisig"
90
+ ]
91
+ }
92
+ )
93
+ ] })
94
+ ] });
95
+ };
96
+
97
+ export { ManageMultisig };
98
+ //# sourceMappingURL=ManageMultisig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ManageMultisig.js","sources":["../src/ManageMultisig.tsx"],"sourcesContent":["import {\n AddressBalance,\n AddressIdentity,\n ModalContext,\n useModalContext,\n usePlugin,\n} from \"@polkahub/context\";\nimport { Button, SourceButton } from \"@polkahub/ui-components\";\nimport { useStateObservable } from \"@react-rxjs/core\";\nimport { CirclePlus, Trash2, Users } from \"lucide-react\";\nimport { useContext, type FC } from \"react\";\nimport { AddIndexedMultisig, GetMultisigDetails } from \"./AddIndexedMultisig\";\nimport { AddManualMultisig } from \"./AddManualMultisig\";\nimport { MultisigProvider, multisigProviderId } from \"./provider\";\nimport { useSetSelectedAccount } from \"@polkahub/select-account\";\n\nexport const ManageMultisig: FC<{\n getMultisigDetails?: GetMultisigDetails;\n}> = (props) => {\n const { pushContent } = useContext(ModalContext)!;\n const multisigProvider = usePlugin<MultisigProvider>(multisigProviderId);\n\n return (\n <SourceButton\n label=\"Multisig\"\n onClick={() =>\n pushContent({\n title: \"Multisig accounts\",\n element: <ManageAddresses {...props} />,\n })\n }\n disabled={!multisigProvider}\n >\n <div>\n <Users className=\"size-10\" />\n </div>\n </SourceButton>\n );\n};\n\nconst ManageAddresses: FC<{ getMultisigDetails?: GetMultisigDetails }> = ({\n getMultisigDetails,\n}) => {\n const { pushContent } = useModalContext();\n const multisigProvider = usePlugin<MultisigProvider>(multisigProviderId)!;\n const multisigAccounts = useStateObservable(multisigProvider.accounts$);\n const setAccount = useSetSelectedAccount();\n\n return (\n <div className=\"space-y-4\">\n {multisigAccounts.length ? (\n <div>\n <h3 className=\"font-medium text-muted-foreground\">Added addresses</h3>\n <ul className=\"space-y-2\">\n {multisigAccounts.map((account, i) => (\n <li key={i} className=\"flex gap-2 items-center\">\n <Button\n variant=\"outline\"\n className=\"text-destructive\"\n type=\"button\"\n onClick={() =>\n multisigProvider.removeMultisig(account.address)\n }\n >\n <Trash2 />\n </Button>\n <AddressIdentity addr={account.address} />\n <div className=\"grow\" />\n <AddressBalance addr={account.address} />\n {setAccount ? (\n <Button\n variant=\"secondary\"\n onClick={() => {\n setAccount(account);\n }}\n >\n Select\n </Button>\n ) : null}\n </li>\n ))}\n </ul>\n </div>\n ) : null}\n <div className=\"flex justify-end gap-2\">\n {getMultisigDetails ? (\n <Button\n type=\"button\"\n onClick={() =>\n pushContent({\n title: \"Add Multisig\",\n element: (\n <AddIndexedMultisig getMultisigDetails={getMultisigDetails} />\n ),\n })\n }\n >\n <CirclePlus />\n Indexed\n </Button>\n ) : null}\n <Button\n type=\"button\"\n onClick={() =>\n pushContent({\n title: \"Add Multisig\",\n element: <AddManualMultisig />,\n })\n }\n >\n <CirclePlus />\n {getMultisigDetails ? \"Manual\" : \"Multisig\"}\n </Button>\n </div>\n </div>\n );\n};\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { AccountId, getMultisigAccountId, Binary } from '@polkadot-api/substrate-bindings';
3
+ import { createSignal } from '@react-rxjs/utils';
4
+ import { state, useStateObservable } from '@react-rxjs/core';
5
+ import { firstValueFrom, filter } from 'rxjs';
6
+ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogBody } from '@polkahub/ui-components';
7
+
8
+ const [urlChange$, setUrl] = createSignal();
9
+ const url$ = state(urlChange$, null);
10
+ const [enc] = AccountId();
11
+ const multisigExternalSigner = (getMultisigUrl, thresholdOneFallback) => (info, signer) => {
12
+ if (info.threshold === 1 && signer && thresholdOneFallback)
13
+ return thresholdOneFallback(info, signer);
14
+ const publicKey = getMultisigAccountId({
15
+ threshold: info.threshold,
16
+ signatories: info.signatories.map(enc)
17
+ });
18
+ return {
19
+ publicKey,
20
+ signBytes() {
21
+ throw new Error("Raw bytes can't be signed with a multisig");
22
+ },
23
+ async signTx(callData) {
24
+ const url = getMultisigUrl(info, Binary.fromBytes(callData).asHex());
25
+ setUrl(url);
26
+ try {
27
+ await firstValueFrom(url$.pipe(filter((v) => !v)));
28
+ throw null;
29
+ } catch (ex) {
30
+ throw new Error("Dismissed");
31
+ }
32
+ }
33
+ };
34
+ };
35
+ const MultisigExternalSignerModal = () => {
36
+ const activeTx = useStateObservable(url$);
37
+ return /* @__PURE__ */ jsx(Dialog, { open: !!activeTx, onOpenChange: () => setUrl(null), children: /* @__PURE__ */ jsxs(DialogContent, { children: [
38
+ /* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: "Multisig Transaction" }) }),
39
+ /* @__PURE__ */ jsx(DialogBody, { children: /* @__PURE__ */ jsxs("div", { children: [
40
+ /* @__PURE__ */ jsx("p", { children: "To sign this multisig transaction, please share the following URL with the signatories" }),
41
+ /* @__PURE__ */ jsx(
42
+ "a",
43
+ {
44
+ href: activeTx ?? "",
45
+ target: "_blank",
46
+ className: "cursor-pointer underline",
47
+ children: activeTx
48
+ }
49
+ )
50
+ ] }) })
51
+ ] }) });
52
+ };
53
+
54
+ export { MultisigExternalSignerModal, multisigExternalSigner };
55
+ //# sourceMappingURL=MultisigExternalSigner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MultisigExternalSigner.js","sources":["../src/MultisigExternalSigner.tsx"],"sourcesContent":["import {\n AccountId,\n Binary,\n getMultisigAccountId,\n HexString,\n} from \"@polkadot-api/substrate-bindings\";\nimport { CreateMultisigSigner, MultisigInfo } from \"./provider\";\nimport { createSignal } from \"@react-rxjs/utils\";\nimport { state, useStateObservable } from \"@react-rxjs/core\";\nimport { filter, firstValueFrom } from \"rxjs\";\nimport { FC } from \"react\";\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogBody,\n} from \"@polkahub/ui-components\";\n\nconst [urlChange$, setUrl] = createSignal<string | null>();\nconst url$ = state(urlChange$, null);\n\nconst [enc] = AccountId();\nexport const multisigExternalSigner =\n (\n getMultisigUrl: (info: MultisigInfo, callData: HexString) => string,\n thresholdOneFallback?: CreateMultisigSigner\n ): CreateMultisigSigner =>\n (info, signer) => {\n if (info.threshold === 1 && signer && thresholdOneFallback)\n return thresholdOneFallback(info, signer);\n\n const publicKey = getMultisigAccountId({\n threshold: info.threshold,\n signatories: info.signatories.map(enc),\n });\n\n return {\n publicKey,\n signBytes() {\n throw new Error(\"Raw bytes can't be signed with a multisig\");\n },\n async signTx(callData) {\n const url = getMultisigUrl(info, Binary.fromBytes(callData).asHex());\n setUrl(url);\n try {\n await firstValueFrom(url$.pipe(filter((v) => !v)));\n throw null;\n } catch (ex) {\n throw new Error(\"Dismissed\");\n }\n },\n };\n };\n\nexport const MultisigExternalSignerModal: FC = () => {\n const activeTx = useStateObservable(url$);\n\n return (\n <Dialog open={!!activeTx} onOpenChange={() => setUrl(null)}>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>Multisig Transaction</DialogTitle>\n </DialogHeader>\n <DialogBody>\n <div>\n <p>\n To sign this multisig transaction, please share the following URL\n with the signatories\n </p>\n <a\n href={activeTx ?? \"\"}\n target=\"_blank\"\n className=\"cursor-pointer underline\"\n >\n {activeTx}\n </a>\n </div>\n </DialogBody>\n </DialogContent>\n </Dialog>\n );\n};\n"],"names":[],"mappings":";;;;;;;AAmBA,MAAM,CAAC,UAAA,EAAY,MAAM,CAAA,GAAI,YAAA,EAA4B;AACzD,MAAM,IAAA,GAAO,KAAA,CAAM,UAAA,EAAY,IAAI,CAAA;AAEnC,MAAM,CAAC,GAAG,CAAA,GAAI,SAAA,EAAU;AACjB,MAAM,yBACX,CACE,cAAA,EACA,oBAAA,KAEF,CAAC,MAAM,MAAA,KAAW;AAChB,EAAA,IAAI,IAAA,CAAK,SAAA,KAAc,CAAA,IAAK,MAAA,IAAU,oBAAA;AACpC,IAAA,OAAO,oBAAA,CAAqB,MAAM,MAAM,CAAA;AAE1C,EAAA,MAAM,YAAY,oBAAA,CAAqB;AAAA,IACrC,WAAW,IAAA,CAAK,SAAA;AAAA,IAChB,WAAA,EAAa,IAAA,CAAK,WAAA,CAAY,GAAA,CAAI,GAAG;AAAA,GACtC,CAAA;AAED,EAAA,OAAO;AAAA,IACL,SAAA;AAAA,IACA,SAAA,GAAY;AACV,MAAA,MAAM,IAAI,MAAM,2CAA2C,CAAA;AAAA,IAC7D,CAAA;AAAA,IACA,MAAM,OAAO,QAAA,EAAU;AACrB,MAAA,MAAM,GAAA,GAAM,eAAe,IAAA,EAAM,MAAA,CAAO,UAAU,QAAQ,CAAA,CAAE,OAAO,CAAA;AACnE,MAAA,MAAA,CAAO,GAAG,CAAA;AACV,MAAA,IAAI;AACF,QAAA,MAAM,cAAA,CAAe,KAAK,IAAA,CAAK,MAAA,CAAO,CAAC,CAAA,KAAM,CAAC,CAAC,CAAC,CAAC,CAAA;AACjD,QAAA,MAAM,IAAA;AAAA,MACR,SAAS,EAAA,EAAI;AACX,QAAA,MAAM,IAAI,MAAM,WAAW,CAAA;AAAA,MAC7B;AAAA,IACF;AAAA,GACF;AACF;AAEK,MAAM,8BAAkC,MAAM;AACnD,EAAA,MAAM,QAAA,GAAW,mBAAmB,IAAI,CAAA;AAExC,EAAA,uBACE,GAAA,CAAC,MAAA,EAAA,EAAO,IAAA,EAAM,CAAC,CAAC,QAAA,EAAU,YAAA,EAAc,MAAM,MAAA,CAAO,IAAI,CAAA,EACvD,QAAA,kBAAA,IAAA,CAAC,aAAA,EAAA,EACC,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,YAAA,EAAA,EACC,QAAA,kBAAA,GAAA,CAAC,WAAA,EAAA,EAAY,QAAA,EAAA,sBAAA,EAAoB,CAAA,EACnC,CAAA;AAAA,oBACA,GAAA,CAAC,UAAA,EAAA,EACC,QAAA,kBAAA,IAAA,CAAC,KAAA,EAAA,EACC,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,OAAE,QAAA,EAAA,wFAAA,EAGH,CAAA;AAAA,sBACA,GAAA;AAAA,QAAC,GAAA;AAAA,QAAA;AAAA,UACC,MAAM,QAAA,IAAY,EAAA;AAAA,UAClB,MAAA,EAAO,QAAA;AAAA,UACP,SAAA,EAAU,0BAAA;AAAA,UAET,QAAA,EAAA;AAAA;AAAA;AACH,KAAA,EACF,CAAA,EACF;AAAA,GAAA,EACF,CAAA,EACF,CAAA;AAEJ;;;;"}
package/dist/index.js CHANGED
@@ -1,2 +1,4 @@
1
- export { createMultisigProvider } from './provider.js';
1
+ export { createMultisigProvider, multisigDirectSigner, multisigProviderId } from './provider.js';
2
+ export { ManageMultisig } from './ManageMultisig.js';
3
+ export { MultisigExternalSignerModal, multisigExternalSigner } from './MultisigExternalSigner.js';
2
4
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
package/dist/provider.js CHANGED
@@ -2,9 +2,10 @@ import { getMultisigSigner } from '@polkadot-api/meta-signers';
2
2
  import { AccountId, getMultisigAccountId, getSs58AddressInfo } from '@polkadot-api/substrate-bindings';
3
3
  import { localStorageProvider, persistedState, addrEq } from '@polkahub/plugin';
4
4
  import { state } from '@react-rxjs/core';
5
- import { BehaviorSubject, combineLatest, switchMap } from 'rxjs';
5
+ import { BehaviorSubject, combineLatest, switchMap, firstValueFrom, map, filter, timeout } from 'rxjs';
6
6
 
7
- const createMultisigProvider = (getMultisigInfo, txPaymentInfo, opts) => {
7
+ const multisigProviderId = "multisig";
8
+ const createMultisigProvider = (createMultisigSigner, opts) => {
8
9
  const { persist } = {
9
10
  persist: localStorageProvider("multisigs"),
10
11
  ...opts
@@ -15,26 +16,33 @@ const createMultisigProvider = (getMultisigInfo, txPaymentInfo, opts) => {
15
16
  );
16
17
  const plugins$ = new BehaviorSubject([]);
17
18
  const getAccount = (info, parentSigner) => ({
18
- provider: "multisig",
19
+ provider: multisigProviderId,
19
20
  address: getMultisigAddress(info),
20
- signer: parentSigner ? getMultisigSigner(
21
- info,
22
- getMultisigInfo,
23
- txPaymentInfo,
24
- parentSigner,
25
- opts?.method ? {
26
- method: opts.method
27
- } : void 0
28
- ) : void 0,
29
- info
21
+ signer: createMultisigSigner(info, parentSigner) ?? void 0,
22
+ info,
23
+ name: info.name
30
24
  });
31
25
  const multisigInfoToAccount = async (info) => {
32
26
  if (!info.parentSigner) return getAccount(info, void 0);
33
- const plugins = plugins$.getValue();
34
- const plugin = plugins.find((p) => info.parentSigner?.provider === p.id);
35
- if (!plugin) return getAccount(info, void 0);
36
- const parentSigner = await plugin.deserialize(info.parentSigner);
37
- return getAccount(info, parentSigner?.signer);
27
+ try {
28
+ const plugin = await firstValueFrom(
29
+ plugins$.pipe(
30
+ map(
31
+ (plugins) => plugins.find((p) => info.parentSigner?.provider === p.id)
32
+ ),
33
+ filter((v) => v != null),
34
+ timeout({
35
+ first: 3e3
36
+ })
37
+ )
38
+ );
39
+ if (!plugin) return getAccount(info, void 0);
40
+ const parentSigner = await plugin.deserialize(info.parentSigner);
41
+ return getAccount(info, parentSigner?.signer);
42
+ } catch (ex) {
43
+ console.error(ex);
44
+ return getAccount(info, void 0);
45
+ }
38
46
  };
39
47
  const accounts$ = state(
40
48
  combineLatest([persistedAccounts$, plugins$]).pipe(
@@ -45,7 +53,7 @@ const createMultisigProvider = (getMultisigInfo, txPaymentInfo, opts) => {
45
53
  []
46
54
  );
47
55
  return {
48
- id: "multisig",
56
+ id: multisigProviderId,
49
57
  deserialize: (account) => {
50
58
  const extra = account.extra;
51
59
  return multisigInfoToAccount(extra);
@@ -85,6 +93,15 @@ const getMultisigAddress = (info) => {
85
93
  }
86
94
  return AccountId(addrInfo.ss58Format).dec(accountId);
87
95
  };
96
+ const multisigDirectSigner = (getMultisigInfo, txPaymentInfo, opts) => (info, parentSigner) => parentSigner ? getMultisigSigner(
97
+ info,
98
+ getMultisigInfo,
99
+ txPaymentInfo,
100
+ parentSigner,
101
+ opts ?? {
102
+ method: () => "as_multi"
103
+ }
104
+ ) : null;
88
105
 
89
- export { createMultisigProvider };
106
+ export { createMultisigProvider, multisigDirectSigner, multisigProviderId };
90
107
  //# sourceMappingURL=provider.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"provider.js","sources":["../src/provider.ts"],"sourcesContent":["import {\n getMultisigSigner,\n MultisigSignerOptions,\n} from \"@polkadot-api/meta-signers\";\nimport {\n AccountId,\n FixedSizeBinary,\n getMultisigAccountId,\n getSs58AddressInfo,\n} from \"@polkadot-api/substrate-bindings\";\nimport {\n Account,\n AccountAddress,\n addrEq,\n localStorageProvider,\n persistedState,\n PersistenceProvider,\n Plugin,\n SerializableAccount,\n} from \"@polkahub/plugin\";\nimport { DefaultedStateObservable, state } from \"@react-rxjs/core\";\nimport { Binary, PolkadotSigner } from \"polkadot-api\";\nimport { BehaviorSubject, combineLatest, switchMap } from \"rxjs\";\n\nexport interface MultisigInfo {\n threshold: number;\n signatories: AccountAddress[];\n // If not set, it will be a read-only account.\n // But with the advantage that it will still figure out the resulting address\n parentSigner?: SerializableAccount;\n}\n\nexport interface MultisigAccount extends Account {\n provider: \"multisig\";\n info: MultisigInfo;\n}\n\nexport interface MultisigProvider extends Plugin<MultisigAccount> {\n id: \"multisig\";\n accounts$: DefaultedStateObservable<MultisigAccount[]>;\n\n setMultisigs: (multisigs: MultisigInfo[]) => void;\n addMultisig: (multisig: MultisigInfo) => Promise<MultisigAccount>;\n removeMultisig: (addr: AccountAddress) => void;\n}\n\nexport const createMultisigProvider = (\n getMultisigInfo: (\n multisig: AccountAddress,\n callHash: FixedSizeBinary<32>\n ) => Promise<\n | {\n when: {\n height: number;\n index: number;\n };\n approvals: Array<AccountAddress>;\n }\n | undefined\n >,\n txPaymentInfo: (\n uxt: Binary,\n len: number\n ) => Promise<{\n weight: {\n ref_time: bigint;\n proof_size: bigint;\n };\n }>,\n opts?: Partial<\n {\n persist: PersistenceProvider;\n } & MultisigSignerOptions<AccountAddress>\n >\n): MultisigProvider => {\n const { persist } = {\n persist: localStorageProvider(\"multisigs\"),\n ...opts,\n };\n\n const [persistedAccounts$, setPersistedAccounts] = persistedState(\n persist,\n [] as MultisigInfo[]\n );\n const plugins$ = new BehaviorSubject<Plugin<Account>[]>([]);\n\n const getAccount = (\n info: MultisigInfo,\n parentSigner: PolkadotSigner | undefined\n ): MultisigAccount => ({\n provider: \"multisig\",\n address: getMultisigAddress(info),\n signer: parentSigner\n ? getMultisigSigner(\n info,\n getMultisigInfo,\n txPaymentInfo,\n parentSigner,\n opts?.method\n ? {\n method: opts.method,\n }\n : undefined\n )\n : undefined,\n info,\n });\n\n const multisigInfoToAccount = async (info: MultisigInfo) => {\n if (!info.parentSigner) return getAccount(info, undefined);\n\n const plugins = plugins$.getValue();\n const plugin = plugins.find((p) => info.parentSigner?.provider === p.id);\n if (!plugin) return getAccount(info, undefined);\n const parentSigner = await plugin.deserialize(info.parentSigner);\n return getAccount(info, parentSigner?.signer);\n };\n\n const accounts$ = state(\n combineLatest([persistedAccounts$, plugins$]).pipe(\n switchMap(\n async ([accounts]) =>\n await Promise.all(accounts.map(multisigInfoToAccount))\n )\n ),\n []\n );\n\n return {\n id: \"multisig\",\n deserialize: (account) => {\n const extra = account.extra as MultisigInfo;\n return multisigInfoToAccount(extra);\n },\n serialize: ({ address, info, provider }) => ({\n address,\n provider,\n extra: info,\n }),\n eq: (a, b) =>\n addrEq(a.address, b.address) &&\n addrEq(a.info.parentSigner?.address, b.info.parentSigner?.address),\n accounts$,\n receiveContext: (ctx) => plugins$.next(ctx.plugins),\n subscription$: accounts$,\n setMultisigs: setPersistedAccounts,\n addMultisig: (multisig) => {\n const addr = getMultisigAddress(multisig);\n setPersistedAccounts((prev) => {\n if (prev.some((v) => getMultisigAddress(v) === addr)) return prev;\n return [...prev, multisig];\n });\n return multisigInfoToAccount(multisig);\n },\n removeMultisig: (addr) =>\n setPersistedAccounts((prev) =>\n prev.filter((v) => getMultisigAddress(v) !== addr)\n ),\n };\n};\n\nconst getPublicKey = AccountId().enc;\nconst getMultisigAddress = (info: MultisigInfo) => {\n const accountId = getMultisigAccountId({\n threshold: info.threshold,\n signatories: info.signatories.map(getPublicKey),\n });\n const addrInfo = getSs58AddressInfo(info.signatories[0]);\n if (!addrInfo.isValid) {\n throw new Error(\"Unreachable\");\n }\n return AccountId(addrInfo.ss58Format).dec(accountId);\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"provider.js","sources":["../src/provider.ts"],"sourcesContent":["import {\n getMultisigSigner,\n MultisigSignerOptions,\n} from \"@polkadot-api/meta-signers\";\nimport {\n AccountId,\n FixedSizeBinary,\n getMultisigAccountId,\n getSs58AddressInfo,\n} from \"@polkadot-api/substrate-bindings\";\nimport {\n Account,\n AccountAddress,\n addrEq,\n localStorageProvider,\n persistedState,\n PersistenceProvider,\n Plugin,\n SerializableAccount,\n} from \"@polkahub/plugin\";\nimport { DefaultedStateObservable, state } from \"@react-rxjs/core\";\nimport { Binary, PolkadotSigner } from \"polkadot-api\";\nimport {\n BehaviorSubject,\n combineLatest,\n filter,\n firstValueFrom,\n map,\n switchMap,\n timeout,\n} from \"rxjs\";\n\nexport interface MultisigInfo {\n threshold: number;\n signatories: AccountAddress[];\n // If not set, it will be a read-only account.\n // But with the advantage that it will still figure out the resulting address\n parentSigner?: SerializableAccount;\n name?: string;\n}\n\nexport type CreateMultisigSigner = (\n info: MultisigInfo,\n parentSigner?: PolkadotSigner\n) => PolkadotSigner | null;\n\nexport const multisigProviderId = \"multisig\";\nexport interface MultisigAccount extends Account {\n provider: \"multisig\";\n info: MultisigInfo;\n}\n\nexport interface MultisigProvider extends Plugin<MultisigAccount> {\n id: \"multisig\";\n accounts$: DefaultedStateObservable<MultisigAccount[]>;\n\n setMultisigs: (multisigs: MultisigInfo[]) => void;\n addMultisig: (multisig: MultisigInfo) => Promise<MultisigAccount>;\n removeMultisig: (addr: AccountAddress) => void;\n}\n\nexport const createMultisigProvider = (\n createMultisigSigner: CreateMultisigSigner,\n opts?: Partial<{\n persist: PersistenceProvider;\n }>\n): MultisigProvider => {\n const { persist } = {\n persist: localStorageProvider(\"multisigs\"),\n ...opts,\n };\n\n const [persistedAccounts$, setPersistedAccounts] = persistedState(\n persist,\n [] as MultisigInfo[]\n );\n const plugins$ = new BehaviorSubject<Plugin<Account>[]>([]);\n\n const getAccount = (\n info: MultisigInfo,\n parentSigner: PolkadotSigner | undefined\n ): MultisigAccount => ({\n provider: multisigProviderId,\n address: getMultisigAddress(info),\n signer: createMultisigSigner(info, parentSigner) ?? undefined,\n info,\n name: info.name,\n });\n\n const multisigInfoToAccount = async (info: MultisigInfo) => {\n if (!info.parentSigner) return getAccount(info, undefined);\n\n try {\n const plugin = await firstValueFrom(\n plugins$.pipe(\n map((plugins) =>\n plugins.find((p) => info.parentSigner?.provider === p.id)\n ),\n filter((v) => v != null),\n timeout({\n first: 3000,\n })\n )\n );\n if (!plugin) return getAccount(info, undefined);\n\n const parentSigner = await plugin.deserialize(info.parentSigner);\n return getAccount(info, parentSigner?.signer);\n } catch (ex) {\n console.error(ex);\n return getAccount(info, undefined);\n }\n };\n\n const accounts$ = state(\n combineLatest([persistedAccounts$, plugins$]).pipe(\n switchMap(\n async ([accounts]) =>\n await Promise.all(accounts.map(multisigInfoToAccount))\n )\n ),\n []\n );\n\n return {\n id: multisigProviderId,\n deserialize: (account) => {\n const extra = account.extra as MultisigInfo;\n return multisigInfoToAccount(extra);\n },\n serialize: ({ address, info, provider }) => ({\n address,\n provider,\n extra: info,\n }),\n eq: (a, b) =>\n addrEq(a.address, b.address) &&\n addrEq(a.info.parentSigner?.address, b.info.parentSigner?.address),\n accounts$,\n receiveContext: (ctx) => plugins$.next(ctx.plugins),\n subscription$: accounts$,\n setMultisigs: setPersistedAccounts,\n addMultisig: (multisig) => {\n const addr = getMultisigAddress(multisig);\n setPersistedAccounts((prev) => {\n if (prev.some((v) => getMultisigAddress(v) === addr)) return prev;\n return [...prev, multisig];\n });\n return multisigInfoToAccount(multisig);\n },\n removeMultisig: (addr) =>\n setPersistedAccounts((prev) =>\n prev.filter((v) => getMultisigAddress(v) !== addr)\n ),\n };\n};\n\nconst getPublicKey = AccountId().enc;\nconst getMultisigAddress = (info: MultisigInfo) => {\n const accountId = getMultisigAccountId({\n threshold: info.threshold,\n signatories: info.signatories.map(getPublicKey),\n });\n const addrInfo = getSs58AddressInfo(info.signatories[0]);\n if (!addrInfo.isValid) {\n throw new Error(\"Unreachable\");\n }\n return AccountId(addrInfo.ss58Format).dec(accountId);\n};\n\nexport const multisigDirectSigner =\n (\n getMultisigInfo: (\n multisig: AccountAddress,\n callHash: FixedSizeBinary<32>\n ) => Promise<\n | {\n when: {\n height: number;\n index: number;\n };\n approvals: Array<AccountAddress>;\n }\n | undefined\n >,\n txPaymentInfo: (\n uxt: Binary,\n len: number\n ) => Promise<{\n weight: {\n ref_time: bigint;\n proof_size: bigint;\n };\n }>,\n opts?: MultisigSignerOptions<AccountAddress>\n ): CreateMultisigSigner =>\n (info, parentSigner) =>\n parentSigner\n ? getMultisigSigner(\n info,\n getMultisigInfo,\n txPaymentInfo,\n parentSigner,\n opts ?? {\n method: () => \"as_multi\",\n }\n )\n : null;\n"],"names":[],"mappings":"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"}
@@ -1,14 +1,18 @@
1
1
  import { MultisigSignerOptions } from '@polkadot-api/meta-signers';
2
- import { FixedSizeBinary } from '@polkadot-api/substrate-bindings';
2
+ import { FixedSizeBinary, HexString } from '@polkadot-api/substrate-bindings';
3
3
  import { AccountAddress, SerializableAccount, Account, Plugin, PersistenceProvider } from '@polkahub/plugin';
4
4
  import { DefaultedStateObservable } from '@react-rxjs/core';
5
- import { Binary } from 'polkadot-api';
5
+ import { PolkadotSigner, Binary } from 'polkadot-api';
6
+ import { FC } from 'react';
6
7
 
7
8
  interface MultisigInfo {
8
9
  threshold: number;
9
10
  signatories: AccountAddress[];
10
11
  parentSigner?: SerializableAccount;
12
+ name?: string;
11
13
  }
14
+ type CreateMultisigSigner = (info: MultisigInfo, parentSigner?: PolkadotSigner) => PolkadotSigner | null;
15
+ declare const multisigProviderId = "multisig";
12
16
  interface MultisigAccount extends Account {
13
17
  provider: "multisig";
14
18
  info: MultisigInfo;
@@ -20,7 +24,10 @@ interface MultisigProvider extends Plugin<MultisigAccount> {
20
24
  addMultisig: (multisig: MultisigInfo) => Promise<MultisigAccount>;
21
25
  removeMultisig: (addr: AccountAddress) => void;
22
26
  }
23
- declare const createMultisigProvider: (getMultisigInfo: (multisig: AccountAddress, callHash: FixedSizeBinary<32>) => Promise<{
27
+ declare const createMultisigProvider: (createMultisigSigner: CreateMultisigSigner, opts?: Partial<{
28
+ persist: PersistenceProvider;
29
+ }>) => MultisigProvider;
30
+ declare const multisigDirectSigner: (getMultisigInfo: (multisig: AccountAddress, callHash: FixedSizeBinary<32>) => Promise<{
24
31
  when: {
25
32
  height: number;
26
33
  index: number;
@@ -31,9 +38,19 @@ declare const createMultisigProvider: (getMultisigInfo: (multisig: AccountAddres
31
38
  ref_time: bigint;
32
39
  proof_size: bigint;
33
40
  };
34
- }>, opts?: Partial<{
35
- persist: PersistenceProvider;
36
- } & MultisigSignerOptions<AccountAddress>>) => MultisigProvider;
41
+ }>, opts?: MultisigSignerOptions<AccountAddress>) => CreateMultisigSigner;
42
+
43
+ type GetMultisigDetails = (address: AccountAddress) => Promise<{
44
+ addresses: AccountAddress[];
45
+ threshold: number;
46
+ } | null>;
47
+
48
+ declare const ManageMultisig: FC<{
49
+ getMultisigDetails?: GetMultisigDetails;
50
+ }>;
51
+
52
+ declare const multisigExternalSigner: (getMultisigUrl: (info: MultisigInfo, callData: HexString) => string, thresholdOneFallback?: CreateMultisigSigner) => CreateMultisigSigner;
53
+ declare const MultisigExternalSignerModal: FC;
37
54
 
38
- export { createMultisigProvider };
39
- export type { MultisigAccount, MultisigInfo, MultisigProvider };
55
+ export { ManageMultisig, MultisigExternalSignerModal, createMultisigProvider, multisigDirectSigner, multisigExternalSigner, multisigProviderId };
56
+ export type { CreateMultisigSigner, MultisigAccount, MultisigInfo, MultisigProvider };
package/index.css ADDED
@@ -0,0 +1,2 @@
1
+ @import "@polkahub/ui-components";
2
+ @source "./dist";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polkahub/multisig",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "license": "MIT",
5
5
  "sideEffects": false,
6
6
  "type": "module",
@@ -9,6 +9,7 @@
9
9
  "types": "./dist/src/index.d.ts",
10
10
  "module": "./dist/index.js",
11
11
  "import": "./dist/index.js",
12
+ "style": "./index.css",
12
13
  "default": "./dist/index.js"
13
14
  },
14
15
  "./package.json": "./package.json"
@@ -17,17 +18,24 @@
17
18
  "main": "./dist/index.js",
18
19
  "module": "./dist/index.js",
19
20
  "browser": "./dist/index.js",
21
+ "style": "./index.css",
20
22
  "files": [
21
- "dist"
23
+ "dist",
24
+ "index.css"
22
25
  ],
23
26
  "dependencies": {
24
27
  "@polkadot-api/meta-signers": "^0.1.17",
25
- "@polkadot-api/substrate-bindings": "^0.16.4",
28
+ "@polkadot-api/substrate-bindings": "^0.16.5",
26
29
  "@react-rxjs/core": "^0.10.8",
27
30
  "@react-rxjs/utils": "^0.9.7",
31
+ "lucide-react": "^0.552.0",
28
32
  "polkadot-api": "^1.20.1",
29
33
  "rxjs": "^7.8.2",
30
- "@polkahub/plugin": "0.2.0"
34
+ "@polkahub/context": "0.3.0",
35
+ "@polkahub/plugin": "0.3.0",
36
+ "@polkahub/select-account": "0.3.0",
37
+ "@polkahub/proxy": "0.3.0",
38
+ "@polkahub/ui-components": "0.3.0"
31
39
  },
32
40
  "peerDependencies": {
33
41
  "@types/react": "^19.2.2",