@rango-dev/provider-phantom 0.0.0-experimental-936229e8-20251208
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/CHANGELOG.md +285 -0
- package/dist/constants.d.ts +7 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/legacy/index.d.ts +15 -0
- package/dist/legacy/index.d.ts.map +1 -0
- package/dist/mod.d.ts +3 -0
- package/dist/mod.d.ts.map +1 -0
- package/dist/mod.js +2 -0
- package/dist/mod.js.map +7 -0
- package/dist/namespaces/evm.d.ts +4 -0
- package/dist/namespaces/evm.d.ts.map +1 -0
- package/dist/namespaces/solana.d.ts +4 -0
- package/dist/namespaces/solana.d.ts.map +1 -0
- package/dist/namespaces/sui.d.ts +4 -0
- package/dist/namespaces/sui.d.ts.map +1 -0
- package/dist/namespaces/utxo.d.ts +4 -0
- package/dist/namespaces/utxo.d.ts.map +1 -0
- package/dist/provider-phantom.build.json +1 -0
- package/dist/provider.d.ts +3 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/signer.d.ts +4 -0
- package/dist/signer.d.ts.map +1 -0
- package/dist/signers/utxoSigner.d.ts +12 -0
- package/dist/signers/utxoSigner.d.ts.map +1 -0
- package/dist/utils.d.ts +11 -0
- package/dist/utils.d.ts.map +1 -0
- package/package.json +37 -0
- package/readme.md +25 -0
- package/src/constants.ts +86 -0
- package/src/legacy/index.ts +175 -0
- package/src/mod.ts +12 -0
- package/src/namespaces/evm.ts +62 -0
- package/src/namespaces/solana.ts +80 -0
- package/src/namespaces/sui.ts +51 -0
- package/src/namespaces/utxo.ts +171 -0
- package/src/provider.ts +27 -0
- package/src/signer.ts +43 -0
- package/src/signers/utxoSigner.ts +92 -0
- package/src/utils.ts +98 -0
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rango-dev/provider-phantom",
|
|
3
|
+
"version": "0.0.0-experimental-936229e8-20251208",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"source": "./src/mod.ts",
|
|
7
|
+
"main": "./dist/mod.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./dist/mod.js"
|
|
10
|
+
},
|
|
11
|
+
"typings": "dist/mod.d.ts",
|
|
12
|
+
"files": [
|
|
13
|
+
"dist",
|
|
14
|
+
"src"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "node ../../scripts/build/command.mjs --path wallets/provider-phantom --inputs src/mod.ts",
|
|
18
|
+
"ts-check": "tsc --declaration --emitDeclarationOnly -p ./tsconfig.json",
|
|
19
|
+
"clean": "rimraf dist",
|
|
20
|
+
"format": "prettier --write '{.,src}/**/*.{ts,tsx}'",
|
|
21
|
+
"lint": "eslint \"**/*.{ts,tsx}\""
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@bitcoinerlab/secp256k1": "^1.2.0",
|
|
25
|
+
"@mysten/sui": "^1.21.2",
|
|
26
|
+
"@mysten/wallet-standard": "^0.13.26",
|
|
27
|
+
"@rango-dev/signer-solana": "^0.46.1",
|
|
28
|
+
"@rango-dev/signer-sui": "^0.8.0",
|
|
29
|
+
"@rango-dev/wallets-core": "^0.0.0-experimental-936229e8-20251208",
|
|
30
|
+
"@rango-dev/wallets-shared": "^0.0.0-experimental-936229e8-20251208",
|
|
31
|
+
"bitcoinjs-lib": "^6.1.7",
|
|
32
|
+
"rango-types": "^0.1.89"
|
|
33
|
+
},
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
}
|
|
37
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Phantom Provider
|
|
2
|
+
Phantom integration for hub.
|
|
3
|
+
[Homepage](https://phantom.com/) | [Docs](https://docs.phantom.com/)
|
|
4
|
+
|
|
5
|
+
More about implementation status can be found [here](../readme.md).
|
|
6
|
+
|
|
7
|
+
## Implementation notes/limitation
|
|
8
|
+
|
|
9
|
+
### Group
|
|
10
|
+
|
|
11
|
+
#### ⚠️ UTXO
|
|
12
|
+
Only supports Bitcoin.
|
|
13
|
+
|
|
14
|
+
#### ⚠️ EVM
|
|
15
|
+
Only supports Ethereum, Base, and Polygon.
|
|
16
|
+
|
|
17
|
+
### Feature
|
|
18
|
+
|
|
19
|
+
#### ⚠️ Auto Connect
|
|
20
|
+
On Sui, Phantom uses Solana's auto-connect mechanism.
|
|
21
|
+
This means if Solana is not connected simultaneously, the auto-connect feature on Sui will not work properly.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
More wallet information can be found in [readme.md](../readme.md).
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { type ProviderMetadata } from '@rango-dev/wallets-core';
|
|
2
|
+
import { LegacyNetworks } from '@rango-dev/wallets-core/legacy';
|
|
3
|
+
import { Networks } from '@rango-dev/wallets-shared';
|
|
4
|
+
import {
|
|
5
|
+
type BlockchainMeta,
|
|
6
|
+
type EvmBlockchainMeta,
|
|
7
|
+
isEvmBlockchain,
|
|
8
|
+
solanaBlockchain,
|
|
9
|
+
type SuiBlockchainMeta,
|
|
10
|
+
TransactionType,
|
|
11
|
+
type TransferBlockchainMeta,
|
|
12
|
+
} from 'rango-types';
|
|
13
|
+
|
|
14
|
+
import getSigners from './signer.js';
|
|
15
|
+
import { getInstanceOrThrow } from './utils.js';
|
|
16
|
+
|
|
17
|
+
export const EVM_SUPPORTED_CHAINS = [
|
|
18
|
+
LegacyNetworks.ETHEREUM,
|
|
19
|
+
LegacyNetworks.POLYGON,
|
|
20
|
+
LegacyNetworks.BASE,
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
export const WALLET_ID = 'phantom';
|
|
24
|
+
export const WALLET_NAME_IN_WALLET_STANDARD = 'Phantom';
|
|
25
|
+
|
|
26
|
+
export const metadata: ProviderMetadata = {
|
|
27
|
+
name: 'Phantom',
|
|
28
|
+
icon: 'https://raw.githubusercontent.com/rango-exchange/assets/main/wallets/phantom/icon.svg',
|
|
29
|
+
extensions: {
|
|
30
|
+
chrome:
|
|
31
|
+
'https://chrome.google.com/webstore/detail/phantom/bfnaelmomeimhlpmgjnjophhpkkoljpa',
|
|
32
|
+
homepage: 'https://phantom.app/',
|
|
33
|
+
},
|
|
34
|
+
properties: [
|
|
35
|
+
{
|
|
36
|
+
name: 'namespaces',
|
|
37
|
+
value: {
|
|
38
|
+
selection: 'multiple',
|
|
39
|
+
data: [
|
|
40
|
+
{
|
|
41
|
+
label: 'EVM',
|
|
42
|
+
value: 'EVM',
|
|
43
|
+
id: 'ETH',
|
|
44
|
+
getSupportedChains: (allBlockchains: BlockchainMeta[]) =>
|
|
45
|
+
allBlockchains.filter(
|
|
46
|
+
(chain): chain is EvmBlockchainMeta =>
|
|
47
|
+
isEvmBlockchain(chain) &&
|
|
48
|
+
EVM_SUPPORTED_CHAINS.includes(chain.name as Networks)
|
|
49
|
+
),
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
label: 'Solana',
|
|
53
|
+
value: 'Solana',
|
|
54
|
+
id: 'SOLANA',
|
|
55
|
+
getSupportedChains: (allBlockchains: BlockchainMeta[]) =>
|
|
56
|
+
solanaBlockchain(allBlockchains),
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
label: 'BTC',
|
|
60
|
+
value: 'UTXO',
|
|
61
|
+
id: 'BTC',
|
|
62
|
+
getSupportedChains: (allBlockchains: BlockchainMeta[]) =>
|
|
63
|
+
allBlockchains.filter(
|
|
64
|
+
(chain): chain is TransferBlockchainMeta =>
|
|
65
|
+
chain.name === Networks.BTC
|
|
66
|
+
),
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
label: 'Sui',
|
|
70
|
+
value: 'Sui',
|
|
71
|
+
id: 'SUI',
|
|
72
|
+
getSupportedChains: (allBlockchains: BlockchainMeta[]) =>
|
|
73
|
+
allBlockchains.filter(
|
|
74
|
+
(chain): chain is SuiBlockchainMeta =>
|
|
75
|
+
chain.type === TransactionType.SUI
|
|
76
|
+
),
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: 'signers',
|
|
83
|
+
value: { getSigners: async () => getSigners(getInstanceOrThrow()) },
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
};
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import type { LegacyProviderInterface } from '@rango-dev/wallets-core/legacy';
|
|
2
|
+
import type {
|
|
3
|
+
CanEagerConnect,
|
|
4
|
+
CanSwitchNetwork,
|
|
5
|
+
Connect,
|
|
6
|
+
Subscribe,
|
|
7
|
+
WalletInfo,
|
|
8
|
+
} from '@rango-dev/wallets-shared';
|
|
9
|
+
import type {
|
|
10
|
+
BlockchainMeta,
|
|
11
|
+
EvmBlockchainMeta,
|
|
12
|
+
SignerFactory,
|
|
13
|
+
SuiBlockchainMeta,
|
|
14
|
+
TransferBlockchainMeta,
|
|
15
|
+
} from 'rango-types';
|
|
16
|
+
|
|
17
|
+
import { LegacyNetworks as Networks } from '@rango-dev/wallets-core/legacy';
|
|
18
|
+
import {
|
|
19
|
+
chooseInstance,
|
|
20
|
+
getSolanaAccounts,
|
|
21
|
+
WalletTypes,
|
|
22
|
+
} from '@rango-dev/wallets-shared';
|
|
23
|
+
import {
|
|
24
|
+
isEvmBlockchain,
|
|
25
|
+
solanaBlockchain,
|
|
26
|
+
TransactionType,
|
|
27
|
+
} from 'rango-types';
|
|
28
|
+
|
|
29
|
+
import { EVM_SUPPORTED_CHAINS } from '../constants.js';
|
|
30
|
+
import signer from '../signer.js';
|
|
31
|
+
import { phantom as phantom_instance, type Provider } from '../utils.js';
|
|
32
|
+
|
|
33
|
+
const WALLET = WalletTypes.PHANTOM;
|
|
34
|
+
|
|
35
|
+
export const config = {
|
|
36
|
+
type: WALLET,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const getInstance = phantom_instance;
|
|
40
|
+
|
|
41
|
+
/*
|
|
42
|
+
* NOTE: Phantom's Hub version has support for EVM as well since we are deprecating the legacy,
|
|
43
|
+
* we just want to keep the implementation for some time and then legacy provider will be removed soon.
|
|
44
|
+
* So we don't add new namespaces (like EVM) to legacy.
|
|
45
|
+
*/
|
|
46
|
+
const connect: Connect = async ({ instance, meta }) => {
|
|
47
|
+
const solanaInstance = instance.get(Networks.SOLANA);
|
|
48
|
+
const result = await getSolanaAccounts({
|
|
49
|
+
instance: solanaInstance,
|
|
50
|
+
meta,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
return result;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const subscribe: Subscribe = ({ instance, updateAccounts, connect }) => {
|
|
57
|
+
const handleAccountsChanged = async (publicKey: string) => {
|
|
58
|
+
const network = Networks.SOLANA;
|
|
59
|
+
if (publicKey) {
|
|
60
|
+
const account = publicKey.toString();
|
|
61
|
+
updateAccounts([account]);
|
|
62
|
+
} else {
|
|
63
|
+
connect(network);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
instance?.on?.('accountChanged', handleAccountsChanged);
|
|
67
|
+
|
|
68
|
+
return () => {
|
|
69
|
+
instance?.off?.('accountChanged', handleAccountsChanged);
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
const canSwitchNetworkTo: CanSwitchNetwork = ({ network }) => {
|
|
73
|
+
return EVM_SUPPORTED_CHAINS.includes(network as Networks);
|
|
74
|
+
};
|
|
75
|
+
export const getSigners: (provider: Provider) => Promise<SignerFactory> =
|
|
76
|
+
signer;
|
|
77
|
+
|
|
78
|
+
const canEagerConnect: CanEagerConnect = async ({ instance, meta }) => {
|
|
79
|
+
const solanaInstance = chooseInstance(instance, meta, Networks.SOLANA);
|
|
80
|
+
try {
|
|
81
|
+
const result = await solanaInstance.connect({ onlyIfTrusted: true });
|
|
82
|
+
return !!result;
|
|
83
|
+
} catch {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
export const getWalletInfo: (allBlockChains: BlockchainMeta[]) => WalletInfo = (
|
|
88
|
+
allBlockChains
|
|
89
|
+
) => {
|
|
90
|
+
let supportedChains: BlockchainMeta[] = [];
|
|
91
|
+
const sui = allBlockChains.filter(
|
|
92
|
+
(blockchain) => blockchain.type === TransactionType.SUI
|
|
93
|
+
);
|
|
94
|
+
const solana = solanaBlockchain(allBlockChains);
|
|
95
|
+
const evms = allBlockChains.filter(
|
|
96
|
+
(chain): chain is EvmBlockchainMeta =>
|
|
97
|
+
isEvmBlockchain(chain) &&
|
|
98
|
+
EVM_SUPPORTED_CHAINS.includes(chain.name as Networks)
|
|
99
|
+
);
|
|
100
|
+
const btc = allBlockChains.find((chain) => chain.name === Networks.BTC);
|
|
101
|
+
supportedChains = supportedChains.concat(solana, evms, sui);
|
|
102
|
+
if (btc) {
|
|
103
|
+
supportedChains.push(btc);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
name: 'Phantom',
|
|
108
|
+
img: 'https://raw.githubusercontent.com/rango-exchange/assets/main/wallets/phantom/icon.svg',
|
|
109
|
+
installLink: {
|
|
110
|
+
CHROME:
|
|
111
|
+
'https://chrome.google.com/webstore/detail/phantom/bfnaelmomeimhlpmgjnjophhpkkoljpa',
|
|
112
|
+
|
|
113
|
+
DEFAULT: 'https://phantom.app/',
|
|
114
|
+
},
|
|
115
|
+
color: '#4d40c6',
|
|
116
|
+
// if you are adding a new namespace, don't forget to also update `properties`
|
|
117
|
+
needsNamespace: {
|
|
118
|
+
selection: 'multiple',
|
|
119
|
+
data: [
|
|
120
|
+
{
|
|
121
|
+
label: 'EVM',
|
|
122
|
+
value: 'EVM',
|
|
123
|
+
id: 'ETH',
|
|
124
|
+
getSupportedChains: (allBlockchains: BlockchainMeta[]) =>
|
|
125
|
+
allBlockchains.filter(
|
|
126
|
+
(chain): chain is EvmBlockchainMeta =>
|
|
127
|
+
isEvmBlockchain(chain) &&
|
|
128
|
+
EVM_SUPPORTED_CHAINS.includes(chain.name as Networks)
|
|
129
|
+
),
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
label: 'Solana',
|
|
133
|
+
value: 'Solana',
|
|
134
|
+
id: 'SOLANA',
|
|
135
|
+
getSupportedChains: (allBlockchains: BlockchainMeta[]) =>
|
|
136
|
+
solanaBlockchain(allBlockchains),
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
label: 'BTC',
|
|
140
|
+
value: 'UTXO',
|
|
141
|
+
id: 'BTC',
|
|
142
|
+
getSupportedChains: (allBlockchains: BlockchainMeta[]) =>
|
|
143
|
+
allBlockchains.filter(
|
|
144
|
+
(chain): chain is TransferBlockchainMeta =>
|
|
145
|
+
chain.name === Networks.BTC
|
|
146
|
+
),
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
label: 'Sui',
|
|
150
|
+
value: 'Sui',
|
|
151
|
+
id: 'SUI',
|
|
152
|
+
getSupportedChains: (allBlockchains: BlockchainMeta[]) =>
|
|
153
|
+
allBlockchains.filter(
|
|
154
|
+
(chain): chain is SuiBlockchainMeta =>
|
|
155
|
+
chain.type === TransactionType.SUI
|
|
156
|
+
),
|
|
157
|
+
},
|
|
158
|
+
],
|
|
159
|
+
},
|
|
160
|
+
supportedChains,
|
|
161
|
+
};
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
const buildLegacyProvider: () => LegacyProviderInterface = () => ({
|
|
165
|
+
config,
|
|
166
|
+
getInstance,
|
|
167
|
+
connect,
|
|
168
|
+
subscribe,
|
|
169
|
+
getSigners,
|
|
170
|
+
getWalletInfo,
|
|
171
|
+
canSwitchNetworkTo,
|
|
172
|
+
canEagerConnect,
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
export { buildLegacyProvider };
|
package/src/mod.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { defineVersions } from '@rango-dev/wallets-core/utils';
|
|
2
|
+
|
|
3
|
+
import { buildLegacyProvider } from './legacy/index.js';
|
|
4
|
+
import { buildProvider } from './provider.js';
|
|
5
|
+
|
|
6
|
+
const versions = () =>
|
|
7
|
+
defineVersions()
|
|
8
|
+
.version('0.0.0', buildLegacyProvider())
|
|
9
|
+
.version('1.0.0', buildProvider())
|
|
10
|
+
.build();
|
|
11
|
+
|
|
12
|
+
export { versions };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { EvmActions } from '@rango-dev/wallets-core/namespaces/evm';
|
|
2
|
+
|
|
3
|
+
import { NamespaceBuilder } from '@rango-dev/wallets-core';
|
|
4
|
+
import {
|
|
5
|
+
builders as commonBuilders,
|
|
6
|
+
standardizeAndThrowError,
|
|
7
|
+
} from '@rango-dev/wallets-core/namespaces/common';
|
|
8
|
+
import {
|
|
9
|
+
actions,
|
|
10
|
+
builders,
|
|
11
|
+
hooks,
|
|
12
|
+
} from '@rango-dev/wallets-core/namespaces/evm';
|
|
13
|
+
|
|
14
|
+
import { WALLET_ID } from '../constants.js';
|
|
15
|
+
import { evmPhantom } from '../utils.js';
|
|
16
|
+
|
|
17
|
+
const [changeAccountSubscriber, changeAccountCleanup] =
|
|
18
|
+
hooks.changeAccountSubscriber(evmPhantom);
|
|
19
|
+
|
|
20
|
+
/*
|
|
21
|
+
* TODO: If user imported a private key for EVM, it hasn't solana.
|
|
22
|
+
* when trying to connect to solana for this user we go through `-32603` which is an internal error.
|
|
23
|
+
* If phantom added an specific error code for this situation, we can consider handling the error here.
|
|
24
|
+
* @see https://docs.phantom.app/solana/errors
|
|
25
|
+
*/
|
|
26
|
+
const connect = builders
|
|
27
|
+
.connect()
|
|
28
|
+
.action(actions.connect(evmPhantom))
|
|
29
|
+
.before(changeAccountSubscriber)
|
|
30
|
+
.or(changeAccountCleanup)
|
|
31
|
+
.or(standardizeAndThrowError)
|
|
32
|
+
.build();
|
|
33
|
+
|
|
34
|
+
const disconnect = commonBuilders
|
|
35
|
+
.disconnect<EvmActions>()
|
|
36
|
+
.after(changeAccountCleanup)
|
|
37
|
+
.build();
|
|
38
|
+
|
|
39
|
+
const canEagerConnect = builders
|
|
40
|
+
.canEagerConnect()
|
|
41
|
+
.action(actions.canEagerConnect(evmPhantom))
|
|
42
|
+
.build();
|
|
43
|
+
|
|
44
|
+
const canSwitchNetwork = builders
|
|
45
|
+
.canSwitchNetwork()
|
|
46
|
+
.action(actions.canSwitchNetwork())
|
|
47
|
+
.build();
|
|
48
|
+
|
|
49
|
+
const getChainId = builders
|
|
50
|
+
.getChainId()
|
|
51
|
+
.action(actions.getChainId(evmPhantom))
|
|
52
|
+
.build();
|
|
53
|
+
|
|
54
|
+
const evm = new NamespaceBuilder<EvmActions>('EVM', WALLET_ID)
|
|
55
|
+
.action(connect)
|
|
56
|
+
.action(disconnect)
|
|
57
|
+
.action(canEagerConnect)
|
|
58
|
+
.action(canSwitchNetwork)
|
|
59
|
+
.action(getChainId)
|
|
60
|
+
.build();
|
|
61
|
+
|
|
62
|
+
export { evm };
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { CaipAccount } from '@rango-dev/wallets-core/namespaces/common';
|
|
2
|
+
import type { SolanaActions } from '@rango-dev/wallets-core/namespaces/solana';
|
|
3
|
+
|
|
4
|
+
import { ActionBuilder, NamespaceBuilder } from '@rango-dev/wallets-core';
|
|
5
|
+
import {
|
|
6
|
+
builders as commonBuilders,
|
|
7
|
+
standardizeAndThrowError,
|
|
8
|
+
} from '@rango-dev/wallets-core/namespaces/common';
|
|
9
|
+
import {
|
|
10
|
+
actions,
|
|
11
|
+
builders,
|
|
12
|
+
CAIP_NAMESPACE,
|
|
13
|
+
CAIP_SOLANA_CHAIN_ID,
|
|
14
|
+
hooks,
|
|
15
|
+
} from '@rango-dev/wallets-core/namespaces/solana';
|
|
16
|
+
import { CAIP } from '@rango-dev/wallets-core/utils';
|
|
17
|
+
import { getSolanaAccounts } from '@rango-dev/wallets-shared';
|
|
18
|
+
|
|
19
|
+
import { WALLET_ID } from '../constants.js';
|
|
20
|
+
import { solanaPhantom } from '../utils.js';
|
|
21
|
+
|
|
22
|
+
const [changeAccountSubscriber, changeAccountCleanup] =
|
|
23
|
+
hooks.changeAccountSubscriber(solanaPhantom);
|
|
24
|
+
|
|
25
|
+
/*
|
|
26
|
+
* TODO: If user imported a private key for EVM, it hasn't solana.
|
|
27
|
+
* when trying to connect to solana for this user we go through `-32603` which is an internal error.
|
|
28
|
+
* If phantom added an specific error code for this situation, we can consider handling the error here.
|
|
29
|
+
* @see https://docs.phantom.app/solana/errors
|
|
30
|
+
*/
|
|
31
|
+
const connect = builders
|
|
32
|
+
.connect()
|
|
33
|
+
.action(async function () {
|
|
34
|
+
const solanaInstance = solanaPhantom();
|
|
35
|
+
const result = await getSolanaAccounts({
|
|
36
|
+
instance: solanaInstance,
|
|
37
|
+
meta: [],
|
|
38
|
+
});
|
|
39
|
+
if (Array.isArray(result)) {
|
|
40
|
+
throw new Error(
|
|
41
|
+
'Expecting solana response to be a single value, not an array.'
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const formatAccounts = result.accounts.map(
|
|
46
|
+
(account) =>
|
|
47
|
+
CAIP.AccountId.format({
|
|
48
|
+
address: account,
|
|
49
|
+
chainId: {
|
|
50
|
+
namespace: CAIP_NAMESPACE,
|
|
51
|
+
reference: CAIP_SOLANA_CHAIN_ID,
|
|
52
|
+
},
|
|
53
|
+
}) as CaipAccount
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
return formatAccounts;
|
|
57
|
+
})
|
|
58
|
+
.before(changeAccountSubscriber)
|
|
59
|
+
.or(changeAccountCleanup)
|
|
60
|
+
.or(standardizeAndThrowError)
|
|
61
|
+
.build();
|
|
62
|
+
|
|
63
|
+
const disconnect = commonBuilders
|
|
64
|
+
.disconnect<SolanaActions>()
|
|
65
|
+
.after(changeAccountCleanup)
|
|
66
|
+
.build();
|
|
67
|
+
|
|
68
|
+
const canEagerConnect = new ActionBuilder<SolanaActions, 'canEagerConnect'>(
|
|
69
|
+
'canEagerConnect'
|
|
70
|
+
)
|
|
71
|
+
.action(actions.canEagerConnect(solanaPhantom))
|
|
72
|
+
.build();
|
|
73
|
+
|
|
74
|
+
const solana = new NamespaceBuilder<SolanaActions>('Solana', WALLET_ID)
|
|
75
|
+
.action(connect)
|
|
76
|
+
.action(disconnect)
|
|
77
|
+
.action(canEagerConnect)
|
|
78
|
+
.build();
|
|
79
|
+
|
|
80
|
+
export { solana };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { SolanaActions } from '@rango-dev/wallets-core/namespaces/solana';
|
|
2
|
+
import type { SuiActions } from '@rango-dev/wallets-core/namespaces/sui';
|
|
3
|
+
|
|
4
|
+
import { ActionBuilder, NamespaceBuilder } from '@rango-dev/wallets-core';
|
|
5
|
+
import {
|
|
6
|
+
builders as commonBuilders,
|
|
7
|
+
standardizeAndThrowError,
|
|
8
|
+
} from '@rango-dev/wallets-core/namespaces/common';
|
|
9
|
+
import { actions as solanaActions } from '@rango-dev/wallets-core/namespaces/solana';
|
|
10
|
+
import { builders, hooks } from '@rango-dev/wallets-core/namespaces/sui';
|
|
11
|
+
|
|
12
|
+
import { WALLET_ID, WALLET_NAME_IN_WALLET_STANDARD } from '../constants.js';
|
|
13
|
+
import { solanaPhantom } from '../utils.js';
|
|
14
|
+
|
|
15
|
+
const [changeAccountSubscriber, changeAccountCleanup] =
|
|
16
|
+
hooks.changeAccountSubscriber({
|
|
17
|
+
name: WALLET_NAME_IN_WALLET_STANDARD,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const connect = builders
|
|
21
|
+
.connect({
|
|
22
|
+
name: WALLET_NAME_IN_WALLET_STANDARD,
|
|
23
|
+
})
|
|
24
|
+
.before(changeAccountSubscriber)
|
|
25
|
+
.or(changeAccountCleanup)
|
|
26
|
+
.or(standardizeAndThrowError)
|
|
27
|
+
.build();
|
|
28
|
+
|
|
29
|
+
const disconnect = commonBuilders
|
|
30
|
+
.disconnect<SuiActions>()
|
|
31
|
+
.after(changeAccountCleanup)
|
|
32
|
+
.build();
|
|
33
|
+
|
|
34
|
+
/*
|
|
35
|
+
* TODO: We are currently using `solanaCanEagerConnectAction` to establish an eager connection to the Sui instance.
|
|
36
|
+
* This is a temporary workaround due to Phantom's limitation in silently connecting to a Sui account.
|
|
37
|
+
* Once Phantom introduces support for silent Sui connections, this implementation should be updated accordingly.
|
|
38
|
+
*/
|
|
39
|
+
const canEagerConnect = new ActionBuilder<SolanaActions, 'canEagerConnect'>(
|
|
40
|
+
'canEagerConnect'
|
|
41
|
+
)
|
|
42
|
+
.action(solanaActions.canEagerConnect(solanaPhantom))
|
|
43
|
+
.build();
|
|
44
|
+
|
|
45
|
+
const sui = new NamespaceBuilder<SuiActions>('Sui', WALLET_ID)
|
|
46
|
+
.action(connect)
|
|
47
|
+
.action(disconnect)
|
|
48
|
+
.action(canEagerConnect)
|
|
49
|
+
.build();
|
|
50
|
+
|
|
51
|
+
export { sui };
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ProviderAPI,
|
|
3
|
+
UtxoActions,
|
|
4
|
+
} from '@rango-dev/wallets-core/namespaces/utxo';
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
ActionBuilder,
|
|
8
|
+
NamespaceBuilder,
|
|
9
|
+
type Subscriber,
|
|
10
|
+
type SubscriberCleanUp,
|
|
11
|
+
} from '@rango-dev/wallets-core';
|
|
12
|
+
import {
|
|
13
|
+
type CaipAccount,
|
|
14
|
+
standardizeAndThrowError,
|
|
15
|
+
} from '@rango-dev/wallets-core/namespaces/common';
|
|
16
|
+
import { builders as commonBuilders } from '@rango-dev/wallets-core/namespaces/common';
|
|
17
|
+
import {
|
|
18
|
+
actions as solanaActions,
|
|
19
|
+
type SolanaActions,
|
|
20
|
+
} from '@rango-dev/wallets-core/namespaces/solana';
|
|
21
|
+
import {
|
|
22
|
+
builders,
|
|
23
|
+
CAIP_BITCOIN_CHAIN_ID,
|
|
24
|
+
CAIP_NAMESPACE,
|
|
25
|
+
} from '@rango-dev/wallets-core/namespaces/utxo';
|
|
26
|
+
import { CAIP } from '@rango-dev/wallets-core/utils';
|
|
27
|
+
import {
|
|
28
|
+
Networks,
|
|
29
|
+
type ProviderConnectResult,
|
|
30
|
+
} from '@rango-dev/wallets-shared';
|
|
31
|
+
|
|
32
|
+
import { WALLET_ID } from '../constants.js';
|
|
33
|
+
import { bitcoinPhantom, solanaPhantom } from '../utils.js';
|
|
34
|
+
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
36
|
+
type AnyFunction = (...args: any[]) => any;
|
|
37
|
+
|
|
38
|
+
type BtcAccount = {
|
|
39
|
+
address: string;
|
|
40
|
+
publicKey: string;
|
|
41
|
+
addressType: 'p2tr' | 'p2wpkh' | 'p2sh' | 'p2pkh';
|
|
42
|
+
purpose: 'payment' | 'ordinals';
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const getBitcoinAccounts: () => Promise<ProviderConnectResult> = async () => {
|
|
46
|
+
const instance = bitcoinPhantom();
|
|
47
|
+
const accounts = await instance.requestAccounts();
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
accounts: accounts
|
|
51
|
+
.filter((account: BtcAccount) => account.purpose === 'payment')
|
|
52
|
+
.map((account: BtcAccount) => account.address),
|
|
53
|
+
chainId: Networks.BTC,
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
function getChangeAccountSubscriber(
|
|
58
|
+
instance: () => ProviderAPI | undefined
|
|
59
|
+
): [Subscriber<UtxoActions>, SubscriberCleanUp<UtxoActions>] {
|
|
60
|
+
let eventCallback: AnyFunction;
|
|
61
|
+
|
|
62
|
+
// subscriber can be passed to `or`, it will get the error and should rethrow error to pass the error to next `or` or throw error.
|
|
63
|
+
return [
|
|
64
|
+
(context, err) => {
|
|
65
|
+
const bitcoinInstance = instance();
|
|
66
|
+
|
|
67
|
+
if (!bitcoinInstance) {
|
|
68
|
+
throw new Error(
|
|
69
|
+
'Trying to subscribe to your Solana wallet, but seems its instance is not available.'
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const [, setState] = context.state();
|
|
74
|
+
|
|
75
|
+
eventCallback = (accounts: BtcAccount[]) => {
|
|
76
|
+
/*
|
|
77
|
+
* In Phantom, when user is switching to an account which is not connected to dApp yet, it returns an empty array on `accountsChanged`.
|
|
78
|
+
* So empty array means we don't have access to account and we need to disconnect and let the user connect the account.
|
|
79
|
+
*/
|
|
80
|
+
if (!accounts.length) {
|
|
81
|
+
context.action('disconnect');
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const formatAccounts = accounts
|
|
86
|
+
.filter((account) => account.purpose === 'payment')
|
|
87
|
+
.map(
|
|
88
|
+
(account: BtcAccount) =>
|
|
89
|
+
CAIP.AccountId.format({
|
|
90
|
+
address: account.address,
|
|
91
|
+
chainId: {
|
|
92
|
+
namespace: CAIP_NAMESPACE,
|
|
93
|
+
reference: CAIP_BITCOIN_CHAIN_ID,
|
|
94
|
+
},
|
|
95
|
+
}) as CaipAccount
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
setState('accounts', formatAccounts);
|
|
99
|
+
};
|
|
100
|
+
bitcoinInstance.on('accountsChanged', eventCallback);
|
|
101
|
+
|
|
102
|
+
if (err instanceof Error) {
|
|
103
|
+
throw err;
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
(_context, err) => {
|
|
107
|
+
const bitcoinInstance = instance();
|
|
108
|
+
|
|
109
|
+
if (eventCallback && bitcoinInstance) {
|
|
110
|
+
bitcoinInstance.off('accountsChanged', eventCallback);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return err;
|
|
114
|
+
},
|
|
115
|
+
];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const [changeAccountSubscriber, changeAccountCleanup] =
|
|
119
|
+
getChangeAccountSubscriber(bitcoinPhantom);
|
|
120
|
+
|
|
121
|
+
const connect = builders
|
|
122
|
+
.connect()
|
|
123
|
+
.action(async function () {
|
|
124
|
+
const result = await getBitcoinAccounts();
|
|
125
|
+
if (Array.isArray(result)) {
|
|
126
|
+
throw new Error(
|
|
127
|
+
'Expecting bitcoin response to be a single value, not an array.'
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const formatAccounts = result.accounts.map(
|
|
132
|
+
(account) =>
|
|
133
|
+
CAIP.AccountId.format({
|
|
134
|
+
address: account,
|
|
135
|
+
chainId: {
|
|
136
|
+
namespace: CAIP_NAMESPACE,
|
|
137
|
+
reference: CAIP_BITCOIN_CHAIN_ID,
|
|
138
|
+
},
|
|
139
|
+
}) as CaipAccount
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
return formatAccounts;
|
|
143
|
+
})
|
|
144
|
+
.before(changeAccountSubscriber)
|
|
145
|
+
.or(changeAccountCleanup)
|
|
146
|
+
.or(standardizeAndThrowError)
|
|
147
|
+
.build();
|
|
148
|
+
|
|
149
|
+
const disconnect = commonBuilders
|
|
150
|
+
.disconnect<UtxoActions>()
|
|
151
|
+
.after(changeAccountCleanup)
|
|
152
|
+
.build();
|
|
153
|
+
|
|
154
|
+
/*
|
|
155
|
+
* TODO: We are currently using `solanaCanEagerConnectAction` to establish an eager connection to the BTC instance.
|
|
156
|
+
* This is a temporary workaround due to Phantom's limitation in silently connecting to a BTC account.
|
|
157
|
+
* Once Phantom introduces support for silent BTC connections, this implementation should be updated accordingly.
|
|
158
|
+
*/
|
|
159
|
+
const canEagerConnect = new ActionBuilder<SolanaActions, 'canEagerConnect'>(
|
|
160
|
+
'canEagerConnect'
|
|
161
|
+
)
|
|
162
|
+
.action(solanaActions.canEagerConnect(solanaPhantom))
|
|
163
|
+
.build();
|
|
164
|
+
|
|
165
|
+
const utxo = new NamespaceBuilder<UtxoActions>('UTXO', WALLET_ID)
|
|
166
|
+
.action(connect)
|
|
167
|
+
.action(disconnect)
|
|
168
|
+
.action(canEagerConnect)
|
|
169
|
+
.build();
|
|
170
|
+
|
|
171
|
+
export { utxo };
|