Skip to content

feat: migrate Safepal to use Hub #986

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: next
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions wallets/core/src/namespaces/evm/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export async function switchOrAddNetwork(
* This error code indicates that the chain has not been added to wallet.
*/
await suggestNetwork(instance, chain);
return;
}
throw switchError;
}
Expand Down
4 changes: 2 additions & 2 deletions wallets/provider-all/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import * as okx from '@rango-dev/provider-okx';
import { versions as phantom } from '@rango-dev/provider-phantom';
import { versions as rabby } from '@rango-dev/provider-rabby';
import * as safe from '@rango-dev/provider-safe';
import * as safepal from '@rango-dev/provider-safepal';
import { versions as safepal } from '@rango-dev/provider-safepal';
import { versions as slush } from '@rango-dev/provider-slush';
import * as solflare from '@rango-dev/provider-solflare';
import * as taho from '@rango-dev/provider-taho';
Expand Down Expand Up @@ -110,7 +110,7 @@ export const allProviders = (
lazyProvider(legacyProviderImportsToVersionsInterface(enkrypt)),
lazyProvider(legacyProviderImportsToVersionsInterface(xdefi)),
lazyProvider(legacyProviderImportsToVersionsInterface(clover)),
lazyProvider(legacyProviderImportsToVersionsInterface(safepal)),
safepal,
lazyProvider(legacyProviderImportsToVersionsInterface(brave)),
lazyProvider(legacyProviderImportsToVersionsInterface(coin98)),
lazyProvider(legacyProviderImportsToVersionsInterface(coinbase)),
Expand Down
12 changes: 6 additions & 6 deletions wallets/provider-safepal/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@
"version": "0.47.1-next.3",
"license": "MIT",
"type": "module",
"source": "./src/index.ts",
"main": "./dist/index.js",
"source": "./src/mod.ts",
"main": "./dist/mod.js",
"exports": {
".": "./dist/index.js"
".": "./dist/mod.js"
},
"typings": "dist/index.d.ts",
"typings": "dist/mod.d.ts",
"files": [
"dist",
"src"
],
"scripts": {
"build": "node ../../scripts/build/command.mjs --path wallets/provider-safepal",
"build": "node ../../scripts/build/command.mjs --path wallets/provider-safepal --inputs src/mod.ts",
"ts-check": "tsc --declaration --emitDeclarationOnly -p ./tsconfig.json",
"clean": "rimraf dist",
"format": "prettier --write '{.,src}/**/*.{ts,tsx}'",
Expand All @@ -29,4 +29,4 @@
"publishConfig": {
"access": "public"
}
}
}
32 changes: 31 additions & 1 deletion wallets/provider-safepal/readme.md
Original file line number Diff line number Diff line change
@@ -1 +1,31 @@
# @rango-dev/provider-safepal
# SafePal
SafePal Wallet integration for hub.
[Homepage](https://www.safepal.com/) | [Docs](http://devdocs.safepal.com/Connect-wallet/Web/introduction.html)

More about implementation status can be found [here](../readme.md).

## Implementation notes/limitations

### Group

#### EVM & Solana
SafePal supports both EVM and Solana, which are implemented in this integration.
The wallet also supports **TON, Sui, Tron, and Aptos**, but these namespaces are **not yet supported**.

### Feature

#### ⚠️ Switch Account
- On **Solana**, switching accounts triggers a **full page reload**.
- Since **auto-connect is not supported**, the wallet **does not reconnect automatically** after reload.
- This results in both EVM and Solana **becoming disconnected** after switching accounts.

#### ❌ Auto Connect
- **Auto-connect is not supported**, as the wallet does **not support silent eager connection** on either EVM or Solana.
- A **popup is always triggered** when attempting to connect.
- Due to this limitation, **eager connect has been removed** from this integration.



---

More wallet information can be found in [readme.md](../readme.md).
45 changes: 45 additions & 0 deletions wallets/provider-safepal/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { type ProviderInfo } from '@rango-dev/wallets-core';
import {
type BlockchainMeta,
evmBlockchains,
solanaBlockchain,
} from 'rango-types';

export const WALLET_ID = 'safepal';
export const info: ProviderInfo = {
name: 'SafePal',
icon: 'https://raw.githubusercontent.com/rango-exchange/assets/main/wallets/safepal/icon.svg',
extensions: {
chrome:
'https://chrome.google.com/webstore/detail/safepal-extension-wallet/lgmpcpglpngdoalbgeoldeajfclnhafa',
brave:
'https://chrome.google.com/webstore/detail/safepal-extension-wallet/lgmpcpglpngdoalbgeoldeajfclnhafa',
firefox:
'https://addons.mozilla.org/en-US/firefox/addon/safepal-extension-wallet',
homepage: 'https://www.safepal.com/download',
},
properties: [
{
name: 'namespaces',
value: {
selection: 'multiple',
data: [
{
label: 'EVM',
value: 'EVM',
id: 'ETH',
getSupportedChains: (allBlockchains: BlockchainMeta[]) =>
evmBlockchains(allBlockchains),
},
{
label: 'Solana',
value: 'Solana',
id: 'SOLANA',
getSupportedChains: (allBlockchains: BlockchainMeta[]) =>
solanaBlockchain(allBlockchains),
},
],
},
},
],
};
48 changes: 0 additions & 48 deletions wallets/provider-safepal/src/helpers.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,27 @@ import type {
} from '@rango-dev/wallets-shared';
import type { BlockchainMeta, SignerFactory } from 'rango-types';

import {
type LegacyProviderInterface,
LegacyNetworks as Networks,
} from '@rango-dev/wallets-core/legacy';
import {
canSwitchNetworkToEvm,
chooseInstance,
getEvmAccounts,
Networks,
subscribeToEvm,
switchNetworkForEvm,
WalletTypes,
} from '@rango-dev/wallets-shared';
import { evmBlockchains, solanaBlockchain } from 'rango-types';

import { getNonEvmAccounts, safepal as safepal_instance } from './helpers.js';
import {
getSolanaAccounts as getNonEvmAccounts,
type Provider,
safepal as safepal_instance,
solanaSafepal,
} from '../utils.js';

import signer from './signer.js';

const WALLET = WalletTypes.SAFEPAL;
Expand All @@ -33,16 +42,18 @@ export const getInstance = safepal_instance;
export const connect: Connect = async ({ instance, meta }) => {
const ethInstance = chooseInstance(instance, meta, Networks.ETHEREUM);

let results: ProviderConnectResult[] = [];
const results: ProviderConnectResult[] = [];

if (ethInstance) {
const evmResult = await getEvmAccounts(ethInstance);
results.push(evmResult);
}

const nonEvmResults = await getNonEvmAccounts(instance);
results = [...results, ...nonEvmResults];

const solanaInstance = solanaSafepal();
if (solanaInstance) {
const solanaResults = await getNonEvmAccounts(solanaInstance);
results.push(solanaResults);
}
return results;
};

Expand All @@ -69,7 +80,8 @@ export const switchNetwork: SwitchNetwork = switchNetworkForEvm;

export const canSwitchNetworkTo: CanSwitchNetwork = canSwitchNetworkToEvm;

export const getSigners: (provider: any) => Promise<SignerFactory> = signer;
export const getSigners: (provider: Provider) => Promise<SignerFactory> =
signer;

export const getWalletInfo: (allBlockChains: BlockchainMeta[]) => WalletInfo = (
allBlockChains
Expand All @@ -90,5 +102,37 @@ export const getWalletInfo: (allBlockChains: BlockchainMeta[]) => WalletInfo = (
},
color: '#4A21EF',
supportedChains: [...evms, ...solana],
needsNamespace: {
selection: 'multiple',
data: [
{
label: 'EVM',
value: 'EVM',
id: 'ETH',
getSupportedChains: (allBlockchains: BlockchainMeta[]) =>
evmBlockchains(allBlockchains),
},
{
label: 'Solana',
value: 'Solana',
id: 'SOLANA',
getSupportedChains: (allBlockchains: BlockchainMeta[]) =>
solanaBlockchain(allBlockchains),
},
],
},
};
};

const buildLegacyProvider: () => LegacyProviderInterface = () => ({
config,
getInstance,
connect,
subscribe,
switchNetwork,
canSwitchNetworkTo,
getSigners,
getWalletInfo,
});

export { buildLegacyProvider };
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import type { Provider } from '../utils.js';
import type { SignerFactory } from 'rango-types';

import { DefaultSolanaSigner } from '@rango-dev/signer-solana';
import { getNetworkInstance, Networks } from '@rango-dev/wallets-shared';
import { LegacyNetworks as Networks } from '@rango-dev/wallets-core/legacy';
import { getNetworkInstance } from '@rango-dev/wallets-shared';
import { DefaultSignerFactory, TransactionType as TxType } from 'rango-types';

export default async function getSigners(
provider: any
provider: Provider
): Promise<SignerFactory> {
const ethProvider = getNetworkInstance(provider, Networks.ETHEREUM);
const solProvider = getNetworkInstance(provider, Networks.SOLANA);
Expand Down
12 changes: 12 additions & 0 deletions wallets/provider-safepal/src/mod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { defineVersions } from '@rango-dev/wallets-core/utils';

import { buildLegacyProvider } from './legacy/index.js';
import { buildProvider } from './provider.js';

const versions = () =>
defineVersions()
.version('0.0.0', buildLegacyProvider())
.version('1.0.0', buildProvider())
.build();

export { versions };
27 changes: 27 additions & 0 deletions wallets/provider-safepal/src/namespaces/evm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { EvmActions } from '@rango-dev/wallets-core/namespaces/evm';

import { NamespaceBuilder } from '@rango-dev/wallets-core';
import { builders as commonBuilders } from '@rango-dev/wallets-core/namespaces/common';
import { actions, builders } from '@rango-dev/wallets-core/namespaces/evm';

import { WALLET_ID } from '../constants.js';
import { evmSafepal } from '../utils.js';

const [changeAccountSubscriber, changeAccountCleanup] =
actions.changeAccountSubscriber(evmSafepal);
const connect = builders
.connect()
.action(actions.connect(evmSafepal))
.before(changeAccountSubscriber)
.or(changeAccountCleanup)
.build();
const disconnect = commonBuilders
.disconnect<EvmActions>()
.after(changeAccountCleanup)
.build();

const evm = new NamespaceBuilder<EvmActions>('EVM', WALLET_ID)
.action(connect)
.action(disconnect)
.build();
export { evm };
28 changes: 28 additions & 0 deletions wallets/provider-safepal/src/namespaces/solana.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { SolanaActions } from '@rango-dev/wallets-core/namespaces/solana';

import { NamespaceBuilder } from '@rango-dev/wallets-core';
import { builders as commonBuilders } from '@rango-dev/wallets-core/namespaces/common';
import { actions, builders } from '@rango-dev/wallets-core/namespaces/solana';

import { WALLET_ID } from '../constants.js';
import { solanaSafepal } from '../utils.js';

const [changeAccountSubscriber, changeAccountCleanup] =
actions.changeAccountSubscriber(solanaSafepal);
const connect = builders
.connect()
.action(actions.connect(solanaSafepal))
.before(changeAccountSubscriber)
.or(changeAccountCleanup)
.build();
const disconnect = commonBuilders
.disconnect<SolanaActions>()
.after(changeAccountCleanup)
.build();

const solana = new NamespaceBuilder<SolanaActions>('Solana', WALLET_ID)
.action(connect)
.action(disconnect)
.build();

export { solana };
26 changes: 26 additions & 0 deletions wallets/provider-safepal/src/provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { ProviderBuilder } from '@rango-dev/wallets-core';

import { info, WALLET_ID } from './constants.js';
import { evm } from './namespaces/evm.js';
import { solana } from './namespaces/solana.js';
import { safepal as safepalInstance } from './utils.js';

const buildProvider = () =>
new ProviderBuilder(WALLET_ID)
.init(function (context) {
// TODO: Remember to remove it.
setTimeout(() => {
const [, setState] = context.state();
if (safepalInstance()) {
setState('installed', true);
console.debug('[safepal] instance detected.', context);
}
});
})

.config('info', info)
.add('solana', solana)
.add('evm', evm)
.build();

export { buildProvider };
Loading