@polkahub/multisig 0.2.2 → 0.3.1
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/dist/AddIndexedMultisig.js +230 -0
- package/dist/AddIndexedMultisig.js.map +1 -0
- package/dist/AddManualMultisig.js +190 -0
- package/dist/AddManualMultisig.js.map +1 -0
- package/dist/ManageMultisig.js +98 -0
- package/dist/ManageMultisig.js.map +1 -0
- package/dist/MultisigExternalSigner.js +55 -0
- package/dist/MultisigExternalSigner.js.map +1 -0
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/provider.js +37 -20
- package/dist/provider.js.map +1 -1
- package/dist/src/index.d.ts +25 -8
- package/index.css +2 -0
- package/package.json +13 -5
|
@@ -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
|
|
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:
|
|
19
|
+
provider: multisigProviderId,
|
|
19
20
|
address: getMultisigAddress(info),
|
|
20
|
-
signer: parentSigner
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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:
|
|
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
|
package/dist/provider.js.map
CHANGED
|
@@ -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 {
|
|
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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"}
|
package/dist/src/index.d.ts
CHANGED
|
@@ -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: (
|
|
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?:
|
|
35
|
-
|
|
36
|
-
|
|
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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polkahub/multisig",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
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.
|
|
28
|
+
"@polkadot-api/substrate-bindings": "^0.16.5",
|
|
26
29
|
"@react-rxjs/core": "^0.10.8",
|
|
27
30
|
"@react-rxjs/utils": "^0.9.7",
|
|
28
|
-
"
|
|
31
|
+
"lucide-react": "^0.553.0",
|
|
32
|
+
"polkadot-api": "^1.20.2",
|
|
29
33
|
"rxjs": "^7.8.2",
|
|
30
|
-
"@polkahub/
|
|
34
|
+
"@polkahub/context": "0.3.1",
|
|
35
|
+
"@polkahub/proxy": "0.3.1",
|
|
36
|
+
"@polkahub/select-account": "0.3.1",
|
|
37
|
+
"@polkahub/plugin": "0.3.1",
|
|
38
|
+
"@polkahub/ui-components": "0.3.1"
|
|
31
39
|
},
|
|
32
40
|
"peerDependencies": {
|
|
33
41
|
"@types/react": "^19.2.2",
|