cruzo-web3 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +242 -0
- package/lib/components/icons/copy-icon.component.ts +14 -0
- package/lib/components/web3-signer/web3-signer.component.module.css +106 -0
- package/lib/components/web3-signer/web3-signer.component.ts +320 -0
- package/lib/components/web3-signing/web3-signing.component.module.css +36 -0
- package/lib/components/web3-signing/web3-signing.component.ts +239 -0
- package/lib/components/web3-wallet-picker/web3-wallet-picker.bucket.ts +22 -0
- package/lib/components/web3-wallet-picker/web3-wallet-picker.component.module.css +62 -0
- package/lib/components/web3-wallet-picker/web3-wallet-picker.component.ts +171 -0
- package/lib/crypto/account-signature.ts +175 -0
- package/lib/crypto/decode-bytes.ts +93 -0
- package/lib/crypto/ecdsa-signature.ts +45 -0
- package/lib/crypto/keccak256.ts +117 -0
- package/lib/crypto/secp256k1-verify.ts +232 -0
- package/lib/crypto/sha256.ts +8 -0
- package/lib/crypto/verify-signature.ts +54 -0
- package/lib/env.d.ts +4 -0
- package/lib/errors/web3-error.ts +49 -0
- package/lib/index.ts +20 -0
- package/lib/providers/eip1193.provider.ts +152 -0
- package/lib/providers/injected.ts +71 -0
- package/lib/providers/message-bytes.ts +13 -0
- package/lib/providers/solana.provider.ts +116 -0
- package/lib/providers/tonconnect.provider.ts +161 -0
- package/lib/providers/tron.provider.ts +142 -0
- package/lib/providers/wallet-transport.ts +1 -0
- package/lib/providers/wallet.ts +92 -0
- package/lib/providers/walletconnect-ethereum.provider.ts +49 -0
- package/lib/pub-key.ts +144 -0
- package/lib/signing-url.ts +334 -0
- package/lib/types/signer-state.ts +10 -0
- package/lib/types/web3-types.ts +27 -0
- package/lib/utils/format-pub-key.ts +18 -0
- package/lib/web3-wallet.ts +31 -0
- package/lib/web3.service.ts +419 -0
- package/package.json +58 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
.signing {
|
|
2
|
+
display: grid;
|
|
3
|
+
gap: var(--m);
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.payloadLimit {
|
|
7
|
+
color: var(--text-muted-mid);
|
|
8
|
+
font-weight: 400;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.stateOverview {
|
|
12
|
+
margin: 0;
|
|
13
|
+
color: var(--text-muted-mid);
|
|
14
|
+
font-family: var(--mono);
|
|
15
|
+
font-size: 13px;
|
|
16
|
+
line-height: 1.5;
|
|
17
|
+
white-space: pre-wrap;
|
|
18
|
+
word-break: break-word;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.signers {
|
|
22
|
+
display: grid;
|
|
23
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
24
|
+
gap: var(--m);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@media (max-width: 860px) {
|
|
28
|
+
.signers {
|
|
29
|
+
grid-template-columns: 1fr;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.hint {
|
|
34
|
+
margin: 0;
|
|
35
|
+
color: var(--text-muted-mid);
|
|
36
|
+
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import styles from "./web3-signing.component.module.css";
|
|
2
|
+
|
|
3
|
+
import { AbstractComponent, componentsRegistryService, routerService, RxBucket } from "cruzo";
|
|
4
|
+
import { UI_KIT } from "cruzo/ui-components/const";
|
|
5
|
+
|
|
6
|
+
import "../web3-signer/web3-signer.component";
|
|
7
|
+
import { detectInjectedWallets, getInjectedWalletLabel } from "../../providers/injected";
|
|
8
|
+
import {
|
|
9
|
+
buildSearchWithSigning,
|
|
10
|
+
clampSigningPayload,
|
|
11
|
+
MAX_SIGNING_PAYLOAD_LENGTH,
|
|
12
|
+
readPayloadFromUrl,
|
|
13
|
+
readSigningUrl,
|
|
14
|
+
} from "../../signing-url";
|
|
15
|
+
import type { SignerState } from "../../types/signer-state";
|
|
16
|
+
import { pubKeyToText } from "../../utils/format-pub-key";
|
|
17
|
+
import { web3Service } from "../../web3.service";
|
|
18
|
+
|
|
19
|
+
const DEFAULT_PAYLOAD = JSON.stringify({ message: "Hello from cruzo-web3" });
|
|
20
|
+
const SIGNER_IDS = ["signer1", "signer2"] as const;
|
|
21
|
+
const PAYLOAD_INPUT_ID = "payload";
|
|
22
|
+
|
|
23
|
+
export class Web3SigningComponent extends AbstractComponent {
|
|
24
|
+
static selector = "web3-signing-component";
|
|
25
|
+
|
|
26
|
+
dependencies = new Set(["web3-signer-component"]);
|
|
27
|
+
|
|
28
|
+
walletHint$ = this.newRx("");
|
|
29
|
+
stateOverview$ = this.newRx("");
|
|
30
|
+
|
|
31
|
+
innerBucket = new RxBucket({
|
|
32
|
+
[PAYLOAD_INPUT_ID]: {},
|
|
33
|
+
signer1: {
|
|
34
|
+
config: { payload: DEFAULT_PAYLOAD, title: "Signer 1" },
|
|
35
|
+
},
|
|
36
|
+
signer2: {
|
|
37
|
+
config: { payload: DEFAULT_PAYLOAD, title: "Signer 2" },
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
payload$ = this.newRxValueFromBucket(this.innerBucket, PAYLOAD_INPUT_ID);
|
|
42
|
+
|
|
43
|
+
private syncingFromUrl = false;
|
|
44
|
+
|
|
45
|
+
getHTML() {
|
|
46
|
+
const k = UI_KIT;
|
|
47
|
+
|
|
48
|
+
return `<div>
|
|
49
|
+
<p class="description-note mb_m ${styles.hint}"
|
|
50
|
+
attached="{{ root.walletHint$::rx }}">{{ root.walletHint$::rx }}</p>
|
|
51
|
+
|
|
52
|
+
<div class="${styles.signing} block">
|
|
53
|
+
<div class="mb_s">
|
|
54
|
+
<div class="description-paragraph mb_xs">
|
|
55
|
+
Payload (signed as JSON string)
|
|
56
|
+
<span class="${styles.payloadLimit}">— max ${MAX_SIGNING_PAYLOAD_LENGTH} chars, synced to URL</span>
|
|
57
|
+
</div>
|
|
58
|
+
<textarea
|
|
59
|
+
class="${k}_textarea"
|
|
60
|
+
maxlength="${MAX_SIGNING_PAYLOAD_LENGTH}"
|
|
61
|
+
rows="4"
|
|
62
|
+
placeholder='{"message": "..."}'
|
|
63
|
+
oninput="{{ root.onPayloadInput(event.currentTarget) }}">{{ root.payload$::rx }}</textarea>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<div class="mb_m">
|
|
67
|
+
<div class="description-paragraph mb_xs">Signing state (synced to URL):</div>
|
|
68
|
+
<pre class="${styles.stateOverview}">{{ root.stateOverview$::rx }}</pre>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<div class="${styles.signers}">
|
|
72
|
+
<web3-signer-component
|
|
73
|
+
component-id="signer1"
|
|
74
|
+
bucket-id="${this.innerBucket.id}">
|
|
75
|
+
</web3-signer-component>
|
|
76
|
+
<web3-signer-component
|
|
77
|
+
component-id="signer2"
|
|
78
|
+
bucket-id="${this.innerBucket.id}">
|
|
79
|
+
</web3-signer-component>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
</div>`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
connectedCallback() {
|
|
86
|
+
componentsRegistryService.connectBucket(this.innerBucket);
|
|
87
|
+
super.connectedCallback();
|
|
88
|
+
this.ensureTonManifest();
|
|
89
|
+
this.updateWalletHint();
|
|
90
|
+
this.setupUrlSync();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
onPayloadInput(el: HTMLTextAreaElement) {
|
|
94
|
+
const value = clampSigningPayload(el?.value ?? "");
|
|
95
|
+
|
|
96
|
+
if ((this.innerBucket.getValue(PAYLOAD_INPUT_ID) ?? "") !== value) {
|
|
97
|
+
this.innerBucket.setValue(PAYLOAD_INPUT_ID, value, "0", true);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private setupUrlSync() {
|
|
102
|
+
this.rxList ??= [];
|
|
103
|
+
|
|
104
|
+
this.applyPayload(DEFAULT_PAYLOAD);
|
|
105
|
+
this.applyUrlState(routerService.search$.actual);
|
|
106
|
+
|
|
107
|
+
this.newRxFunc((search: string) => {
|
|
108
|
+
this.applyUrlState(search);
|
|
109
|
+
}, routerService.search$);
|
|
110
|
+
|
|
111
|
+
this.innerBucket.newRxValue(
|
|
112
|
+
PAYLOAD_INPUT_ID,
|
|
113
|
+
(value) => {
|
|
114
|
+
if (this.syncingFromUrl) return;
|
|
115
|
+
|
|
116
|
+
const payload = clampSigningPayload(value ?? "");
|
|
117
|
+
this.updateSignerPayloadConfigs(payload);
|
|
118
|
+
this.syncToUrl();
|
|
119
|
+
},
|
|
120
|
+
this.rxList,
|
|
121
|
+
DEFAULT_PAYLOAD,
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
for (const id of SIGNER_IDS) {
|
|
125
|
+
this.innerBucket.newRxState(
|
|
126
|
+
id,
|
|
127
|
+
() => {
|
|
128
|
+
this.syncToUrl();
|
|
129
|
+
this.updateStateOverview();
|
|
130
|
+
},
|
|
131
|
+
this.rxList,
|
|
132
|
+
this.innerBucket.getState(id),
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
this.updateStateOverview();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private applyUrlState(search: string) {
|
|
140
|
+
const snapshot = readSigningUrl(search);
|
|
141
|
+
const payloadFromUrl = readPayloadFromUrl(search);
|
|
142
|
+
|
|
143
|
+
if (!snapshot && payloadFromUrl == null) return;
|
|
144
|
+
|
|
145
|
+
this.syncingFromUrl = true;
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
this.applyPayload(payloadFromUrl ?? DEFAULT_PAYLOAD);
|
|
149
|
+
|
|
150
|
+
if (snapshot) {
|
|
151
|
+
for (const id of SIGNER_IDS) {
|
|
152
|
+
const state = snapshot[id];
|
|
153
|
+
|
|
154
|
+
if (state) {
|
|
155
|
+
this.innerBucket.setState(id, state);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
} finally {
|
|
160
|
+
this.syncingFromUrl = false;
|
|
161
|
+
this.updateStateOverview();
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
private syncToUrl() {
|
|
166
|
+
if (this.syncingFromUrl) return;
|
|
167
|
+
|
|
168
|
+
const snapshot: Record<string, SignerState> = {};
|
|
169
|
+
|
|
170
|
+
for (const id of SIGNER_IDS) {
|
|
171
|
+
snapshot[id] = this.innerBucket.getState(id) ?? { pubKey: null, signed: false, wallet: null };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const payload = this.getPayload();
|
|
175
|
+
const nextSearch = buildSearchWithSigning(routerService.search$.actual, snapshot, payload);
|
|
176
|
+
|
|
177
|
+
if (nextSearch === routerService.search$.actual) return;
|
|
178
|
+
|
|
179
|
+
routerService.pushHistory(routerService.pathname$.actual + nextSearch);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
private getPayload(): string {
|
|
183
|
+
return clampSigningPayload(this.innerBucket.getValue(PAYLOAD_INPUT_ID) ?? DEFAULT_PAYLOAD);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private applyPayload(payload: string) {
|
|
187
|
+
const clamped = clampSigningPayload(payload);
|
|
188
|
+
|
|
189
|
+
this.innerBucket.setValue(PAYLOAD_INPUT_ID, clamped);
|
|
190
|
+
this.updateSignerPayloadConfigs(clamped);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
private updateSignerPayloadConfigs(payload: string) {
|
|
194
|
+
for (const id of SIGNER_IDS) {
|
|
195
|
+
const config = this.innerBucket.descriptors[id]?.config ?? {};
|
|
196
|
+
|
|
197
|
+
this.innerBucket.setConfig(id, { ...config, payload });
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
private updateStateOverview() {
|
|
202
|
+
const lines = SIGNER_IDS.map((id, index) => {
|
|
203
|
+
const state = this.innerBucket.getState(id) ?? this.emptySignerState();
|
|
204
|
+
const title = `Signer ${index + 1}`;
|
|
205
|
+
const status = state.signed ? "Signed" : state.pubKey ? "Connected" : "Idle";
|
|
206
|
+
const wallet = state.wallet
|
|
207
|
+
? web3Service.getWalletLabel(state.wallet)
|
|
208
|
+
: "No wallet";
|
|
209
|
+
const pubKey = pubKeyToText(state.pubKey);
|
|
210
|
+
|
|
211
|
+
return `${title}: ${status} · ${wallet} · ${pubKey}`;
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
this.stateOverview$.update(lines.join("\n"));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
private emptySignerState(): SignerState {
|
|
218
|
+
return { pubKey: null, signed: false, wallet: null };
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
private ensureTonManifest() {
|
|
222
|
+
if (web3Service.getTonManifestUrl()) return;
|
|
223
|
+
|
|
224
|
+
web3Service.setTonManifestUrl(new URL("/tonconnect-manifest.json", window.location.href).href);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
private updateWalletHint() {
|
|
228
|
+
const extensions = detectInjectedWallets();
|
|
229
|
+
const hints = ["Click Connect wallet to choose a provider (extension or mobile app)."];
|
|
230
|
+
|
|
231
|
+
if (extensions.length) {
|
|
232
|
+
hints.push(`Extensions detected: ${extensions.map(getInjectedWalletLabel).join(" · ")}`);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
this.walletHint$.update(hints.join(" "));
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
componentsRegistryService.define(Web3SigningComponent);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { componentsRegistryService, RxBucket } from "cruzo";
|
|
2
|
+
import { ModalConfig } from "cruzo/ui-components/modal";
|
|
3
|
+
|
|
4
|
+
export const WALLET_MODAL_ID = "web3-wallet-modal";
|
|
5
|
+
export const WALLET_PICKER_ID = "web3-wallet-picker";
|
|
6
|
+
export const WALLET_ACTIVE_SIGNER_ID = "active-signer";
|
|
7
|
+
|
|
8
|
+
export const web3WalletPickerBucket = new RxBucket({
|
|
9
|
+
[WALLET_MODAL_ID]: {},
|
|
10
|
+
[WALLET_PICKER_ID]: {},
|
|
11
|
+
[WALLET_ACTIVE_SIGNER_ID]: {},
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
web3WalletPickerBucket.setConfig(
|
|
15
|
+
WALLET_MODAL_ID,
|
|
16
|
+
ModalConfig({
|
|
17
|
+
bodyContent: `<web3-wallet-picker-component component-id="${WALLET_PICKER_ID}" bucket-id="${web3WalletPickerBucket.id}"></web3-wallet-picker-component>`,
|
|
18
|
+
dependencies: new Set(["web3-wallet-picker-component"]),
|
|
19
|
+
}),
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
componentsRegistryService.connectBucket(web3WalletPickerBucket);
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
.picker {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
gap: var(--m);
|
|
5
|
+
min-width: 280px;
|
|
6
|
+
max-width: 360px;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.title {
|
|
10
|
+
margin: 0;
|
|
11
|
+
font-size: 18px;
|
|
12
|
+
font-weight: 600;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.section {
|
|
16
|
+
display: flex;
|
|
17
|
+
flex-direction: column;
|
|
18
|
+
gap: var(--xs);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.sectionTitle {
|
|
22
|
+
margin: 0;
|
|
23
|
+
font-size: 12px;
|
|
24
|
+
font-weight: 600;
|
|
25
|
+
letter-spacing: 0.04em;
|
|
26
|
+
text-transform: uppercase;
|
|
27
|
+
color: rgba(0, 0, 0, 0.45);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.option {
|
|
31
|
+
display: flex;
|
|
32
|
+
flex-direction: column;
|
|
33
|
+
align-items: flex-start;
|
|
34
|
+
gap: 2px;
|
|
35
|
+
width: 100%;
|
|
36
|
+
padding: var(--s) var(--m);
|
|
37
|
+
border: 1px solid var(--border-dark);
|
|
38
|
+
border-radius: var(--s);
|
|
39
|
+
background: var(--surface);
|
|
40
|
+
text-align: left;
|
|
41
|
+
cursor: pointer;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.option:hover:not(:disabled) {
|
|
45
|
+
border-color: rgba(0, 0, 0, 0.25);
|
|
46
|
+
background: rgba(0, 0, 0, 0.02);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.option:disabled {
|
|
50
|
+
opacity: 0.45;
|
|
51
|
+
cursor: not-allowed;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.optionLabel {
|
|
55
|
+
font-size: 14px;
|
|
56
|
+
font-weight: 500;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.optionHint {
|
|
60
|
+
font-size: 12px;
|
|
61
|
+
color: rgba(0, 0, 0, 0.55);
|
|
62
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import styles from "./web3-wallet-picker.component.module.css";
|
|
2
|
+
|
|
3
|
+
import { AbstractComponent, componentsRegistryService } from "cruzo";
|
|
4
|
+
|
|
5
|
+
import { hasInjectedWallet } from "../../providers/injected";
|
|
6
|
+
import type { WalletKind } from "../../providers/wallet";
|
|
7
|
+
import type { WalletTransport } from "../../providers/wallet-transport";
|
|
8
|
+
import { web3Service } from "../../web3.service";
|
|
9
|
+
import type { Web3WalletTarget } from "../../web3-wallet";
|
|
10
|
+
import { WALLET_MODAL_ID, WALLET_PICKER_ID } from "./web3-wallet-picker.bucket";
|
|
11
|
+
|
|
12
|
+
declare global {
|
|
13
|
+
interface BucketEventMap {
|
|
14
|
+
web3WalletSelected: Web3WalletTarget;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class Web3WalletPickerComponent extends AbstractComponent {
|
|
19
|
+
static selector = "web3-wallet-picker-component";
|
|
20
|
+
hasOuterBucket = true;
|
|
21
|
+
|
|
22
|
+
ethSectionVisible$ = this.newRx(false);
|
|
23
|
+
ethExtVisible$ = this.newRx(false);
|
|
24
|
+
ethExtAvailable$ = this.newRx(false);
|
|
25
|
+
ethAppVisible$ = this.newRx(false);
|
|
26
|
+
ethAppAvailable$ = this.newRx(false);
|
|
27
|
+
|
|
28
|
+
tonSectionVisible$ = this.newRx(false);
|
|
29
|
+
tonExtVisible$ = this.newRx(false);
|
|
30
|
+
tonExtAvailable$ = this.newRx(false);
|
|
31
|
+
tonAppVisible$ = this.newRx(false);
|
|
32
|
+
tonAppAvailable$ = this.newRx(false);
|
|
33
|
+
|
|
34
|
+
solSectionVisible$ = this.newRx(false);
|
|
35
|
+
solExtVisible$ = this.newRx(false);
|
|
36
|
+
solExtAvailable$ = this.newRx(false);
|
|
37
|
+
|
|
38
|
+
tronSectionVisible$ = this.newRx(false);
|
|
39
|
+
tronExtVisible$ = this.newRx(false);
|
|
40
|
+
tronExtAvailable$ = this.newRx(false);
|
|
41
|
+
|
|
42
|
+
customOptions$ = this.newRx<{ id: string; label: string; hint: string }[]>([]);
|
|
43
|
+
|
|
44
|
+
getHTML() {
|
|
45
|
+
return `<div class="${styles.picker}">
|
|
46
|
+
<h3 class="${styles.title}">Connect wallet</h3>
|
|
47
|
+
|
|
48
|
+
<div class="${styles.section}" attached="{{ root.ethSectionVisible$::rx }}">
|
|
49
|
+
<h4 class="${styles.sectionTitle}">Ethereum</h4>
|
|
50
|
+
<button type="button" class="${styles.option}"
|
|
51
|
+
attached="{{ root.ethExtVisible$::rx }}"
|
|
52
|
+
disabled="{{ !root.ethExtAvailable$::rx }}"
|
|
53
|
+
onclick="{{ root.pick('ethereum', 'extension') }}">
|
|
54
|
+
<span class="${styles.optionLabel}">Browser extension</span>
|
|
55
|
+
<span class="${styles.optionHint}">MetaMask, Rabby and other EIP-1193 wallets</span>
|
|
56
|
+
</button>
|
|
57
|
+
<button type="button" class="${styles.option}"
|
|
58
|
+
attached="{{ root.ethAppVisible$::rx }}"
|
|
59
|
+
disabled="{{ !root.ethAppAvailable$::rx }}"
|
|
60
|
+
onclick="{{ root.pick('ethereum', 'app') }}">
|
|
61
|
+
<span class="${styles.optionLabel}">Mobile wallet</span>
|
|
62
|
+
<span class="${styles.optionHint}">WalletConnect</span>
|
|
63
|
+
</button>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<div class="${styles.section}" attached="{{ root.tonSectionVisible$::rx }}">
|
|
67
|
+
<h4 class="${styles.sectionTitle}">TON</h4>
|
|
68
|
+
<button type="button" class="${styles.option}"
|
|
69
|
+
attached="{{ root.tonExtVisible$::rx }}"
|
|
70
|
+
disabled="{{ !root.tonExtAvailable$::rx }}"
|
|
71
|
+
onclick="{{ root.pick('ton', 'extension') }}">
|
|
72
|
+
<span class="${styles.optionLabel}">Browser extension</span>
|
|
73
|
+
<span class="${styles.optionHint}">Tonkeeper and other TON wallets</span>
|
|
74
|
+
</button>
|
|
75
|
+
<button type="button" class="${styles.option}"
|
|
76
|
+
attached="{{ root.tonAppVisible$::rx }}"
|
|
77
|
+
disabled="{{ !root.tonAppAvailable$::rx }}"
|
|
78
|
+
onclick="{{ root.pick('ton', 'app') }}">
|
|
79
|
+
<span class="${styles.optionLabel}">Mobile wallet</span>
|
|
80
|
+
<span class="${styles.optionHint}">Tonkeeper app via Ton Connect</span>
|
|
81
|
+
</button>
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
<div class="${styles.section}" attached="{{ root.solSectionVisible$::rx }}">
|
|
85
|
+
<h4 class="${styles.sectionTitle}">Solana</h4>
|
|
86
|
+
<button type="button" class="${styles.option}"
|
|
87
|
+
attached="{{ root.solExtVisible$::rx }}"
|
|
88
|
+
disabled="{{ !root.solExtAvailable$::rx }}"
|
|
89
|
+
onclick="{{ root.pick('solana', 'extension') }}">
|
|
90
|
+
<span class="${styles.optionLabel}">Browser extension</span>
|
|
91
|
+
<span class="${styles.optionHint}">Phantom and other Solana wallets</span>
|
|
92
|
+
</button>
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
<div class="${styles.section}" attached="{{ root.tronSectionVisible$::rx }}">
|
|
96
|
+
<h4 class="${styles.sectionTitle}">Tron</h4>
|
|
97
|
+
<button type="button" class="${styles.option}"
|
|
98
|
+
attached="{{ root.tronExtVisible$::rx }}"
|
|
99
|
+
disabled="{{ !root.tronExtAvailable$::rx }}"
|
|
100
|
+
onclick="{{ root.pick('tron', 'extension') }}">
|
|
101
|
+
<span class="${styles.optionLabel}">Browser extension</span>
|
|
102
|
+
<span class="${styles.optionHint}">TronLink</span>
|
|
103
|
+
</button>
|
|
104
|
+
</div>
|
|
105
|
+
|
|
106
|
+
<div class="${styles.section}" attached="{{ root.customOptions$::rx?.length }}">
|
|
107
|
+
<h4 class="${styles.sectionTitle}">Custom</h4>
|
|
108
|
+
<button type="button" class="${styles.option}"
|
|
109
|
+
repeat="{{ root.customOptions$::rx }}"
|
|
110
|
+
onclick="{{ root.pickCustom(repeat.id) }}">
|
|
111
|
+
<span class="${styles.optionLabel}">{{ repeat.label }}</span>
|
|
112
|
+
<span class="${styles.optionHint}" attached="{{ repeat.hint }}">{{ repeat.hint }}</span>
|
|
113
|
+
</button>
|
|
114
|
+
</div>
|
|
115
|
+
</div>`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
connectedCallback() {
|
|
119
|
+
super.connectedCallback();
|
|
120
|
+
this.updateFromConfig();
|
|
121
|
+
this.newRxFunc(() => this.updateFromConfig(), web3Service.setup$);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
pick(kind: WalletKind, transport: WalletTransport) {
|
|
125
|
+
this.outerBucket.emitEvent(WALLET_PICKER_ID, "web3WalletSelected", { data: { kind, transport } });
|
|
126
|
+
this.outerBucket.emitEvent(WALLET_MODAL_ID, "closeModal", { data: { isOK: true } });
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
pickCustom(providerId: string) {
|
|
130
|
+
this.outerBucket.emitEvent(WALLET_PICKER_ID, "web3WalletSelected", {
|
|
131
|
+
data: { type: "custom", providerId },
|
|
132
|
+
});
|
|
133
|
+
this.outerBucket.emitEvent(WALLET_MODAL_ID, "closeModal", { data: { isOK: true } });
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private updateFromConfig() {
|
|
137
|
+
const config = web3Service;
|
|
138
|
+
|
|
139
|
+
const ethExtVisible = config.isBuiltinEnabled("ethereum", "extension");
|
|
140
|
+
const ethAppVisible = config.isBuiltinEnabled("ethereum", "app");
|
|
141
|
+
const tonExtVisible = config.isBuiltinEnabled("ton", "extension");
|
|
142
|
+
const tonAppVisible = config.isBuiltinEnabled("ton", "app");
|
|
143
|
+
const solExtVisible = config.isBuiltinEnabled("solana", "extension");
|
|
144
|
+
const tronExtVisible = config.isBuiltinEnabled("tron", "extension");
|
|
145
|
+
|
|
146
|
+
this.ethExtVisible$.update(ethExtVisible);
|
|
147
|
+
this.ethAppVisible$.update(ethAppVisible);
|
|
148
|
+
this.ethSectionVisible$.update(ethExtVisible || ethAppVisible);
|
|
149
|
+
|
|
150
|
+
this.tonExtVisible$.update(tonExtVisible);
|
|
151
|
+
this.tonAppVisible$.update(tonAppVisible);
|
|
152
|
+
this.tonSectionVisible$.update(tonExtVisible || tonAppVisible);
|
|
153
|
+
|
|
154
|
+
this.solExtVisible$.update(solExtVisible);
|
|
155
|
+
this.solSectionVisible$.update(solExtVisible);
|
|
156
|
+
|
|
157
|
+
this.tronExtVisible$.update(tronExtVisible);
|
|
158
|
+
this.tronSectionVisible$.update(tronExtVisible);
|
|
159
|
+
|
|
160
|
+
this.ethExtAvailable$.update(hasInjectedWallet("ethereum"));
|
|
161
|
+
this.ethAppAvailable$.update(!!web3Service.getWalletConnectProjectId());
|
|
162
|
+
this.tonExtAvailable$.update(hasInjectedWallet("ton"));
|
|
163
|
+
this.tonAppAvailable$.update(!!web3Service.getTonManifestUrl());
|
|
164
|
+
this.solExtAvailable$.update(hasInjectedWallet("solana"));
|
|
165
|
+
this.tronExtAvailable$.update(hasInjectedWallet("tron"));
|
|
166
|
+
|
|
167
|
+
this.customOptions$.update(config.listCustomProviderOptions());
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
componentsRegistryService.define(Web3WalletPickerComponent);
|