@layerswap/wallet-stellar 2.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/dist/esm/constants.js +8 -0
- package/dist/esm/index.js +50 -0
- package/dist/esm/service/StellarConnectionService.js +205 -0
- package/dist/esm/service/StellarWalletConnectModule.js +218 -0
- package/dist/esm/service/createStellarConnection.js +63 -0
- package/dist/esm/service/stellarConnector.js +23 -0
- package/dist/esm/service/stellarKitManager.js +272 -0
- package/dist/esm/service/stellarStore.js +14 -0
- package/dist/esm/stellarBalanceProvider.js +49 -0
- package/dist/esm/stellarBalances.js +40 -0
- package/dist/esm/stellarGasProvider.js +22 -0
- package/dist/esm/stellarNetwork.js +34 -0
- package/dist/esm/stellarServers.js +51 -0
- package/dist/esm/transferProvider/createStellarTransfer.js +122 -0
- package/dist/esm/transferProvider/validateStellarXdr.js +249 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types/constants.d.ts +5 -0
- package/dist/types/constants.d.ts.map +1 -0
- package/dist/types/index.d.ts +14 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/service/StellarConnectionService.d.ts +44 -0
- package/dist/types/service/StellarConnectionService.d.ts.map +1 -0
- package/dist/types/service/StellarWalletConnectModule.d.ts +86 -0
- package/dist/types/service/StellarWalletConnectModule.d.ts.map +1 -0
- package/dist/types/service/createStellarConnection.d.ts +7 -0
- package/dist/types/service/createStellarConnection.d.ts.map +1 -0
- package/dist/types/service/stellarConnector.d.ts +4 -0
- package/dist/types/service/stellarConnector.d.ts.map +1 -0
- package/dist/types/service/stellarKitManager.d.ts +37 -0
- package/dist/types/service/stellarKitManager.d.ts.map +1 -0
- package/dist/types/service/stellarStore.d.ts +24 -0
- package/dist/types/service/stellarStore.d.ts.map +1 -0
- package/dist/types/stellarBalanceProvider.d.ts +6 -0
- package/dist/types/stellarBalanceProvider.d.ts.map +1 -0
- package/dist/types/stellarBalances.d.ts +22 -0
- package/dist/types/stellarBalances.d.ts.map +1 -0
- package/dist/types/stellarGasProvider.d.ts +11 -0
- package/dist/types/stellarGasProvider.d.ts.map +1 -0
- package/dist/types/stellarNetwork.d.ts +7 -0
- package/dist/types/stellarNetwork.d.ts.map +1 -0
- package/dist/types/stellarServers.d.ts +5 -0
- package/dist/types/stellarServers.d.ts.map +1 -0
- package/dist/types/transferProvider/createStellarTransfer.d.ts +3 -0
- package/dist/types/transferProvider/createStellarTransfer.d.ts.map +1 -0
- package/dist/types/transferProvider/validateStellarXdr.d.ts +22 -0
- package/dist/types/transferProvider/validateStellarXdr.d.ts.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import KnownInternalNames from '@layerswap/utils/known-ids';
|
|
2
|
+
export const name = 'Stellar';
|
|
3
|
+
export const id = 'stellar';
|
|
4
|
+
export const supportedNetworkNames = [
|
|
5
|
+
KnownInternalNames.Networks.StellarMainnet,
|
|
6
|
+
KnownInternalNames.Networks.StellarTestnet,
|
|
7
|
+
];
|
|
8
|
+
export const STELLAR_SESSION_KEY = 'layerswap:stellar-wallet';
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { LazyBalanceProvider, LazyGasProvider, NetworkType, } from '@layerswap/widget-types';
|
|
2
|
+
import { id } from './constants';
|
|
3
|
+
import { createStellarConnection } from './service/createStellarConnection';
|
|
4
|
+
import { stellarKitManager } from './service/stellarKitManager';
|
|
5
|
+
import { createStellarTransfer } from './transferProvider/createStellarTransfer';
|
|
6
|
+
export function createStellarProvider(config = {}) {
|
|
7
|
+
const { walletConnect, customConnection, balanceProviders, gasProviders, transferProviders, } = config;
|
|
8
|
+
const initialize = () => {
|
|
9
|
+
void stellarKitManager.init(walletConnect).catch(error => {
|
|
10
|
+
console.error('[Stellar] Failed to initialize wallet connectors', error);
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
const init = (_context) => {
|
|
14
|
+
initialize();
|
|
15
|
+
return () => stellarKitManager.dispose();
|
|
16
|
+
};
|
|
17
|
+
const createConnection = (props) => {
|
|
18
|
+
initialize();
|
|
19
|
+
return customConnection
|
|
20
|
+
? customConnection(props)
|
|
21
|
+
: createStellarConnection(props, { walletConnectProjectId: walletConnect?.projectId });
|
|
22
|
+
};
|
|
23
|
+
const defaultBalanceProviders = [
|
|
24
|
+
new LazyBalanceProvider(network => network.type === NetworkType.Stellar, () => import('./stellarBalanceProvider').then(module => new module.StellarBalanceProvider())),
|
|
25
|
+
];
|
|
26
|
+
const finalBalanceProviders = balanceProviders !== undefined
|
|
27
|
+
? (Array.isArray(balanceProviders) ? balanceProviders : [balanceProviders])
|
|
28
|
+
: defaultBalanceProviders;
|
|
29
|
+
const defaultGasProviders = [
|
|
30
|
+
new LazyGasProvider(network => network.type === NetworkType.Stellar, () => import('./stellarGasProvider').then(module => new module.StellarGasProvider())),
|
|
31
|
+
];
|
|
32
|
+
const finalGasProviders = gasProviders !== undefined
|
|
33
|
+
? (Array.isArray(gasProviders) ? gasProviders : [gasProviders])
|
|
34
|
+
: defaultGasProviders;
|
|
35
|
+
const finalTransferProviders = transferProviders !== undefined
|
|
36
|
+
? (Array.isArray(transferProviders) ? transferProviders : [transferProviders])
|
|
37
|
+
: [createStellarTransfer];
|
|
38
|
+
return {
|
|
39
|
+
id,
|
|
40
|
+
init,
|
|
41
|
+
createConnection,
|
|
42
|
+
balanceProvider: finalBalanceProviders,
|
|
43
|
+
gasProvider: finalGasProviders,
|
|
44
|
+
transferProvider: finalTransferProviders,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export { createStellarConnection } from './service/createStellarConnection';
|
|
48
|
+
export { stellarStore } from './service/stellarStore';
|
|
49
|
+
export { stellarKitManager } from './service/stellarKitManager';
|
|
50
|
+
export { validateStellarOperationXdr, validateStellarXdr } from './transferProvider/validateStellarXdr';
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { NetworkType } from '@layerswap/widget-types';
|
|
2
|
+
import { buildDeepLink, clearPendingDynamicWcMetadata, createRegistryConnector, getDynamicWcMetadata, getPendingDynamicWcMetadata, isWalletConnectRegistryConnector, setDynamicWcMetadata, setPendingMetadataForRegistry, subscribeDisplayUri, walletIconResolver, } from '@layerswap/wallet-core';
|
|
3
|
+
import { id as PROVIDER_ID, name as PROVIDER_NAME } from '../constants';
|
|
4
|
+
import { STELLAR_APPKIT_WALLET_CONNECT_ID } from './StellarWalletConnectModule';
|
|
5
|
+
import { stellarKitManager } from './stellarKitManager';
|
|
6
|
+
import { stellarStore } from './stellarStore';
|
|
7
|
+
import { toStellarConnector } from './stellarConnector';
|
|
8
|
+
export class StellarConnectionService {
|
|
9
|
+
constructor(kitManager = stellarKitManager) {
|
|
10
|
+
this.kitManager = kitManager;
|
|
11
|
+
this.networks = [];
|
|
12
|
+
this.networksKey = '';
|
|
13
|
+
this.deps = {};
|
|
14
|
+
}
|
|
15
|
+
setNetworks(networks, networkAdapter) {
|
|
16
|
+
const key = networks.map(network => networkAdapter.getId(network)).join('|');
|
|
17
|
+
if (this.networksKey === key)
|
|
18
|
+
return;
|
|
19
|
+
this.networks = networks;
|
|
20
|
+
this.networkAdapter = networkAdapter;
|
|
21
|
+
this.networksKey = key;
|
|
22
|
+
}
|
|
23
|
+
configure(deps) {
|
|
24
|
+
this.deps = { ...this.deps, ...deps };
|
|
25
|
+
}
|
|
26
|
+
getAvailableConnectors() {
|
|
27
|
+
const isMobilePlatform = this.deps.isMobilePlatform ?? false;
|
|
28
|
+
const configured = stellarStore.getState().wallets
|
|
29
|
+
.filter(wallet => wallet.id !== STELLAR_APPKIT_WALLET_CONNECT_ID)
|
|
30
|
+
.map(toStellarConnector)
|
|
31
|
+
.filter(connector => !isMobilePlatform || connector.isMobileSupported);
|
|
32
|
+
const recent = (this.deps.recentConnectors ?? [])
|
|
33
|
+
.filter(connector => !isMobilePlatform || connector.isMobileSupported);
|
|
34
|
+
return [...configured, ...recent];
|
|
35
|
+
}
|
|
36
|
+
getAdditionalConnectors() {
|
|
37
|
+
const isMobilePlatform = this.deps.isMobilePlatform ?? false;
|
|
38
|
+
return (this.deps.registryConnectors ?? [])
|
|
39
|
+
.filter(wallet => !isMobilePlatform || wallet.isMobileSupported)
|
|
40
|
+
.map(wallet => createRegistryConnector(wallet, isMobilePlatform, PROVIDER_NAME));
|
|
41
|
+
}
|
|
42
|
+
getConnectedWallets() {
|
|
43
|
+
const { wallets, activeWalletId, activeAddress } = stellarStore.getState();
|
|
44
|
+
if (!activeWalletId || !activeAddress)
|
|
45
|
+
return [];
|
|
46
|
+
const snapshot = wallets.find(wallet => wallet.id === activeWalletId);
|
|
47
|
+
if (!snapshot)
|
|
48
|
+
return [];
|
|
49
|
+
return [this.resolveWallet(snapshot, activeAddress)];
|
|
50
|
+
}
|
|
51
|
+
async connectWallet({ connector }) {
|
|
52
|
+
const wallets = stellarStore.getState().wallets;
|
|
53
|
+
const registryConnector = isWalletConnectRegistryConnector(connector) ? connector : undefined;
|
|
54
|
+
const wallet = registryConnector
|
|
55
|
+
? wallets.find(item => item.type === 'BRIDGE_WALLET')
|
|
56
|
+
: wallets.find(item => item.id === connector.id);
|
|
57
|
+
if (!wallet)
|
|
58
|
+
throw new Error('Stellar wallet connector not found');
|
|
59
|
+
const isWalletConnect = wallet.type === 'BRIDGE_WALLET';
|
|
60
|
+
const isMobilePlatform = this.deps.isMobilePlatform ?? false;
|
|
61
|
+
const mobile = registryConnector?.mobile;
|
|
62
|
+
const deepLink = mobile?.native || mobile?.universal || undefined;
|
|
63
|
+
const resolveURI = registryConnector && mobile && deepLink
|
|
64
|
+
? (uri) => buildDeepLink({ id: registryConnector.id, mobile }, uri)
|
|
65
|
+
: undefined;
|
|
66
|
+
const useAppKit = isWalletConnect && isMobilePlatform && !registryConnector;
|
|
67
|
+
const kitWallet = useAppKit ? wallets.find(item => item.id === STELLAR_APPKIT_WALLET_CONNECT_ID) ?? wallet : wallet;
|
|
68
|
+
let unsubscribeDisplayUri;
|
|
69
|
+
const setSelectedConnectorIfCurrent = (next) => {
|
|
70
|
+
if (!this.deps.getSelectedConnector) {
|
|
71
|
+
this.deps.setSelectedConnector?.(next);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const current = this.deps.getSelectedConnector();
|
|
75
|
+
if (current?.id === connector.id)
|
|
76
|
+
this.deps.setSelectedConnector?.(next);
|
|
77
|
+
};
|
|
78
|
+
try {
|
|
79
|
+
if (isWalletConnect) {
|
|
80
|
+
setPendingMetadataForRegistry(PROVIDER_ID, registryConnector ? { ...registryConnector, deepLink } : undefined);
|
|
81
|
+
const wantsQrModal = !useAppKit && (!isMobilePlatform || !resolveURI);
|
|
82
|
+
if (wantsQrModal) {
|
|
83
|
+
this.deps.setSelectedConnector?.({
|
|
84
|
+
...connector,
|
|
85
|
+
qr: { state: 'loading', value: undefined },
|
|
86
|
+
showQrCode: true,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
if (!useAppKit) {
|
|
90
|
+
unsubscribeDisplayUri = subscribeDisplayUri({
|
|
91
|
+
source: this.kitManager,
|
|
92
|
+
resolveURI,
|
|
93
|
+
isMobilePlatform,
|
|
94
|
+
onQr: qr => setSelectedConnectorIfCurrent({ ...connector, qr, showQrCode: true }),
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
if (registryConnector)
|
|
98
|
+
this.deps.addRecentConnector?.(registryConnector);
|
|
99
|
+
}
|
|
100
|
+
const { address } = await this.kitManager.connect(kitWallet.id);
|
|
101
|
+
if (registryConnector) {
|
|
102
|
+
setDynamicWcMetadata(PROVIDER_ID, address, {
|
|
103
|
+
name: registryConnector.name,
|
|
104
|
+
icon: registryConnector.icon || '',
|
|
105
|
+
id: registryConnector.id,
|
|
106
|
+
deepLink,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
return this.resolveWallet(kitWallet, address);
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
unsubscribeDisplayUri?.();
|
|
113
|
+
if (isWalletConnect)
|
|
114
|
+
clearPendingDynamicWcMetadata(PROVIDER_ID);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
async disconnectWallets() {
|
|
118
|
+
await this.kitManager.disconnect();
|
|
119
|
+
}
|
|
120
|
+
async requestAdditionalConnectors(params = {}) {
|
|
121
|
+
if (!this.deps.requestRegistryConnectors || !this.hasWalletConnectTransport()) {
|
|
122
|
+
return { connectors: [], nextPage: null, totalCount: 0 };
|
|
123
|
+
}
|
|
124
|
+
const result = await this.deps.requestRegistryConnectors(params);
|
|
125
|
+
const isMobilePlatform = this.deps.isMobilePlatform ?? false;
|
|
126
|
+
const connectors = result.connectors
|
|
127
|
+
.filter(wallet => !isMobilePlatform || wallet.isMobileSupported)
|
|
128
|
+
.map(wallet => createRegistryConnector(wallet, isMobilePlatform, PROVIDER_NAME));
|
|
129
|
+
return {
|
|
130
|
+
connectors,
|
|
131
|
+
nextPage: result.nextPage,
|
|
132
|
+
totalCount: result.totalCount,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
buildProvider() {
|
|
136
|
+
const connectedWallets = this.getConnectedWallets();
|
|
137
|
+
const activeWallet = connectedWallets[0];
|
|
138
|
+
const supportedNetworks = this.getSupportedNetworks();
|
|
139
|
+
const networkLogo = this.getNetworkLogo();
|
|
140
|
+
return {
|
|
141
|
+
connectWallet: this.connectWallet.bind(this),
|
|
142
|
+
disconnectWallets: this.disconnectWallets.bind(this),
|
|
143
|
+
requestAdditionalConnectors: this.hasWalletConnectTransport()
|
|
144
|
+
? this.requestAdditionalConnectors.bind(this)
|
|
145
|
+
: undefined,
|
|
146
|
+
availableConnectors: this.getAvailableConnectors(),
|
|
147
|
+
additionalConnectors: this.hasWalletConnectTransport()
|
|
148
|
+
? this.getAdditionalConnectors()
|
|
149
|
+
: undefined,
|
|
150
|
+
connectedWallets,
|
|
151
|
+
activeWallet,
|
|
152
|
+
autofillSupportedNetworks: supportedNetworks,
|
|
153
|
+
withdrawalSupportedNetworks: supportedNetworks,
|
|
154
|
+
asSourceSupportedNetworks: supportedNetworks,
|
|
155
|
+
name: PROVIDER_NAME,
|
|
156
|
+
id: PROVIDER_ID,
|
|
157
|
+
capabilities: this.hasWalletConnectTransport() ? {
|
|
158
|
+
walletConnectRegistry: {
|
|
159
|
+
networkTypes: [NetworkType.Stellar],
|
|
160
|
+
},
|
|
161
|
+
} : undefined,
|
|
162
|
+
providerIcon: networkLogo,
|
|
163
|
+
ready: stellarStore.getState().ready,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
resolveWallet(snapshot, address) {
|
|
167
|
+
const supportedNetworks = this.getSupportedNetworks();
|
|
168
|
+
const isWalletConnect = snapshot.type === 'BRIDGE_WALLET';
|
|
169
|
+
const dynamicMetadata = isWalletConnect
|
|
170
|
+
? getDynamicWcMetadata(PROVIDER_ID, address) || getPendingDynamicWcMetadata(PROVIDER_ID)
|
|
171
|
+
: null;
|
|
172
|
+
const displayName = dynamicMetadata?.name || snapshot.name;
|
|
173
|
+
const walletId = dynamicMetadata?.id || snapshot.id;
|
|
174
|
+
const icon = dynamicMetadata?.icon || snapshot.icon;
|
|
175
|
+
return {
|
|
176
|
+
id: walletId,
|
|
177
|
+
address,
|
|
178
|
+
addresses: [address],
|
|
179
|
+
displayName: `${displayName} - Stellar`,
|
|
180
|
+
providerName: PROVIDER_NAME,
|
|
181
|
+
isActive: true,
|
|
182
|
+
icon: walletIconResolver(address, icon),
|
|
183
|
+
networkIcon: this.getNetworkLogo(),
|
|
184
|
+
disconnect: () => this.disconnectWallets(),
|
|
185
|
+
autofillSupportedNetworks: supportedNetworks,
|
|
186
|
+
withdrawalSupportedNetworks: supportedNetworks,
|
|
187
|
+
asSourceSupportedNetworks: supportedNetworks,
|
|
188
|
+
metadata: { deepLink: dynamicMetadata?.deepLink },
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
hasWalletConnectTransport() {
|
|
192
|
+
return stellarStore.getState().wallets.some(wallet => wallet.type === 'BRIDGE_WALLET');
|
|
193
|
+
}
|
|
194
|
+
getNetworkLogo() {
|
|
195
|
+
const network = this.networks.find(item => this.networkAdapter?.isStellarNetwork(item));
|
|
196
|
+
return network && this.networkAdapter ? this.networkAdapter.getIcon(network) : undefined;
|
|
197
|
+
}
|
|
198
|
+
getSupportedNetworks() {
|
|
199
|
+
if (!this.networkAdapter)
|
|
200
|
+
return [];
|
|
201
|
+
return this.networks
|
|
202
|
+
.filter(network => this.networkAdapter?.isStellarNetwork(network))
|
|
203
|
+
.map(network => this.networkAdapter.getId(network));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { ModuleType, Networks } from '@creit.tech/stellar-wallets-kit/types';
|
|
2
|
+
const STELLAR_WALLET_CONNECT_STORAGE_PREFIX = 'layerswapStellarWalletConnect';
|
|
3
|
+
export const STELLAR_WALLET_CONNECT_ID = 'wallet_connect';
|
|
4
|
+
export const STELLAR_APPKIT_WALLET_CONNECT_ID = 'wallet_connect_appkit';
|
|
5
|
+
export const StellarWalletConnectChain = {
|
|
6
|
+
Public: 'stellar:pubnet',
|
|
7
|
+
Testnet: 'stellar:testnet',
|
|
8
|
+
};
|
|
9
|
+
const StellarWalletConnectMethod = {
|
|
10
|
+
Sign: 'stellar_signXDR',
|
|
11
|
+
SignAndSubmit: 'stellar_signAndSubmitXDR',
|
|
12
|
+
SignMessage: 'stellar_signMessage',
|
|
13
|
+
SignAuthEntry: 'stellar_signAuthEntry',
|
|
14
|
+
};
|
|
15
|
+
const stellarAccounts = (session) => session.namespaces.stellar?.accounts
|
|
16
|
+
?.map(account => account.split(':')[2])
|
|
17
|
+
.filter((address) => !!address) ?? [];
|
|
18
|
+
const stellarAccount = (session, expectedAddress) => {
|
|
19
|
+
const accounts = stellarAccounts(session);
|
|
20
|
+
return expectedAddress
|
|
21
|
+
? accounts.find(address => address === expectedAddress)
|
|
22
|
+
: accounts[0];
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Stellar Wallets Kit module backed directly by WalletConnect SignClient.
|
|
26
|
+
* The Kit still owns wallet selection and signing dispatch, while Layerswap owns
|
|
27
|
+
* presentation of the emitted `wc:` URI through its shared QR modal.
|
|
28
|
+
*/
|
|
29
|
+
export class StellarWalletConnectModule {
|
|
30
|
+
constructor(config, clientFactory = async () => {
|
|
31
|
+
const { SignClient } = await import('@walletconnect/sign-client');
|
|
32
|
+
return SignClient.init({
|
|
33
|
+
projectId: config.projectId,
|
|
34
|
+
metadata: {
|
|
35
|
+
name: config.name,
|
|
36
|
+
description: config.description,
|
|
37
|
+
url: config.url,
|
|
38
|
+
icons: config.icons,
|
|
39
|
+
},
|
|
40
|
+
customStoragePrefix: STELLAR_WALLET_CONNECT_STORAGE_PREFIX,
|
|
41
|
+
});
|
|
42
|
+
}, chains = [StellarWalletConnectChain.Public, StellarWalletConnectChain.Testnet]) {
|
|
43
|
+
this.config = config;
|
|
44
|
+
this.clientFactory = clientFactory;
|
|
45
|
+
this.chains = chains;
|
|
46
|
+
this.moduleType = ModuleType.BRIDGE_WALLET;
|
|
47
|
+
this.productId = STELLAR_WALLET_CONNECT_ID;
|
|
48
|
+
this.productName = 'WalletConnect';
|
|
49
|
+
this.productUrl = 'https://walletconnect.com/';
|
|
50
|
+
this.productIcon = 'https://stellar.creit.tech/wallet-icons/walletconnect.png';
|
|
51
|
+
this.displayUriListeners = new Set();
|
|
52
|
+
this.sessionDeleteListeners = new Set();
|
|
53
|
+
}
|
|
54
|
+
async isAvailable() {
|
|
55
|
+
return typeof window !== 'undefined';
|
|
56
|
+
}
|
|
57
|
+
async isPlatformWrapper() {
|
|
58
|
+
if (typeof window === 'undefined')
|
|
59
|
+
return false;
|
|
60
|
+
const stellar = window.stellar;
|
|
61
|
+
return stellar?.provider === 'freighter' && stellar.platform === 'mobile';
|
|
62
|
+
}
|
|
63
|
+
onDisplayUri(listener) {
|
|
64
|
+
this.displayUriListeners.add(listener);
|
|
65
|
+
return () => this.displayUriListeners.delete(listener);
|
|
66
|
+
}
|
|
67
|
+
onSessionDelete(listener) {
|
|
68
|
+
this.sessionDeleteListeners.add(listener);
|
|
69
|
+
return () => this.sessionDeleteListeners.delete(listener);
|
|
70
|
+
}
|
|
71
|
+
warmup() {
|
|
72
|
+
void this.getClient().catch(() => {
|
|
73
|
+
// A real connect attempt reports initialization failures to the UI.
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
async getAddress() {
|
|
77
|
+
const client = await this.getClient();
|
|
78
|
+
const chains = this.chains;
|
|
79
|
+
const { uri, approval } = await client.connect({
|
|
80
|
+
requiredNamespaces: {
|
|
81
|
+
stellar: {
|
|
82
|
+
chains,
|
|
83
|
+
methods: [StellarWalletConnectMethod.Sign],
|
|
84
|
+
events: [],
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
optionalNamespaces: {
|
|
88
|
+
stellar: {
|
|
89
|
+
chains,
|
|
90
|
+
methods: [
|
|
91
|
+
StellarWalletConnectMethod.SignAndSubmit,
|
|
92
|
+
StellarWalletConnectMethod.SignAuthEntry,
|
|
93
|
+
StellarWalletConnectMethod.SignMessage,
|
|
94
|
+
],
|
|
95
|
+
events: [],
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
if (uri) {
|
|
100
|
+
for (const listener of this.displayUriListeners)
|
|
101
|
+
listener(uri);
|
|
102
|
+
}
|
|
103
|
+
const session = await approval();
|
|
104
|
+
const address = stellarAccount(session);
|
|
105
|
+
if (!address) {
|
|
106
|
+
await client.disconnect({
|
|
107
|
+
topic: session.topic,
|
|
108
|
+
reason: { code: -1, message: 'Session approved without a Stellar account' },
|
|
109
|
+
});
|
|
110
|
+
throw new Error('The approved WalletConnect session has no Stellar account');
|
|
111
|
+
}
|
|
112
|
+
this.activeTopic = session.topic;
|
|
113
|
+
return { address };
|
|
114
|
+
}
|
|
115
|
+
async getConnectedAddress(expectedAddress) {
|
|
116
|
+
const client = await this.getClient();
|
|
117
|
+
const session = this.findSession(client, expectedAddress);
|
|
118
|
+
const address = session && stellarAccount(session, expectedAddress);
|
|
119
|
+
if (!address) {
|
|
120
|
+
throw new Error('The Stellar WalletConnect session expired; reconnect the wallet');
|
|
121
|
+
}
|
|
122
|
+
this.activeTopic = session.topic;
|
|
123
|
+
return { address };
|
|
124
|
+
}
|
|
125
|
+
async signTransaction(xdr, opts) {
|
|
126
|
+
const result = await this.request(StellarWalletConnectMethod.Sign, { xdr }, opts?.networkPassphrase, opts?.address);
|
|
127
|
+
if (!result?.signedXDR)
|
|
128
|
+
throw new Error('WalletConnect wallet returned no signed Stellar XDR');
|
|
129
|
+
return { signedTxXdr: result.signedXDR, signerAddress: opts?.address };
|
|
130
|
+
}
|
|
131
|
+
async signAndSubmitTransaction(xdr, opts) {
|
|
132
|
+
const result = await this.request(StellarWalletConnectMethod.SignAndSubmit, { xdr }, opts?.networkPassphrase, opts?.address);
|
|
133
|
+
if (result.status !== 'success' && result.status !== 'pending') {
|
|
134
|
+
throw new Error(`Unexpected Stellar WalletConnect status: ${result.status}`);
|
|
135
|
+
}
|
|
136
|
+
return { status: result.status };
|
|
137
|
+
}
|
|
138
|
+
async signAuthEntry(authEntry, opts) {
|
|
139
|
+
return this.request(StellarWalletConnectMethod.SignAuthEntry, { entryXdr: authEntry }, opts?.networkPassphrase, opts?.address);
|
|
140
|
+
}
|
|
141
|
+
async signMessage(message, opts) {
|
|
142
|
+
const result = await this.request(StellarWalletConnectMethod.SignMessage, { message }, opts?.networkPassphrase, opts?.address);
|
|
143
|
+
return { signedMessage: result.signature, signerAddress: result.signerAddress };
|
|
144
|
+
}
|
|
145
|
+
async getNetwork() {
|
|
146
|
+
throw new Error('WalletConnect does not expose the active Stellar network');
|
|
147
|
+
}
|
|
148
|
+
async disconnect() {
|
|
149
|
+
const client = this.client;
|
|
150
|
+
if (!client)
|
|
151
|
+
return;
|
|
152
|
+
const sessions = client.session.values.filter(session => stellarAccount(session));
|
|
153
|
+
await Promise.all(sessions.map(session => client.disconnect({
|
|
154
|
+
topic: session.topic,
|
|
155
|
+
reason: { code: -1, message: 'Session closed' },
|
|
156
|
+
})));
|
|
157
|
+
}
|
|
158
|
+
dispose() {
|
|
159
|
+
if (this.client && this.sessionEndedHandler) {
|
|
160
|
+
this.client.off('session_delete', this.sessionEndedHandler);
|
|
161
|
+
this.client.off('session_expire', this.sessionEndedHandler);
|
|
162
|
+
}
|
|
163
|
+
this.displayUriListeners.clear();
|
|
164
|
+
this.sessionDeleteListeners.clear();
|
|
165
|
+
this.sessionEndedHandler = undefined;
|
|
166
|
+
this.activeTopic = undefined;
|
|
167
|
+
this.client = undefined;
|
|
168
|
+
this.clientPromise = undefined;
|
|
169
|
+
}
|
|
170
|
+
async getClient() {
|
|
171
|
+
if (this.client)
|
|
172
|
+
return this.client;
|
|
173
|
+
if (!this.clientPromise) {
|
|
174
|
+
this.clientPromise = this.clientFactory().then(client => {
|
|
175
|
+
this.client = client;
|
|
176
|
+
this.sessionEndedHandler = event => {
|
|
177
|
+
if (this.activeTopic && event.topic !== this.activeTopic)
|
|
178
|
+
return;
|
|
179
|
+
this.activeTopic = undefined;
|
|
180
|
+
for (const listener of this.sessionDeleteListeners)
|
|
181
|
+
listener();
|
|
182
|
+
};
|
|
183
|
+
client.on('session_delete', this.sessionEndedHandler);
|
|
184
|
+
client.on('session_expire', this.sessionEndedHandler);
|
|
185
|
+
return client;
|
|
186
|
+
}).finally(() => {
|
|
187
|
+
this.clientPromise = undefined;
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
return this.clientPromise;
|
|
191
|
+
}
|
|
192
|
+
async request(method, params, networkPassphrase, address) {
|
|
193
|
+
const client = await this.getClient();
|
|
194
|
+
const session = this.findSession(client, address);
|
|
195
|
+
if (!session) {
|
|
196
|
+
throw new Error('No active Stellar WalletConnect session for the selected address');
|
|
197
|
+
}
|
|
198
|
+
this.activeTopic = session.topic;
|
|
199
|
+
const chainId = networkPassphrase === Networks.PUBLIC
|
|
200
|
+
? StellarWalletConnectChain.Public
|
|
201
|
+
: StellarWalletConnectChain.Testnet;
|
|
202
|
+
return client.request({
|
|
203
|
+
topic: session.topic,
|
|
204
|
+
chainId,
|
|
205
|
+
request: { method, params },
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
findSession(client, address) {
|
|
209
|
+
const active = this.activeTopic
|
|
210
|
+
? client.session.values.find(session => (session.topic === this.activeTopic && stellarAccount(session, address)))
|
|
211
|
+
: undefined;
|
|
212
|
+
if (active)
|
|
213
|
+
return active;
|
|
214
|
+
return [...client.session.values]
|
|
215
|
+
.reverse()
|
|
216
|
+
.find(session => stellarAccount(session, address));
|
|
217
|
+
}
|
|
218
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { connectModalStore, createMemoizedConnectionStore, getAdditionalConnectorsStore } from '@layerswap/wallet-core';
|
|
2
|
+
import { isMobile } from '@layerswap/utils';
|
|
3
|
+
import { id as PROVIDER_ID } from '../constants';
|
|
4
|
+
import { StellarConnectionService } from './StellarConnectionService';
|
|
5
|
+
import { stellarKitManager } from './stellarKitManager';
|
|
6
|
+
import { stellarStore } from './stellarStore';
|
|
7
|
+
export function createStellarConnection(initialProps, options = {}) {
|
|
8
|
+
let networks = initialProps.networks;
|
|
9
|
+
let networkAdapter = initialProps.networkAdapter;
|
|
10
|
+
const service = new StellarConnectionService();
|
|
11
|
+
service.setNetworks(networks, networkAdapter);
|
|
12
|
+
const additionalConnectorsStore = options.walletConnectProjectId
|
|
13
|
+
? getAdditionalConnectorsStore(PROVIDER_ID, options.walletConnectProjectId)
|
|
14
|
+
: undefined;
|
|
15
|
+
service.configure({
|
|
16
|
+
setSelectedConnector: connector => connectModalStore.setSelectedConnector(connector),
|
|
17
|
+
getSelectedConnector: () => connectModalStore.getSnapshot().selectedConnector,
|
|
18
|
+
addRecentConnector: additionalConnectorsStore?.addRecentConnector,
|
|
19
|
+
requestRegistryConnectors: additionalConnectorsStore?.requestAdditionalConnectors,
|
|
20
|
+
registryConnectors: additionalConnectorsStore?.getSnapshot().browseConnectors,
|
|
21
|
+
recentConnectors: additionalConnectorsStore?.getSnapshot().recentConnectors,
|
|
22
|
+
isMobilePlatform: isMobile(),
|
|
23
|
+
});
|
|
24
|
+
return createMemoizedConnectionStore({
|
|
25
|
+
computeInputs: () => {
|
|
26
|
+
const state = stellarStore.getState();
|
|
27
|
+
const additional = additionalConnectorsStore?.getSnapshot();
|
|
28
|
+
return {
|
|
29
|
+
wallets: state.wallets,
|
|
30
|
+
activeWalletId: state.activeWalletId,
|
|
31
|
+
activeAddress: state.activeAddress,
|
|
32
|
+
networkPassphrase: state.networkPassphrase,
|
|
33
|
+
ready: state.ready,
|
|
34
|
+
error: state.error,
|
|
35
|
+
browseConnectors: additional?.browseConnectors,
|
|
36
|
+
recentConnectors: additional?.recentConnectors,
|
|
37
|
+
networks,
|
|
38
|
+
};
|
|
39
|
+
},
|
|
40
|
+
buildSnapshot: inputs => {
|
|
41
|
+
service.configure({
|
|
42
|
+
registryConnectors: inputs.browseConnectors,
|
|
43
|
+
recentConnectors: inputs.recentConnectors,
|
|
44
|
+
});
|
|
45
|
+
return service.buildProvider();
|
|
46
|
+
},
|
|
47
|
+
subscribe: sync => [
|
|
48
|
+
stellarStore.subscribe(sync),
|
|
49
|
+
...(additionalConnectorsStore ? [additionalConnectorsStore.subscribe(sync)] : []),
|
|
50
|
+
connectModalStore.subscribe(() => {
|
|
51
|
+
if (!connectModalStore.getSnapshot().isWalletModalOpen)
|
|
52
|
+
return;
|
|
53
|
+
void additionalConnectorsStore?.ensureBrowseLoaded();
|
|
54
|
+
stellarKitManager.warmUpWalletConnect();
|
|
55
|
+
}),
|
|
56
|
+
],
|
|
57
|
+
onUpdateProps: nextProps => {
|
|
58
|
+
networks = nextProps.networks;
|
|
59
|
+
networkAdapter = nextProps.networkAdapter;
|
|
60
|
+
service.setNetworks(networks, networkAdapter);
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { name as PROVIDER_NAME } from '../constants';
|
|
2
|
+
// Wallets Kit reports both installed extensions and wallets that are usable
|
|
3
|
+
// without installation as `isAvailable`. Albedo/xBull open their own web flow,
|
|
4
|
+
// while BRIDGE_WALLET modules (currently WalletConnect) use a pairing transport.
|
|
5
|
+
// Neither kind should ever be presented as a missing browser extension.
|
|
6
|
+
const LOADABLE_WALLET_IDS = new Set(['albedo', 'xbull']);
|
|
7
|
+
export function toStellarConnector(wallet) {
|
|
8
|
+
const isBridgeWallet = wallet.type === 'BRIDGE_WALLET';
|
|
9
|
+
const isLoadable = isBridgeWallet || LOADABLE_WALLET_IDS.has(wallet.id);
|
|
10
|
+
const isUnavailable = !isLoadable && !wallet.isAvailable && !wallet.isPlatformWrapper;
|
|
11
|
+
return {
|
|
12
|
+
id: wallet.id,
|
|
13
|
+
name: wallet.name,
|
|
14
|
+
icon: wallet.icon,
|
|
15
|
+
type: isBridgeWallet ? 'walletConnect' : isUnavailable ? 'other' : 'injected',
|
|
16
|
+
installUrl: wallet.url,
|
|
17
|
+
hasBrowserExtension: isBridgeWallet ? false : undefined,
|
|
18
|
+
extensionNotFound: isUnavailable,
|
|
19
|
+
isLoadable,
|
|
20
|
+
isMobileSupported: isLoadable || wallet.isPlatformWrapper,
|
|
21
|
+
providerName: PROVIDER_NAME,
|
|
22
|
+
};
|
|
23
|
+
}
|