@rango-dev/provider-okx 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 +258 -0
- package/dist/builders/utxo.d.ts +12 -0
- package/dist/builders/utxo.d.ts.map +1 -0
- package/dist/constants.d.ts +6 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/legacy/index.d.ts +21 -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/utxo.d.ts +4 -0
- package/dist/namespaces/utxo.d.ts.map +1 -0
- package/dist/provider-okx.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/solana.d.ts +6 -0
- package/dist/signers/solana.d.ts.map +1 -0
- package/dist/signers/utxo.d.ts +11 -0
- package/dist/signers/utxo.d.ts.map +1 -0
- package/dist/types.d.ts +16 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils.d.ts +11 -0
- package/dist/utils.d.ts.map +1 -0
- package/package.json +33 -0
- package/readme.md +78 -0
- package/src/builders/utxo.ts +48 -0
- package/src/constants.ts +83 -0
- package/src/legacy/index.ts +137 -0
- package/src/mod.ts +12 -0
- package/src/namespaces/evm.ts +73 -0
- package/src/namespaces/solana.ts +62 -0
- package/src/namespaces/utxo.ts +51 -0
- package/src/provider.ts +26 -0
- package/src/signer.ts +30 -0
- package/src/signers/solana.ts +25 -0
- package/src/signers/utxo.ts +57 -0
- package/src/types.ts +19 -0
- package/src/utils.ts +83 -0
package/readme.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# OKX Wallet
|
|
2
|
+
|
|
3
|
+
OKX Wallet integration for hub.
|
|
4
|
+
[Homepage](https://www.okx.com/web3) | [Docs](https://www.okx.com/web3/build/docs)
|
|
5
|
+
|
|
6
|
+
More about implementation status can be found [here](../readme.md).
|
|
7
|
+
|
|
8
|
+
## Implementation notes/limitations
|
|
9
|
+
|
|
10
|
+
### Group
|
|
11
|
+
|
|
12
|
+
#### ✅ Solana
|
|
13
|
+
|
|
14
|
+
- Fully supported in this integration.
|
|
15
|
+
- No known limitations.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
#### ⚠️ EVM
|
|
20
|
+
|
|
21
|
+
Supported networks :
|
|
22
|
+
|
|
23
|
+
- Ethereum
|
|
24
|
+
- BSC
|
|
25
|
+
- Polygon
|
|
26
|
+
- Fantom
|
|
27
|
+
- Arbitrum
|
|
28
|
+
- Optimism
|
|
29
|
+
- Cronos
|
|
30
|
+
- Boba
|
|
31
|
+
- Gnosis
|
|
32
|
+
- Moonbeam
|
|
33
|
+
- Moonriver
|
|
34
|
+
- Harmony
|
|
35
|
+
- Avalanche C-Chain
|
|
36
|
+
|
|
37
|
+
#### ⚠️ UTXO Chains
|
|
38
|
+
|
|
39
|
+
The wallet only supports bitcoin.
|
|
40
|
+
|
|
41
|
+
#### 🚧 Cosmos
|
|
42
|
+
|
|
43
|
+
The wallet supports Cosmos, but it is **not implemented** in the current integration.
|
|
44
|
+
|
|
45
|
+
#### 🚧 TON
|
|
46
|
+
|
|
47
|
+
The wallet supports TON, but it is **not implemented** in the current integration.
|
|
48
|
+
|
|
49
|
+
#### 🚧 Sui
|
|
50
|
+
|
|
51
|
+
The wallet supports Sui, but it is **not implemented** in the current integration.
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
### Feature
|
|
56
|
+
|
|
57
|
+
#### ⚠️ Disconnect
|
|
58
|
+
|
|
59
|
+
The disconnect function will not work for this wallet, as it attempts to invoke disconnect on the connect method for private key imported wallets.
|
|
60
|
+
If the user disconnects the dApp from the wallet itself, we won't disconnect ourself. But we will get disconnected after refreshing the page.
|
|
61
|
+
If the user connects the wallet to one account, switches to another account, and then disconnects one of the two from the wallet, the dApp automatically switches to the remaining account that wasn’t disconnected.
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
Here’s the updated **Switch Account** section with a concise mention that connection order affects switching accounts:
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
#### ⚠️ Switch Account
|
|
70
|
+
|
|
71
|
+
**Connection order also affects this behavior** depending on which namespace was connected first, some account updates may not propagate correctly across other namespaces. As a result, previously connected namespaces will not update automatically and require a manual disconnect and reconnect to sync the correct account.
|
|
72
|
+
|
|
73
|
+
Additionally, calling `connect` on a namespace that was already connected does not return the address of the newly selected account. To avoid leaving the user in an inconsistent state, we invoke a full wallet `disconnect` when the user disconnects the wallet from the provider.
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
More wallet information can be found in [readme.md](../readme.md).
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { OkxBtcAddress } from '../types.js';
|
|
2
|
+
|
|
3
|
+
import { ActionBuilder } from '@rango-dev/wallets-core';
|
|
4
|
+
import { ChangeAccountSubscriberBuilder } from '@rango-dev/wallets-core/namespaces/common';
|
|
5
|
+
import {
|
|
6
|
+
type ProviderAPI,
|
|
7
|
+
utils,
|
|
8
|
+
type UtxoActions,
|
|
9
|
+
} from '@rango-dev/wallets-core/namespaces/utxo';
|
|
10
|
+
|
|
11
|
+
export const changeAccountSubscriber = (getInstance: () => ProviderAPI) =>
|
|
12
|
+
new ChangeAccountSubscriberBuilder<OkxBtcAddress, ProviderAPI, UtxoActions>()
|
|
13
|
+
.getInstance(getInstance)
|
|
14
|
+
/*
|
|
15
|
+
* Okx wallet may call the `changeAccount` event with `null` value
|
|
16
|
+
* but we shouldn't disconnect in this case.
|
|
17
|
+
*/
|
|
18
|
+
.onSwitchAccount((event) => {
|
|
19
|
+
if (!event.payload?.address) {
|
|
20
|
+
event.preventDefault();
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
.format(async (_, event) => {
|
|
25
|
+
return utils.formatAccountsToCAIP(event ? [event.address] : []);
|
|
26
|
+
})
|
|
27
|
+
.addEventListener((instance, callback) => {
|
|
28
|
+
return instance.addListener('accountChanged', callback);
|
|
29
|
+
})
|
|
30
|
+
.removeEventListener((instance, callback) => {
|
|
31
|
+
return instance.removeListener('accountChanged', callback);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
function canEagerConnect(getInstance: () => ProviderAPI) {
|
|
35
|
+
return new ActionBuilder<UtxoActions, 'canEagerConnect'>(
|
|
36
|
+
'canEagerConnect'
|
|
37
|
+
).action(async () => {
|
|
38
|
+
const instance = getInstance();
|
|
39
|
+
try {
|
|
40
|
+
const accounts = await instance.getAccounts();
|
|
41
|
+
return !!accounts.length;
|
|
42
|
+
} catch {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const utxoBuilders = { changeAccountSubscriber, canEagerConnect };
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
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
|
+
solanaBlockchain,
|
|
7
|
+
type TransferBlockchainMeta,
|
|
8
|
+
} from 'rango-types';
|
|
9
|
+
|
|
10
|
+
import getSigners from './signer.js';
|
|
11
|
+
import { getInstanceOrThrow } from './utils.js';
|
|
12
|
+
|
|
13
|
+
export const WALLET_ID = 'okx';
|
|
14
|
+
export const OKX_WALLET_SUPPORTED_CHAINS = [
|
|
15
|
+
LegacyNetworks.ETHEREUM,
|
|
16
|
+
LegacyNetworks.BSC,
|
|
17
|
+
LegacyNetworks.POLYGON,
|
|
18
|
+
LegacyNetworks.FANTOM,
|
|
19
|
+
LegacyNetworks.ARBITRUM,
|
|
20
|
+
LegacyNetworks.OPTIMISM,
|
|
21
|
+
LegacyNetworks.CRONOS,
|
|
22
|
+
LegacyNetworks.BOBA,
|
|
23
|
+
LegacyNetworks.GNOSIS,
|
|
24
|
+
LegacyNetworks.MOONBEAM,
|
|
25
|
+
LegacyNetworks.MOONRIVER,
|
|
26
|
+
LegacyNetworks.HARMONY,
|
|
27
|
+
LegacyNetworks.AVAX_CCHAIN,
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
export const metadata: ProviderMetadata = {
|
|
31
|
+
name: 'OKX',
|
|
32
|
+
icon: 'https://raw.githubusercontent.com/rango-exchange/assets/main/wallets/okx/icon.svg',
|
|
33
|
+
extensions: {
|
|
34
|
+
chrome:
|
|
35
|
+
'https://chrome.google.com/webstore/detail/okx-wallet/mcohilncbfahbmgdjkbpemcciiolgcge',
|
|
36
|
+
brave:
|
|
37
|
+
'https://chrome.google.com/webstore/detail/okx-wallet/mcohilncbfahbmgdjkbpemcciiolgcge',
|
|
38
|
+
firefox: 'https://addons.mozilla.org/en-US/firefox/addon/okexwallet',
|
|
39
|
+
homepage: 'https://www.okx.com/web3',
|
|
40
|
+
},
|
|
41
|
+
properties: [
|
|
42
|
+
{
|
|
43
|
+
name: 'namespaces',
|
|
44
|
+
value: {
|
|
45
|
+
selection: 'multiple',
|
|
46
|
+
data: [
|
|
47
|
+
{
|
|
48
|
+
label: 'EVM',
|
|
49
|
+
value: 'EVM',
|
|
50
|
+
id: 'ETH',
|
|
51
|
+
getSupportedChains: (allBlockchains: BlockchainMeta[]) =>
|
|
52
|
+
allBlockchains.filter((blockchainMeta) =>
|
|
53
|
+
OKX_WALLET_SUPPORTED_CHAINS.includes(
|
|
54
|
+
blockchainMeta.name as LegacyNetworks
|
|
55
|
+
)
|
|
56
|
+
),
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
label: 'Solana',
|
|
60
|
+
value: 'Solana',
|
|
61
|
+
id: 'SOLANA',
|
|
62
|
+
getSupportedChains: (allBlockchains: BlockchainMeta[]) =>
|
|
63
|
+
solanaBlockchain(allBlockchains),
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
label: 'BTC',
|
|
67
|
+
value: 'UTXO',
|
|
68
|
+
id: 'BTC',
|
|
69
|
+
getSupportedChains: (allBlockchains: BlockchainMeta[]) =>
|
|
70
|
+
allBlockchains.filter(
|
|
71
|
+
(chain): chain is TransferBlockchainMeta =>
|
|
72
|
+
chain.name === Networks.BTC
|
|
73
|
+
),
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: 'signers',
|
|
80
|
+
value: { getSigners: async () => getSigners(getInstanceOrThrow()) },
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import type { LegacyProviderInterface } from '@rango-dev/wallets-core/legacy';
|
|
2
|
+
import type {
|
|
3
|
+
CanEagerConnect,
|
|
4
|
+
CanSwitchNetwork,
|
|
5
|
+
Connect,
|
|
6
|
+
ProviderConnectResult,
|
|
7
|
+
Subscribe,
|
|
8
|
+
SwitchNetwork,
|
|
9
|
+
WalletInfo,
|
|
10
|
+
} from '@rango-dev/wallets-shared';
|
|
11
|
+
import type { BlockchainMeta, SignerFactory } from 'rango-types';
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
canEagerlyConnectToEvm,
|
|
15
|
+
canSwitchNetworkToEvm,
|
|
16
|
+
chooseInstance,
|
|
17
|
+
getEvmAccounts,
|
|
18
|
+
getSolanaAccounts,
|
|
19
|
+
Networks,
|
|
20
|
+
switchNetworkForEvm,
|
|
21
|
+
WalletTypes,
|
|
22
|
+
} from '@rango-dev/wallets-shared';
|
|
23
|
+
import { isEvmBlockchain } from 'rango-types';
|
|
24
|
+
|
|
25
|
+
import { OKX_WALLET_SUPPORTED_CHAINS } from '../constants.js';
|
|
26
|
+
import signer from '../signer.js';
|
|
27
|
+
import { okx } from '../utils.js';
|
|
28
|
+
|
|
29
|
+
const WALLET = WalletTypes.OKX;
|
|
30
|
+
|
|
31
|
+
export const config = {
|
|
32
|
+
type: WALLET,
|
|
33
|
+
defaultNetwork: Networks.ETHEREUM,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const getInstance = okx;
|
|
37
|
+
export const connect: Connect = async ({ instance, meta }) => {
|
|
38
|
+
const results: ProviderConnectResult[] = [];
|
|
39
|
+
|
|
40
|
+
const evmInstance = chooseInstance(instance, meta, Networks.ETHEREUM);
|
|
41
|
+
|
|
42
|
+
if (evmInstance) {
|
|
43
|
+
const evmResults = await getEvmAccounts(evmInstance);
|
|
44
|
+
results.push(evmResults);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const solanaResults = await getSolanaAccounts(instance);
|
|
48
|
+
|
|
49
|
+
results.push(
|
|
50
|
+
...(Array.isArray(solanaResults) ? solanaResults : [solanaResults])
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
return results;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const subscribe: Subscribe = ({ instance, updateAccounts, meta }) => {
|
|
57
|
+
const ethInstance = chooseInstance(instance, meta, Networks.ETHEREUM);
|
|
58
|
+
const handleEvmAccountsChanged = async (addresses: string[]) => {
|
|
59
|
+
const eth_chainId = meta
|
|
60
|
+
.filter(isEvmBlockchain)
|
|
61
|
+
.find((blockchain) => blockchain.name === Networks.ETHEREUM)?.chainId;
|
|
62
|
+
|
|
63
|
+
updateAccounts(addresses, eth_chainId);
|
|
64
|
+
const solanaResults = await getSolanaAccounts(instance);
|
|
65
|
+
const resultsArray = Array.isArray(solanaResults)
|
|
66
|
+
? solanaResults
|
|
67
|
+
: [solanaResults];
|
|
68
|
+
|
|
69
|
+
resultsArray.forEach(({ chainId, accounts }) => {
|
|
70
|
+
updateAccounts(accounts, chainId);
|
|
71
|
+
});
|
|
72
|
+
};
|
|
73
|
+
ethInstance?.on?.('accountsChanged', handleEvmAccountsChanged);
|
|
74
|
+
|
|
75
|
+
return () => {
|
|
76
|
+
ethInstance?.off?.('accountsChanged', handleEvmAccountsChanged);
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export const switchNetwork: SwitchNetwork = async (options) => {
|
|
81
|
+
const instance = chooseInstance(
|
|
82
|
+
options.instance,
|
|
83
|
+
options.meta,
|
|
84
|
+
options.network
|
|
85
|
+
);
|
|
86
|
+
return switchNetworkForEvm({
|
|
87
|
+
...options,
|
|
88
|
+
instance,
|
|
89
|
+
});
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export const canSwitchNetworkTo: CanSwitchNetwork = canSwitchNetworkToEvm;
|
|
93
|
+
|
|
94
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
95
|
+
type Provider = any;
|
|
96
|
+
export const getSigners: (provider: Provider) => Promise<SignerFactory> =
|
|
97
|
+
signer;
|
|
98
|
+
|
|
99
|
+
export const canEagerConnect: CanEagerConnect = async ({ instance, meta }) => {
|
|
100
|
+
const evm_instance = chooseInstance(instance, meta, Networks.ETHEREUM);
|
|
101
|
+
if (evm_instance) {
|
|
102
|
+
return canEagerlyConnectToEvm({ instance: evm_instance, meta });
|
|
103
|
+
}
|
|
104
|
+
return Promise.resolve(false);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export const getWalletInfo: (allBlockChains: BlockchainMeta[]) => WalletInfo = (
|
|
108
|
+
allBlockChains
|
|
109
|
+
) => ({
|
|
110
|
+
name: 'OKX',
|
|
111
|
+
img: 'https://raw.githubusercontent.com/rango-exchange/assets/main/wallets/okx/icon.svg',
|
|
112
|
+
installLink: {
|
|
113
|
+
CHROME:
|
|
114
|
+
'https://chrome.google.com/webstore/detail/okx-wallet/mcohilncbfahbmgdjkbpemcciiolgcge',
|
|
115
|
+
BRAVE:
|
|
116
|
+
'https://chrome.google.com/webstore/detail/okx-wallet/mcohilncbfahbmgdjkbpemcciiolgcge',
|
|
117
|
+
FIREFOX: 'https://addons.mozilla.org/en-US/firefox/addon/okexwallet',
|
|
118
|
+
DEFAULT: 'https://www.okx.com/web3',
|
|
119
|
+
},
|
|
120
|
+
color: 'white',
|
|
121
|
+
supportedChains: allBlockChains.filter((blockchainMeta) =>
|
|
122
|
+
OKX_WALLET_SUPPORTED_CHAINS.includes(blockchainMeta.name as Networks)
|
|
123
|
+
),
|
|
124
|
+
});
|
|
125
|
+
const buildLegacyProvider: () => LegacyProviderInterface = () => ({
|
|
126
|
+
config,
|
|
127
|
+
getInstance,
|
|
128
|
+
connect,
|
|
129
|
+
subscribe,
|
|
130
|
+
switchNetwork,
|
|
131
|
+
canSwitchNetworkTo,
|
|
132
|
+
getSigners,
|
|
133
|
+
getWalletInfo,
|
|
134
|
+
canEagerConnect,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
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,73 @@
|
|
|
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
|
+
utils,
|
|
12
|
+
} from '@rango-dev/wallets-core/namespaces/evm';
|
|
13
|
+
|
|
14
|
+
import { WALLET_ID } from '../constants.js';
|
|
15
|
+
import { evmOKX } from '../utils.js';
|
|
16
|
+
|
|
17
|
+
const [changeAccountSubscriber, changeAccountCleanup] = builders
|
|
18
|
+
.changeAccountSubscriber(evmOKX)
|
|
19
|
+
/*
|
|
20
|
+
* Okx wallet may call the `changeAccount` event with `null` value
|
|
21
|
+
* but we shouldn't disconnect in this case.
|
|
22
|
+
*/
|
|
23
|
+
.onSwitchAccount((event) => {
|
|
24
|
+
if (!event.payload.length) {
|
|
25
|
+
event.preventDefault();
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
.format(async (instance, event) => {
|
|
29
|
+
const chainId = await instance.request({ method: 'eth_chainId' });
|
|
30
|
+
// The wallet may emit a `null` value in its `sent` event.
|
|
31
|
+
return utils.formatAccountsToCAIP(event.filter(Boolean), chainId);
|
|
32
|
+
})
|
|
33
|
+
.build();
|
|
34
|
+
const connect = builders
|
|
35
|
+
.connect()
|
|
36
|
+
.action(actions.connect(evmOKX))
|
|
37
|
+
.before(changeAccountSubscriber)
|
|
38
|
+
.or(changeAccountCleanup)
|
|
39
|
+
.or(standardizeAndThrowError)
|
|
40
|
+
.build();
|
|
41
|
+
|
|
42
|
+
const disconnect = commonBuilders
|
|
43
|
+
.disconnect<EvmActions>()
|
|
44
|
+
.before(() => {
|
|
45
|
+
evmOKX().disconnect();
|
|
46
|
+
})
|
|
47
|
+
.after(changeAccountCleanup)
|
|
48
|
+
.build();
|
|
49
|
+
|
|
50
|
+
const canSwitchNetwork = builders
|
|
51
|
+
.canSwitchNetwork()
|
|
52
|
+
.action(actions.canSwitchNetwork())
|
|
53
|
+
.build();
|
|
54
|
+
|
|
55
|
+
const canEagerConnect = builders
|
|
56
|
+
.canEagerConnect()
|
|
57
|
+
.action(actions.canEagerConnect(evmOKX))
|
|
58
|
+
.build();
|
|
59
|
+
|
|
60
|
+
const getChainId = builders
|
|
61
|
+
.getChainId()
|
|
62
|
+
.action(actions.getChainId(evmOKX))
|
|
63
|
+
.build();
|
|
64
|
+
|
|
65
|
+
const evm = new NamespaceBuilder<EvmActions>('EVM', WALLET_ID)
|
|
66
|
+
.action(connect)
|
|
67
|
+
.action(disconnect)
|
|
68
|
+
.action(canEagerConnect)
|
|
69
|
+
.action(canSwitchNetwork)
|
|
70
|
+
.action(getChainId)
|
|
71
|
+
.build();
|
|
72
|
+
|
|
73
|
+
export { evm };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { SolanaActions } from '@rango-dev/wallets-core/namespaces/solana';
|
|
2
|
+
|
|
3
|
+
import { ActionBuilder, 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
|
+
utils,
|
|
12
|
+
} from '@rango-dev/wallets-core/namespaces/solana';
|
|
13
|
+
|
|
14
|
+
import { WALLET_ID } from '../constants.js';
|
|
15
|
+
import { solanaOKX } from '../utils.js';
|
|
16
|
+
|
|
17
|
+
const [changeAccountSubscriber, changeAccountCleanup] = builders
|
|
18
|
+
.changeAccountSubscriber(solanaOKX)
|
|
19
|
+
/*
|
|
20
|
+
* Okx wallet may call the `changeAccount` event with `null` value
|
|
21
|
+
* but we shouldn't disconnect in this case.
|
|
22
|
+
*/
|
|
23
|
+
.onSwitchAccount((event) => {
|
|
24
|
+
if (!event.payload) {
|
|
25
|
+
event.preventDefault();
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
.format(async (_, event) => {
|
|
29
|
+
// The wallet may emit a `null` value in its `sent` event.
|
|
30
|
+
return utils.formatAccountsToCAIP(event ? [event] : []);
|
|
31
|
+
})
|
|
32
|
+
.build();
|
|
33
|
+
|
|
34
|
+
const connect = builders
|
|
35
|
+
.connect()
|
|
36
|
+
.action(actions.connect(solanaOKX))
|
|
37
|
+
.before(changeAccountSubscriber)
|
|
38
|
+
.or(changeAccountCleanup)
|
|
39
|
+
.or(standardizeAndThrowError)
|
|
40
|
+
.build();
|
|
41
|
+
|
|
42
|
+
const disconnect = commonBuilders
|
|
43
|
+
.disconnect<SolanaActions>()
|
|
44
|
+
.before(() => {
|
|
45
|
+
solanaOKX().disconnect();
|
|
46
|
+
})
|
|
47
|
+
.after(changeAccountCleanup)
|
|
48
|
+
.build();
|
|
49
|
+
|
|
50
|
+
const canEagerConnect = new ActionBuilder<SolanaActions, 'canEagerConnect'>(
|
|
51
|
+
'canEagerConnect'
|
|
52
|
+
)
|
|
53
|
+
.action(actions.canEagerConnect(solanaOKX))
|
|
54
|
+
.build();
|
|
55
|
+
|
|
56
|
+
const solana = new NamespaceBuilder<SolanaActions>('Solana', WALLET_ID)
|
|
57
|
+
.action(connect)
|
|
58
|
+
.action(disconnect)
|
|
59
|
+
.action(canEagerConnect)
|
|
60
|
+
.build();
|
|
61
|
+
|
|
62
|
+
export { solana };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { NamespaceBuilder } from '@rango-dev/wallets-core';
|
|
2
|
+
import {
|
|
3
|
+
builders as commonBuilders,
|
|
4
|
+
standardizeAndThrowError,
|
|
5
|
+
} from '@rango-dev/wallets-core/namespaces/common';
|
|
6
|
+
import {
|
|
7
|
+
utils,
|
|
8
|
+
type UtxoActions,
|
|
9
|
+
} from '@rango-dev/wallets-core/namespaces/utxo';
|
|
10
|
+
import { builders } from '@rango-dev/wallets-core/namespaces/utxo';
|
|
11
|
+
|
|
12
|
+
import { utxoBuilders } from '../builders/utxo.js';
|
|
13
|
+
import { WALLET_ID } from '../constants.js';
|
|
14
|
+
import { bitcoinOKX, getBitcoinAccounts } from '../utils.js';
|
|
15
|
+
|
|
16
|
+
const [changeAccountSubscriber, changeAccountCleanup] = utxoBuilders
|
|
17
|
+
.changeAccountSubscriber(bitcoinOKX)
|
|
18
|
+
.build();
|
|
19
|
+
|
|
20
|
+
const canEagerConnect = utxoBuilders.canEagerConnect(bitcoinOKX).build();
|
|
21
|
+
|
|
22
|
+
const connect = builders
|
|
23
|
+
.connect()
|
|
24
|
+
.action(async function () {
|
|
25
|
+
const accountsResult = await getBitcoinAccounts();
|
|
26
|
+
|
|
27
|
+
if (!accountsResult?.address) {
|
|
28
|
+
throw new Error("Couldn't find any address!");
|
|
29
|
+
}
|
|
30
|
+
return utils.formatAccountsToCAIP([accountsResult.address]);
|
|
31
|
+
})
|
|
32
|
+
.before(changeAccountSubscriber)
|
|
33
|
+
.or(changeAccountCleanup)
|
|
34
|
+
.or(standardizeAndThrowError)
|
|
35
|
+
.build();
|
|
36
|
+
|
|
37
|
+
const disconnect = commonBuilders
|
|
38
|
+
.disconnect<UtxoActions>()
|
|
39
|
+
.before(() => {
|
|
40
|
+
bitcoinOKX().disconnect();
|
|
41
|
+
})
|
|
42
|
+
.after(changeAccountCleanup)
|
|
43
|
+
.build();
|
|
44
|
+
|
|
45
|
+
const utxo = new NamespaceBuilder<UtxoActions>('UTXO', WALLET_ID)
|
|
46
|
+
.action(connect)
|
|
47
|
+
.action(disconnect)
|
|
48
|
+
.action(canEagerConnect)
|
|
49
|
+
.build();
|
|
50
|
+
|
|
51
|
+
export { utxo };
|
package/src/provider.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ProviderBuilder } from '@rango-dev/wallets-core';
|
|
2
|
+
|
|
3
|
+
import { metadata, WALLET_ID } from './constants.js';
|
|
4
|
+
import { evm } from './namespaces/evm.js';
|
|
5
|
+
import { solana } from './namespaces/solana.js';
|
|
6
|
+
import { utxo } from './namespaces/utxo.js';
|
|
7
|
+
import { okx as okxInstance } from './utils.js';
|
|
8
|
+
|
|
9
|
+
const buildProvider = () =>
|
|
10
|
+
new ProviderBuilder(WALLET_ID)
|
|
11
|
+
.init(function (context) {
|
|
12
|
+
const [, setState] = context.state();
|
|
13
|
+
|
|
14
|
+
if (okxInstance()) {
|
|
15
|
+
setState('installed', true);
|
|
16
|
+
console.debug('[okx] instance detected.', context);
|
|
17
|
+
}
|
|
18
|
+
})
|
|
19
|
+
.config('metadata', metadata)
|
|
20
|
+
.add('solana', solana)
|
|
21
|
+
.add('evm', evm)
|
|
22
|
+
.add('utxo', utxo)
|
|
23
|
+
|
|
24
|
+
.build();
|
|
25
|
+
|
|
26
|
+
export { buildProvider };
|
package/src/signer.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Provider } from './types.js';
|
|
2
|
+
import type { SignerFactory } from 'rango-types';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
dynamicImportWithRefinedError,
|
|
6
|
+
getNetworkInstance,
|
|
7
|
+
Networks,
|
|
8
|
+
} from '@rango-dev/wallets-shared';
|
|
9
|
+
import { DefaultSignerFactory, TransactionType as TxType } from 'rango-types';
|
|
10
|
+
|
|
11
|
+
import { OKXSolanaSigner } from './signers/solana.js';
|
|
12
|
+
import { OKXUTXOSigner } from './signers/utxo.js';
|
|
13
|
+
|
|
14
|
+
export default async function getSigners(
|
|
15
|
+
provider: Provider
|
|
16
|
+
): Promise<SignerFactory> {
|
|
17
|
+
const ethProvider = getNetworkInstance(provider, Networks.ETHEREUM);
|
|
18
|
+
const solProvider = getNetworkInstance(provider, Networks.SOLANA);
|
|
19
|
+
const utxoProvider = getNetworkInstance(provider, Networks.BTC);
|
|
20
|
+
|
|
21
|
+
const signers = new DefaultSignerFactory();
|
|
22
|
+
const { DefaultEvmSigner } = await dynamicImportWithRefinedError(
|
|
23
|
+
async () => await import('@rango-dev/signer-evm')
|
|
24
|
+
);
|
|
25
|
+
signers.registerSigner(TxType.EVM, new DefaultEvmSigner(ethProvider));
|
|
26
|
+
signers.registerSigner(TxType.SOLANA, new OKXSolanaSigner(solProvider));
|
|
27
|
+
signers.registerSigner(TxType.TRANSFER, new OKXUTXOSigner(utxoProvider));
|
|
28
|
+
|
|
29
|
+
return signers;
|
|
30
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DefaultSolanaSigner,
|
|
3
|
+
type SolanaExternalProvider,
|
|
4
|
+
} from '@rango-dev/signer-solana';
|
|
5
|
+
import { SignerError, SignerErrorCode } from 'rango-types';
|
|
6
|
+
|
|
7
|
+
export class OKXSolanaSigner extends DefaultSolanaSigner {
|
|
8
|
+
constructor(provider: SolanaExternalProvider) {
|
|
9
|
+
super(provider);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async signMessage(msg: string): Promise<string> {
|
|
13
|
+
try {
|
|
14
|
+
const { signature } = await this.provider.request({
|
|
15
|
+
method: 'signMessage',
|
|
16
|
+
params: {
|
|
17
|
+
message: msg,
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
return signature;
|
|
21
|
+
} catch (error) {
|
|
22
|
+
throw new SignerError(SignerErrorCode.SIGN_TX_ERROR, undefined, error);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { ProviderAPI } from '@rango-dev/wallets-core/namespaces/utxo';
|
|
2
|
+
import type { GenericSigner, Transfer } from 'rango-types';
|
|
3
|
+
|
|
4
|
+
import { Networks } from '@rango-dev/wallets-shared';
|
|
5
|
+
import { SignerError } from 'rango-types';
|
|
6
|
+
|
|
7
|
+
export class OKXUTXOSigner implements GenericSigner<Transfer> {
|
|
8
|
+
private provider: ProviderAPI;
|
|
9
|
+
constructor(provider: ProviderAPI) {
|
|
10
|
+
this.provider = provider;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async signMessage(): Promise<string> {
|
|
14
|
+
throw SignerError.UnimplementedError('signMessage');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async signAndSendTx(tx: Transfer): Promise<{ hash: string }> {
|
|
18
|
+
const { asset, psbt } = tx;
|
|
19
|
+
if (!psbt) {
|
|
20
|
+
throw new Error(
|
|
21
|
+
'No PSBT found to sign. Ensure a valid PSBT is provided.'
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (asset.blockchain !== Networks.BTC) {
|
|
26
|
+
throw new Error(
|
|
27
|
+
`Signing ${asset.blockchain} transaction is not implemented by the signer.`
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
// 1. Decode Base64 to hex
|
|
31
|
+
const psbtHex = Buffer.from(psbt.unsignedPsbtBase64, 'base64').toString(
|
|
32
|
+
'hex'
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
// 2. Build UniSat inputs
|
|
36
|
+
const toSignInputs = psbt.inputsToSign.flatMap(
|
|
37
|
+
({ address, signingIndexes }) =>
|
|
38
|
+
signingIndexes.map((index) => ({ index, address }))
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
// 3. Sign (& auto-finalize)
|
|
43
|
+
const signedPsbtHex = await this.provider.signPsbt(psbtHex, {
|
|
44
|
+
autoFinalized: true,
|
|
45
|
+
toSignInputs,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// 4. Push signed psbt
|
|
49
|
+
const transactionHash = await this.provider.pushPsbt(signedPsbtHex);
|
|
50
|
+
|
|
51
|
+
return { hash: transactionHash };
|
|
52
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
53
|
+
} catch (error: any) {
|
|
54
|
+
throw new Error(error?.message || 'Error during signing transaction');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|