@rango-dev/provider-ctrl 0.62.1-next.2
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 +319 -0
- package/dist/actions/utxo.d.ts +17 -0
- package/dist/actions/utxo.d.ts.map +1 -0
- package/dist/builders/utxo.d.ts +21 -0
- package/dist/builders/utxo.d.ts.map +1 -0
- package/dist/constants.d.ts +23 -0
- package/dist/constants.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-ctrl.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 +13 -0
- package/dist/signers/solana.d.ts.map +1 -0
- package/dist/signers/utxo.d.ts +17 -0
- package/dist/signers/utxo.d.ts.map +1 -0
- package/dist/types.d.ts +14 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils.d.ts +25 -0
- package/dist/utils.d.ts.map +1 -0
- package/package.json +37 -0
- package/readme.md +80 -0
- package/src/actions/utxo.ts +32 -0
- package/src/builders/utxo.ts +46 -0
- package/src/constants.ts +84 -0
- package/src/mod.ts +8 -0
- package/src/namespaces/evm.ts +50 -0
- package/src/namespaces/solana.ts +39 -0
- package/src/namespaces/utxo.ts +44 -0
- package/src/provider.ts +24 -0
- package/src/signer.ts +28 -0
- package/src/signers/solana.ts +22 -0
- package/src/signers/utxo.ts +167 -0
- package/src/types.ts +18 -0
- package/src/utils.ts +141 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Provider } from '../types.js';
|
|
2
|
+
import type { GenericSigner, Transfer } from 'rango-types';
|
|
3
|
+
/**
|
|
4
|
+
* One signer for all of Ctrl's UTXO chains. BTC is signed via PSBT (`sign_psbt`);
|
|
5
|
+
* LTC/DOGE/BCH use the generic `transfer` request. It receives the whole provider
|
|
6
|
+
* map and resolves the right per-chain instance per transaction.
|
|
7
|
+
*/
|
|
8
|
+
export declare class CustomTransferSigner implements GenericSigner<Transfer> {
|
|
9
|
+
#private;
|
|
10
|
+
private provider;
|
|
11
|
+
constructor(provider: Provider);
|
|
12
|
+
signMessage(): Promise<string>;
|
|
13
|
+
signAndSendTx(tx: Transfer): Promise<{
|
|
14
|
+
hash: string;
|
|
15
|
+
}>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=utxo.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utxo.d.ts","sourceRoot":"","sources":["../../src/signers/utxo.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAkB,MAAM,aAAa,CAAC;AAE5D,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAqD3D;;;;GAIG;AACH,qBAAa,oBAAqB,YAAW,aAAa,CAAC,QAAQ,CAAC;;IAClE,OAAO,CAAC,QAAQ,CAAW;gBACf,QAAQ,EAAE,QAAQ;IAIxB,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC;IAI9B,aAAa,CAAC,EAAE,EAAE,QAAQ,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CAgG7D"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ProviderAPI as EvmProviderApi } from '@hub3js/evm';
|
|
2
|
+
import type { ProviderAPI as SolanaProviderApi } from '@hub3js/solana';
|
|
3
|
+
import type { LegacyNetworks } from '@rango-dev/wallets-core/legacy';
|
|
4
|
+
import type { ProviderAPI as UtxoProviderApi } from '@rango-dev/wallets-core/namespaces/utxo';
|
|
5
|
+
export type ProviderObject = {
|
|
6
|
+
[LegacyNetworks.ETHEREUM]: EvmProviderApi;
|
|
7
|
+
[LegacyNetworks.SOLANA]: SolanaProviderApi;
|
|
8
|
+
[LegacyNetworks.BTC]: UtxoProviderApi;
|
|
9
|
+
[LegacyNetworks.LTC]: UtxoProviderApi;
|
|
10
|
+
[LegacyNetworks.DOGE]: UtxoProviderApi;
|
|
11
|
+
[LegacyNetworks.BCH]: UtxoProviderApi;
|
|
12
|
+
};
|
|
13
|
+
export type Provider = Map<keyof ProviderObject, ProviderObject[keyof ProviderObject]>;
|
|
14
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,IAAI,cAAc,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,IAAI,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACvE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,KAAK,EAAE,WAAW,IAAI,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAE9F,MAAM,MAAM,cAAc,GAAG;IAC3B,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1C,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3C,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,eAAe,CAAC;IACtC,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,eAAe,CAAC;IACtC,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,eAAe,CAAC;IACvC,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,eAAe,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG,GAAG,CACxB,MAAM,cAAc,EACpB,cAAc,CAAC,MAAM,cAAc,CAAC,CACrC,CAAC"}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Provider } from './types.js';
|
|
2
|
+
import type { ProviderAPI as EvmProviderApi } from '@hub3js/evm';
|
|
3
|
+
import type { ProviderAPI as SolanaProviderApi } from '@hub3js/solana';
|
|
4
|
+
import type { CaipAccount } from '@hub3js/std/types';
|
|
5
|
+
export declare function ctrl(): Provider | null;
|
|
6
|
+
export declare function getInstanceOrThrow(): Provider;
|
|
7
|
+
export declare function evmCtrl(): EvmProviderApi;
|
|
8
|
+
export declare function solanaCtrl(): SolanaProviderApi;
|
|
9
|
+
/**
|
|
10
|
+
* The EVM instance, used as the trigger source for UTXO account changes.
|
|
11
|
+
*
|
|
12
|
+
* Ctrl switches the active account across every chain at once but only signals it
|
|
13
|
+
* reliably through the EVM provider's `accountsChanged` (the UTXO providers emit an
|
|
14
|
+
* empty `{}` payload, and re-fetching them while disconnected opens a wallet popup).
|
|
15
|
+
* Returns an empty object when EVM isn't injected so the subscriber attaches safely.
|
|
16
|
+
*/
|
|
17
|
+
export declare function evmEventSource(): EvmProviderApi;
|
|
18
|
+
/**
|
|
19
|
+
* Fetch and CAIP-format accounts across every available UTXO chain, merged into a
|
|
20
|
+
* single array. Each account encodes its own chain via its CAIP reference, so
|
|
21
|
+
* BTC/LTC/DOGE/BCH addresses coexist in one UTXO namespace. Silent while the wallet
|
|
22
|
+
* is connected (only called on connect / switch, never on disconnect).
|
|
23
|
+
*/
|
|
24
|
+
export declare function getAllUtxoAccounts(): Promise<CaipAccount[]>;
|
|
25
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,IAAI,cAAc,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,IAAI,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACvE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAQrD,wBAAgB,IAAI,IAAI,QAAQ,GAAG,IAAI,CAiCtC;AAED,wBAAgB,kBAAkB,IAAI,QAAQ,CAQ7C;AAED,wBAAgB,OAAO,IAAI,cAAc,CAWxC;AAED,wBAAgB,UAAU,IAAI,iBAAiB,CAW9C;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,IAAI,cAAc,CAE/C;AAoBD;;;;;GAKG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CAsBjE"}
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rango-dev/provider-ctrl",
|
|
3
|
+
"version": "0.62.1-next.2",
|
|
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-ctrl --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
|
+
"@hub3js/core": "^0.60.1",
|
|
25
|
+
"@hub3js/evm": "^0.2.1",
|
|
26
|
+
"@hub3js/solana": "^0.2.1",
|
|
27
|
+
"@hub3js/std": "^0.1.1",
|
|
28
|
+
"@rango-dev/signer-evm": "^0.42.0",
|
|
29
|
+
"@rango-dev/signer-solana": "^0.48.0",
|
|
30
|
+
"@rango-dev/wallets-core": "^0.59.1-next.1",
|
|
31
|
+
"bs58": "^5.0.0",
|
|
32
|
+
"rango-types": "^0.5.0"
|
|
33
|
+
},
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
}
|
|
37
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Ctrl (formerly XDEFI)
|
|
2
|
+
|
|
3
|
+
Ctrl Wallet integration for hub.
|
|
4
|
+
[Homepage](https://ctrl.xyz/) | [Docs](https://developers.ctrl.xyz/)
|
|
5
|
+
|
|
6
|
+
More about implementation status can be found [here](../readme.md).
|
|
7
|
+
|
|
8
|
+
## Implementation notes/limitations
|
|
9
|
+
|
|
10
|
+
### Group
|
|
11
|
+
|
|
12
|
+
#### ✅ EVM
|
|
13
|
+
|
|
14
|
+
Supports the full EVM set Rango exposes (`evmBlockchains`), plus arbitrary EVM
|
|
15
|
+
chains via `wallet_addEthereumChain` / `wallet_switchEthereumChain`. Not limited to
|
|
16
|
+
a fixed list.
|
|
17
|
+
|
|
18
|
+
#### ⚠️ UTXO
|
|
19
|
+
|
|
20
|
+
A single UTXO namespace grouping **Bitcoin, Litecoin, Dogecoin, and Bitcoin Cash**
|
|
21
|
+
(this is the only multi-chain UTXO wallet in the hub). `connect` returns the
|
|
22
|
+
addresses of every available UTXO chain at once, each CAIP-encoded with its own
|
|
23
|
+
`bip122` chain id.
|
|
24
|
+
|
|
25
|
+
Signing differs per chain:
|
|
26
|
+
|
|
27
|
+
- **Bitcoin** is signed and broadcast by the wallet via `sign_psbt` (PSBT, `broadcast: true`).
|
|
28
|
+
- **Litecoin / Dogecoin / Bitcoin Cash** use the wallet's `transfer` method.
|
|
29
|
+
|
|
30
|
+
#### ✅ Solana
|
|
31
|
+
|
|
32
|
+
Supported. Message signing uses a custom signer because Ctrl exposes
|
|
33
|
+
`signMessage(bytes)` directly (rather than the `request({ method: 'signMessage' })`
|
|
34
|
+
form), then base58-encodes the signature.
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
### Feature
|
|
38
|
+
|
|
39
|
+
#### ⚠️ Switch Account
|
|
40
|
+
|
|
41
|
+
EVM and Solana use their providers' native `accountsChanged` events.
|
|
42
|
+
|
|
43
|
+
UTXO is different: Ctrl's UTXO providers emit `accountsChanged` with an **empty
|
|
44
|
+
`{}` payload**, so they can't report the new account themselves, and re-fetching
|
|
45
|
+
them while disconnected opens a wallet popup. Because Ctrl switches the active
|
|
46
|
+
account across **all** chains at once and signals it reliably on the **EVM
|
|
47
|
+
provider**, the UTXO namespace is driven off the EVM `accountsChanged`: on a switch
|
|
48
|
+
(non-empty array) it re-fetches all UTXO chains; on disconnect (empty array) it
|
|
49
|
+
disconnects. This means UTXO switch/disconnect detection relies on EVM being
|
|
50
|
+
connected alongside UTXO — always the case in practice, since Ctrl grants all chains
|
|
51
|
+
together on connect.
|
|
52
|
+
|
|
53
|
+
**Known issues — Ctrl-wallet behavior, not Rango logic, and currently not worked
|
|
54
|
+
around here:**
|
|
55
|
+
|
|
56
|
+
- **Live switching snaps back.** Ctrl pins the dApp to the account it connected with,
|
|
57
|
+
so switching the active account in the wallet (without disconnecting first) snaps
|
|
58
|
+
back to the connected one. To use a different account you must disconnect the dApp
|
|
59
|
+
(Ctrl → Connected dApps) → switch account → reconnect.
|
|
60
|
+
- **Switching to a single-namespace (private-key-imported) account is unreliable,
|
|
61
|
+
because Ctrl's UTXO providers give no usable account info and the EVM `[]` signal is
|
|
62
|
+
ambiguous (full disconnect vs. switched-to-a-non-EVM account):**
|
|
63
|
+
- **→ EVM-only account:** the BTC/UTXO namespace may stay connected (stale) instead
|
|
64
|
+
of dropping.
|
|
65
|
+
- **→ BTC-only account:** the whole wallet may disconnect instead of keeping BTC
|
|
66
|
+
connected.
|
|
67
|
+
|
|
68
|
+
#### ⚠️ Disconnect
|
|
69
|
+
|
|
70
|
+
A wallet-initiated disconnect is detected through the EVM provider's empty
|
|
71
|
+
`accountsChanged` (see Switch Account). The UTXO providers expose no usable
|
|
72
|
+
disconnect signal on their own.
|
|
73
|
+
|
|
74
|
+
#### ⚠️ Cross Browser
|
|
75
|
+
|
|
76
|
+
Ctrl is supported **only on Chromium-based browsers** (e.g. Chrome, Brave).
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
More wallet information can be found in [readme.md](../readme.md).
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Context, FunctionWithContext } from '@hub3js/core';
|
|
2
|
+
|
|
3
|
+
import { type UtxoActions } from '@rango-dev/wallets-core/namespaces/utxo';
|
|
4
|
+
|
|
5
|
+
import { getAllUtxoAccounts } from '../utils.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Connect the UTXO namespace across all of Ctrl's UTXO chains at once.
|
|
9
|
+
*
|
|
10
|
+
* Ctrl exposes a separate provider per chain (BTC/LTC/DOGE/BCH), and a UTXO account
|
|
11
|
+
* holds an address on each simultaneously (they're concurrent, not mutually exclusive
|
|
12
|
+
* like EVM networks). So we fetch every available chain's account and return one merged
|
|
13
|
+
* array where each entry is CAIP-encoded with its own `bip122` chain id — letting the
|
|
14
|
+
* single UTXO namespace represent all four chains. Throws only if no chain returns an
|
|
15
|
+
* address.
|
|
16
|
+
*/
|
|
17
|
+
export function connect(): FunctionWithContext<
|
|
18
|
+
UtxoActions['connect'],
|
|
19
|
+
Context
|
|
20
|
+
> {
|
|
21
|
+
return async () => {
|
|
22
|
+
const accounts = await getAllUtxoAccounts();
|
|
23
|
+
|
|
24
|
+
if (!accounts.length) {
|
|
25
|
+
throw new Error("Couldn't find any address!");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return accounts;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const utxoActions = { connect };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ProviderAPI as EvmProviderApi } from '@hub3js/evm';
|
|
2
|
+
|
|
3
|
+
import { ActionBuilder } from '@hub3js/core';
|
|
4
|
+
import { ChangeAccountSubscriberBuilder } from '@hub3js/std/hooks';
|
|
5
|
+
import { type UtxoActions } from '@rango-dev/wallets-core/namespaces/utxo';
|
|
6
|
+
|
|
7
|
+
import { getAllUtxoAccounts } from '../utils.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Ctrl switches the active account across all chains at once, but its UTXO providers
|
|
11
|
+
* emit an empty `accountsChanged` payload (`{}`) and re-fetching them while
|
|
12
|
+
* disconnected opens a wallet popup — so they can't drive the update themselves. The
|
|
13
|
+
* EVM provider, however, emits a proper `accountsChanged`: a non-empty array on
|
|
14
|
+
* switch and an empty array on disconnect. We therefore drive UTXO off the EVM
|
|
15
|
+
* signal: re-fetch all UTXO chains on a switch (silent while connected), and
|
|
16
|
+
* disconnect WITHOUT re-fetching (so no popup) when EVM reports empty.
|
|
17
|
+
*/
|
|
18
|
+
export const changeAccountSubscriber = (
|
|
19
|
+
getInstance: () => EvmProviderApi
|
|
20
|
+
): ChangeAccountSubscriberBuilder<string[], EvmProviderApi, UtxoActions> =>
|
|
21
|
+
new ChangeAccountSubscriberBuilder<string[], EvmProviderApi, UtxoActions>()
|
|
22
|
+
.getInstance(getInstance)
|
|
23
|
+
.onSwitchAccount((event, context) => {
|
|
24
|
+
if (!event.payload?.length) {
|
|
25
|
+
context.action('disconnect');
|
|
26
|
+
event.preventDefault();
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
.format(async () => getAllUtxoAccounts())
|
|
30
|
+
.addEventListener((instance, callback) => {
|
|
31
|
+
instance.on?.('accountsChanged', callback);
|
|
32
|
+
})
|
|
33
|
+
.removeEventListener((instance, callback) => {
|
|
34
|
+
instance.removeListener?.('accountsChanged', callback);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
function canEagerConnect() {
|
|
38
|
+
return new ActionBuilder<UtxoActions, 'canEagerConnect'>(
|
|
39
|
+
'canEagerConnect'
|
|
40
|
+
).action(async () => {
|
|
41
|
+
const accounts = await getAllUtxoAccounts().catch(() => []);
|
|
42
|
+
return accounts.length > 0;
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const utxoBuilders = { changeAccountSubscriber, canEagerConnect };
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { ProviderMetadata } from '@hub3js/core';
|
|
2
|
+
|
|
3
|
+
import { LegacyNetworks } from '@rango-dev/wallets-core/legacy';
|
|
4
|
+
import {
|
|
5
|
+
CAIP_BITCOIN_CHAIN_ID,
|
|
6
|
+
CAIP_BITCOINCASH_CHAIN_ID,
|
|
7
|
+
CAIP_DOGECOIN_CHAIN_ID,
|
|
8
|
+
CAIP_LITECOIN_CHAIN_ID,
|
|
9
|
+
} from '@rango-dev/wallets-core/namespaces/utxo';
|
|
10
|
+
import {
|
|
11
|
+
type BlockchainMeta,
|
|
12
|
+
evmBlockchains,
|
|
13
|
+
solanaBlockchain,
|
|
14
|
+
type TransferBlockchainMeta,
|
|
15
|
+
} from 'rango-types';
|
|
16
|
+
|
|
17
|
+
import getSigners from './signer.js';
|
|
18
|
+
import { getInstanceOrThrow } from './utils.js';
|
|
19
|
+
|
|
20
|
+
export const WALLET_ID = 'ctrl';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The UTXO chains Ctrl exposes, grouped under the single UTXO namespace, each paired
|
|
24
|
+
* with its CAIP-2 (bip122) reference so accounts can be self-describing.
|
|
25
|
+
*/
|
|
26
|
+
export const UTXO_CHAINS = [
|
|
27
|
+
{ network: LegacyNetworks.BTC, caip: CAIP_BITCOIN_CHAIN_ID },
|
|
28
|
+
{ network: LegacyNetworks.LTC, caip: CAIP_LITECOIN_CHAIN_ID },
|
|
29
|
+
{ network: LegacyNetworks.DOGE, caip: CAIP_DOGECOIN_CHAIN_ID },
|
|
30
|
+
{ network: LegacyNetworks.BCH, caip: CAIP_BITCOINCASH_CHAIN_ID },
|
|
31
|
+
] as const;
|
|
32
|
+
|
|
33
|
+
export const SUPPORTED_UTXO_CHAINS: string[] = UTXO_CHAINS.map(
|
|
34
|
+
(chain) => chain.network
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
export const metadata: ProviderMetadata = {
|
|
38
|
+
name: 'Ctrl',
|
|
39
|
+
icon: 'https://raw.githubusercontent.com/rango-exchange/assets/main/wallets/xdefi/icon.svg',
|
|
40
|
+
extensions: {
|
|
41
|
+
chrome:
|
|
42
|
+
'https://chromewebstore.google.com/detail/ctrl-wallet/hmeobnfnfcmdkdcmlblgagmfpfboieaf',
|
|
43
|
+
brave:
|
|
44
|
+
'https://chromewebstore.google.com/detail/ctrl-wallet/hmeobnfnfcmdkdcmlblgagmfpfboieaf',
|
|
45
|
+
homepage: 'https://ctrl.xyz/',
|
|
46
|
+
},
|
|
47
|
+
properties: [
|
|
48
|
+
{
|
|
49
|
+
name: 'namespaces',
|
|
50
|
+
value: {
|
|
51
|
+
selection: 'multiple',
|
|
52
|
+
data: [
|
|
53
|
+
{
|
|
54
|
+
label: 'EVM',
|
|
55
|
+
value: 'EVM',
|
|
56
|
+
id: 'ETH',
|
|
57
|
+
getSupportedChains: (allBlockchains: BlockchainMeta[]) =>
|
|
58
|
+
evmBlockchains(allBlockchains),
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
label: 'UTXO',
|
|
62
|
+
value: 'UTXO',
|
|
63
|
+
id: 'BTC',
|
|
64
|
+
getSupportedChains: (allBlockchains: BlockchainMeta[]) =>
|
|
65
|
+
allBlockchains.filter((chain): chain is TransferBlockchainMeta =>
|
|
66
|
+
SUPPORTED_UTXO_CHAINS.includes(chain.name)
|
|
67
|
+
),
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
label: 'Solana',
|
|
71
|
+
value: 'Solana',
|
|
72
|
+
id: 'SOLANA',
|
|
73
|
+
getSupportedChains: (allBlockchains: BlockchainMeta[]) =>
|
|
74
|
+
solanaBlockchain(allBlockchains),
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: 'signers',
|
|
81
|
+
value: { getSigners: async () => getSigners(getInstanceOrThrow()) },
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
};
|
package/src/mod.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { EvmActions } from '@hub3js/evm';
|
|
2
|
+
|
|
3
|
+
import { NamespaceBuilder } from '@hub3js/core';
|
|
4
|
+
import { actions, builders, hooks } from '@hub3js/evm';
|
|
5
|
+
import * as commonBuilders from '@hub3js/std/builders';
|
|
6
|
+
import { standardizeAndThrowError } from '@hub3js/std/operators';
|
|
7
|
+
|
|
8
|
+
import { WALLET_ID } from '../constants.js';
|
|
9
|
+
import { evmCtrl } from '../utils.js';
|
|
10
|
+
|
|
11
|
+
const [changeAccountSubscriber, changeAccountCleanup] =
|
|
12
|
+
hooks.changeAccountSubscriber(evmCtrl);
|
|
13
|
+
|
|
14
|
+
const connect = builders
|
|
15
|
+
.connect()
|
|
16
|
+
.action(actions.connect(evmCtrl))
|
|
17
|
+
.before(changeAccountSubscriber)
|
|
18
|
+
.or(changeAccountCleanup)
|
|
19
|
+
.or(standardizeAndThrowError)
|
|
20
|
+
.build();
|
|
21
|
+
|
|
22
|
+
const disconnect = commonBuilders
|
|
23
|
+
.disconnect<EvmActions>()
|
|
24
|
+
.after(changeAccountCleanup)
|
|
25
|
+
.build();
|
|
26
|
+
|
|
27
|
+
const canSwitchNetwork = builders
|
|
28
|
+
.canSwitchNetwork()
|
|
29
|
+
.action(actions.canSwitchNetwork())
|
|
30
|
+
.build();
|
|
31
|
+
|
|
32
|
+
const canEagerConnect = builders
|
|
33
|
+
.canEagerConnect()
|
|
34
|
+
.action(actions.canEagerConnect(evmCtrl))
|
|
35
|
+
.build();
|
|
36
|
+
|
|
37
|
+
const getChainId = builders
|
|
38
|
+
.getChainId()
|
|
39
|
+
.action(actions.getChainId(evmCtrl))
|
|
40
|
+
.build();
|
|
41
|
+
|
|
42
|
+
const evm = new NamespaceBuilder<EvmActions>('EVM', WALLET_ID)
|
|
43
|
+
.action(connect)
|
|
44
|
+
.action(disconnect)
|
|
45
|
+
.action(canEagerConnect)
|
|
46
|
+
.action(canSwitchNetwork)
|
|
47
|
+
.action(getChainId)
|
|
48
|
+
.build();
|
|
49
|
+
|
|
50
|
+
export { evm };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { SolanaActions } from '@hub3js/solana';
|
|
2
|
+
|
|
3
|
+
import { ActionBuilder, NamespaceBuilder } from '@hub3js/core';
|
|
4
|
+
import { actions, builders, hooks } from '@hub3js/solana';
|
|
5
|
+
import * as commonBuilders from '@hub3js/std/builders';
|
|
6
|
+
import { standardizeAndThrowError } from '@hub3js/std/operators';
|
|
7
|
+
|
|
8
|
+
import { WALLET_ID } from '../constants.js';
|
|
9
|
+
import { solanaCtrl } from '../utils.js';
|
|
10
|
+
|
|
11
|
+
const [changeAccountSubscriber, changeAccountCleanup] =
|
|
12
|
+
hooks.changeAccountSubscriber(solanaCtrl);
|
|
13
|
+
|
|
14
|
+
const connect = builders
|
|
15
|
+
.connect()
|
|
16
|
+
.action(actions.connect(solanaCtrl))
|
|
17
|
+
.before(changeAccountSubscriber)
|
|
18
|
+
.or(changeAccountCleanup)
|
|
19
|
+
.or(standardizeAndThrowError)
|
|
20
|
+
.build();
|
|
21
|
+
|
|
22
|
+
const disconnect = commonBuilders
|
|
23
|
+
.disconnect<SolanaActions>()
|
|
24
|
+
.after(changeAccountCleanup)
|
|
25
|
+
.build();
|
|
26
|
+
|
|
27
|
+
const canEagerConnect = new ActionBuilder<SolanaActions, 'canEagerConnect'>(
|
|
28
|
+
'canEagerConnect'
|
|
29
|
+
)
|
|
30
|
+
.action(actions.canEagerConnect(solanaCtrl))
|
|
31
|
+
.build();
|
|
32
|
+
|
|
33
|
+
const solana = new NamespaceBuilder<SolanaActions>('Solana', WALLET_ID)
|
|
34
|
+
.action(connect)
|
|
35
|
+
.action(disconnect)
|
|
36
|
+
.action(canEagerConnect)
|
|
37
|
+
.build();
|
|
38
|
+
|
|
39
|
+
export { solana };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { UtxoActions } from '@rango-dev/wallets-core/namespaces/utxo';
|
|
2
|
+
|
|
3
|
+
import { NamespaceBuilder } from '@hub3js/core';
|
|
4
|
+
import * as commonBuilders from '@hub3js/std/builders';
|
|
5
|
+
import { standardizeAndThrowError } from '@hub3js/std/operators';
|
|
6
|
+
import { builders } from '@rango-dev/wallets-core/namespaces/utxo';
|
|
7
|
+
|
|
8
|
+
import { utxoActions } from '../actions/utxo.js';
|
|
9
|
+
import { utxoBuilders } from '../builders/utxo.js';
|
|
10
|
+
import { WALLET_ID } from '../constants.js';
|
|
11
|
+
import { evmEventSource } from '../utils.js';
|
|
12
|
+
|
|
13
|
+
/*
|
|
14
|
+
* UTXO account changes are driven by the EVM provider's `accountsChanged` (see
|
|
15
|
+
* builders/utxo.ts) because Ctrl's UTXO providers emit no usable payload. One
|
|
16
|
+
* subscriber, listening on the EVM instance, re-fetches all UTXO chains on a switch
|
|
17
|
+
* and disconnects on empty.
|
|
18
|
+
*/
|
|
19
|
+
const [changeAccountSubscriber, changeAccountCleanup] = utxoBuilders
|
|
20
|
+
.changeAccountSubscriber(evmEventSource)
|
|
21
|
+
.build();
|
|
22
|
+
|
|
23
|
+
const connect = builders
|
|
24
|
+
.connect()
|
|
25
|
+
.action(utxoActions.connect())
|
|
26
|
+
.before(changeAccountSubscriber)
|
|
27
|
+
.or(changeAccountCleanup)
|
|
28
|
+
.or(standardizeAndThrowError)
|
|
29
|
+
.build();
|
|
30
|
+
|
|
31
|
+
const disconnect = commonBuilders
|
|
32
|
+
.disconnect<UtxoActions>()
|
|
33
|
+
.after(changeAccountCleanup)
|
|
34
|
+
.build();
|
|
35
|
+
|
|
36
|
+
const canEagerConnect = utxoBuilders.canEagerConnect().build();
|
|
37
|
+
|
|
38
|
+
const utxo = new NamespaceBuilder<UtxoActions>('UTXO', WALLET_ID)
|
|
39
|
+
.action(connect)
|
|
40
|
+
.action(disconnect)
|
|
41
|
+
.action(canEagerConnect)
|
|
42
|
+
.build();
|
|
43
|
+
|
|
44
|
+
export { utxo };
|
package/src/provider.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ProviderBuilder } from '@hub3js/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 { ctrl as ctrlInstance } from './utils.js';
|
|
8
|
+
|
|
9
|
+
const buildProvider = () =>
|
|
10
|
+
new ProviderBuilder(WALLET_ID)
|
|
11
|
+
.init(function (context) {
|
|
12
|
+
const [, setState] = context.state();
|
|
13
|
+
if (ctrlInstance()) {
|
|
14
|
+
setState('installed', true);
|
|
15
|
+
console.debug('[ctrl] instance detected.', context);
|
|
16
|
+
}
|
|
17
|
+
})
|
|
18
|
+
.config('metadata', metadata)
|
|
19
|
+
.add('evm', evm)
|
|
20
|
+
.add('utxo', utxo)
|
|
21
|
+
.add('solana', solana)
|
|
22
|
+
.build();
|
|
23
|
+
|
|
24
|
+
export { buildProvider };
|
package/src/signer.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Provider } from './types.js';
|
|
2
|
+
import type { ProviderAPI as EvmProviderApi } from '@hub3js/evm';
|
|
3
|
+
import type { SolanaExternalProvider } from '@rango-dev/signer-solana';
|
|
4
|
+
import type { SignerFactory } from 'rango-types';
|
|
5
|
+
|
|
6
|
+
import { LegacyNetworks } from '@rango-dev/wallets-core/legacy';
|
|
7
|
+
import { DefaultSignerFactory, TransactionType as TxType } from 'rango-types';
|
|
8
|
+
|
|
9
|
+
import { CustomSolanaSigner } from './signers/solana.js';
|
|
10
|
+
import { CustomTransferSigner } from './signers/utxo.js';
|
|
11
|
+
|
|
12
|
+
export default async function getSigners(
|
|
13
|
+
provider: Provider
|
|
14
|
+
): Promise<SignerFactory> {
|
|
15
|
+
const ethProvider = provider.get(LegacyNetworks.ETHEREUM) as EvmProviderApi;
|
|
16
|
+
const solProvider = provider.get(
|
|
17
|
+
LegacyNetworks.SOLANA
|
|
18
|
+
) as SolanaExternalProvider;
|
|
19
|
+
|
|
20
|
+
const signers = new DefaultSignerFactory();
|
|
21
|
+
const { DefaultEvmSigner } = await import('@rango-dev/signer-evm');
|
|
22
|
+
|
|
23
|
+
signers.registerSigner(TxType.EVM, new DefaultEvmSigner(ethProvider));
|
|
24
|
+
signers.registerSigner(TxType.SOLANA, new CustomSolanaSigner(solProvider));
|
|
25
|
+
// the transfer signer comprises several UTXO chains, so it gets the whole map
|
|
26
|
+
signers.registerSigner(TxType.TRANSFER, new CustomTransferSigner(provider));
|
|
27
|
+
return signers;
|
|
28
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { SolanaExternalProvider } from '@rango-dev/signer-solana';
|
|
2
|
+
|
|
3
|
+
import { DefaultSolanaSigner } from '@rango-dev/signer-solana';
|
|
4
|
+
import base58 from 'bs58';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Ctrl's Solana provider signs messages via a direct `signMessage(bytes)` call
|
|
8
|
+
* (the standard wallet-adapter shape). `DefaultSolanaSigner` signs through
|
|
9
|
+
* `request({ method: 'signMessage' })`, which Ctrl does not support, so we keep the
|
|
10
|
+
* direct call and inherit everything else (incl. base58 output and `signAndSendTx`).
|
|
11
|
+
*/
|
|
12
|
+
export class CustomSolanaSigner extends DefaultSolanaSigner {
|
|
13
|
+
constructor(provider: SolanaExternalProvider) {
|
|
14
|
+
super(provider);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async signMessage(msg: string): Promise<string> {
|
|
18
|
+
const encodedMessage = new TextEncoder().encode(msg);
|
|
19
|
+
const { signature } = await this.provider.signMessage(encodedMessage);
|
|
20
|
+
return base58.encode(signature);
|
|
21
|
+
}
|
|
22
|
+
}
|