Skip to content
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

[TEST] Solana work-in-progress #160

Draft
wants to merge 4 commits into
base: feat/solana-secrets
Choose a base branch
from
Draft
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
39 changes: 23 additions & 16 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,27 @@
"sentry": "node sentryscript.js"
},
"dependencies": {
"@avalabs/avalanche-module": "0.0.0-feat-solana-address-resolution-20250205161605",
"@avalabs/avalanche-module": "0.0.0-feat-solana-sign-send-tx-20250226150841",
"@avalabs/avalanchejs": "4.2.0-alpha.1",
"@avalabs/bitcoin-module": "0.0.0-feat-solana-address-resolution-20250205161605",
"@avalabs/bitcoin-module": "0.0.0-feat-solana-sign-send-tx-20250226150841",
"@avalabs/bridge-unified": "4.0.1",
"@avalabs/core-bridge-sdk": "3.1.0-alpha.37",
"@avalabs/core-chains-sdk": "3.1.0-alpha.37",
"@avalabs/core-coingecko-sdk": "3.1.0-alpha.37",
"@avalabs/core-covalent-sdk": "3.1.0-alpha.37",
"@avalabs/core-etherscan-sdk": "3.1.0-alpha.37",
"@avalabs/core-bridge-sdk": "3.1.0-canary.4a6f312.0",
"@avalabs/core-chains-sdk": "3.1.0-canary.4a6f312.0",
"@avalabs/core-coingecko-sdk": "3.1.0-canary.4a6f312.0",
"@avalabs/core-covalent-sdk": "3.1.0-canary.4a6f312.0",
"@avalabs/core-etherscan-sdk": "3.1.0-canary.4a6f312.0",
"@avalabs/core-k2-components": "4.18.0-alpha.53",
"@avalabs/core-snowtrace-sdk": "3.1.0-alpha.37",
"@avalabs/core-token-prices-sdk": "3.1.0-alpha.37",
"@avalabs/core-utils-sdk": "3.1.0-alpha.37",
"@avalabs/core-wallets-sdk": "3.1.0-alpha.37",
"@avalabs/evm-module": "0.0.0-feat-solana-address-resolution-20250205161605",
"@avalabs/glacier-sdk": "3.1.0-alpha.37",
"@avalabs/core-snowtrace-sdk": "3.1.0-canary.4a6f312.0",
"@avalabs/core-token-prices-sdk": "3.1.0-canary.4a6f312.0",
"@avalabs/core-utils-sdk": "3.1.0-canary.4a6f312.0",
"@avalabs/core-wallets-sdk": "3.1.0-canary.4a6f312.0",
"@avalabs/evm-module": "0.0.0-feat-solana-sign-send-tx-20250226150841",
"@avalabs/glacier-sdk": "3.1.0-canary.4a6f312.0",
"@avalabs/hvm-module": "0.0.0-feat-solana-sign-send-tx-20250226150841",
"@avalabs/hw-app-avalanche": "0.14.1",
"@avalabs/hvm-module": "0.0.0-feat-solana-address-resolution-20250205161605",
"@avalabs/types": "3.1.0-alpha.37",
"@avalabs/vm-module-types": "0.0.0-feat-solana-address-resolution-20250205161605",
"@avalabs/svm-module": "0.0.0-feat-solana-sign-send-tx-20250226150841",
"@avalabs/types": "3.1.0-canary.4a6f312.0",
"@avalabs/vm-module-types": "0.0.0-feat-solana-sign-send-tx-20250226150841",
"@blockaid/client": "0.10.0",
"@coinbase/cbpay-js": "1.6.0",
"@cubist-labs/cubesigner-sdk": "0.3.28",
Expand All @@ -65,6 +66,11 @@
"@sentry/browser": "7.66.0",
"@sentry/integrations": "7.66.0",
"@sentry/react": "7.66.0",
"@solana-program/compute-budget": "0.6.1",
"@solana-program/system": "0.6.2",
"@solana-program/token": "0.4.1",
"@solana/web3.js": "2.0.0",
"@solana/webcrypto-ed25519-polyfill": "2.0.0",
"@walletconnect/core": "2.9.1",
"@walletconnect/sign-client": "2.9.1",
"@walletconnect/utils": "2.9.2",
Expand All @@ -91,6 +97,7 @@
"lru-cache": "7.10.1",
"micro-key-producer": "0.7.5",
"micro-signals": "2.4.0",
"micro-sol-signer": "0.5.0",
"paraswap": "5.1.0",
"qrcode.react": "1.0.1",
"react": "17.0.2",
Expand Down
1 change: 1 addition & 0 deletions src/background/services/accounts/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export interface AccountStorageItem {
addressPVM?: string;
addressCoreEth?: string;
addressHVM?: string;
addressSVM?: string;
type?: AccountType;
}

Expand Down
1 change: 1 addition & 0 deletions src/background/services/accounts/utils/mapVMAddresses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ export const mapVMAddresses = (addresses: Record<NetworkVMType, string>) =>
addressPVM: addresses[NetworkVMType.PVM] || undefined,
addressCoreEth: addresses[NetworkVMType.CoreEth] || undefined,
addressHVM: addresses[NetworkVMType.HVM] || undefined,
addressSVM: addresses[NetworkVMType.SVM] || undefined,
} as const);
3 changes: 2 additions & 1 deletion src/background/services/balances/BalancesService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ export class BalancesService {
const customTokens = Object.values(
settings.customTokens[network.chainId] ?? {},
).map((t) => ({ ...t, type: TokenType.ERC20 as const }));

const rawBalances = await module.getBalances({
// TODO: Use public key and module.getAddress instead to make this more modular
addresses: accounts
Expand All @@ -62,6 +61,8 @@ export class BalancesService {
return account.addressCoreEth;
case NetworkVMType.HVM:
return account.addressHVM;
case NetworkVMType.SVM:
return account.addressSVM;
default:
return undefined;
}
Expand Down
2 changes: 2 additions & 0 deletions src/background/services/history/HistoryService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ export class HistoryService {
return this.accountsService.activeAccount?.addressPVM;
case NetworkVMType.CoreEth:
return this.accountsService.activeAccount?.addressCoreEth;
case NetworkVMType.SVM:
return this.accountsService.activeAccount?.addressSVM;
default:
return undefined;
}
Expand Down
3 changes: 2 additions & 1 deletion src/background/services/network/NetworkService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,8 @@ export class NetworkService implements OnLock, OnStorageReady {
const [result] = await resolve(
getChainsAndTokens(
process.env.RELEASE === 'production',
process.env.TOKENLIST_OVERRIDE || '',
'https://proxy-api-dev.avax.network/tokenlist?includeSolana', // TODO: restore
// process.env.TOKENLIST_OVERRIDE || '',
),
);

Expand Down
13 changes: 13 additions & 0 deletions src/background/services/network/utils/isSolanaNetwork.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Network, ChainId } from '@avalabs/core-chains-sdk';

export function isSolanaNetwork(network: Network) {
return isSolanaChainId(network.chainId);
}

export function isSolanaChainId(chainId: number) {
return (
ChainId.SOLANA_DEVNET_ID === chainId ||
ChainId.SOLANA_MAINNET_ID === chainId ||
ChainId.SOLANA_TESTNET_ID === chainId
);
}
37 changes: 36 additions & 1 deletion src/background/services/wallet/WalletService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
WalletEvents,
WalletDetails,
SUPPORTED_PRIMARY_SECRET_TYPES,
isSolanaSigningRequest,
} from './models';
import {
MessageParams,
Expand All @@ -25,6 +26,7 @@ import {
getWalletFromMnemonic,
JsonRpcBatchInternal,
LedgerSigner,
SolanaSigner,
} from '@avalabs/core-wallets-sdk';
import { NetworkService } from '../network/NetworkService';
import { NetworkVMType } from '@avalabs/core-chains-sdk';
Expand Down Expand Up @@ -184,6 +186,23 @@ export class WalletService implements OnUnlock {
return;
}
const { secretType } = secrets;

// Solana
if (network.vmName === NetworkVMType.SVM) {
switch (secretType) {
case SecretType.Mnemonic:
return SolanaSigner.fromMnemonic(
secrets.mnemonic,
secrets.account.index,
);
case SecretType.PrivateKey:
return new SolanaSigner(Buffer.from(secrets.secret, 'hex'));
default:
throw new Error(
`Unsupported wallet type for Solana transaction: ${secretType}`,
);
}
}
// HVM
if (network.vmName === NetworkVMType.HVM) {
if (secretType === SecretType.Mnemonic) {
Expand Down Expand Up @@ -530,12 +549,27 @@ export class WalletService implements OnUnlock {
tabId?: number,
originalRequestMethod?: string,
): Promise<SigningResult> {
const wallet = await this.getWallet({ network, tabId });
const wallet = await this.getWallet({
network,
tabId,
});

if (!wallet) {
throw new Error('Wallet not found');
}

if (isSolanaSigningRequest(tx)) {
if (wallet instanceof SolanaSigner) {
const signedTx = await wallet.signTx(tx.data);

return {
signedTx,
};
}

throw new Error('Signing error, wrong network');
}

// handle BTC signing
if ('inputs' in tx) {
if (
Expand Down Expand Up @@ -710,6 +744,7 @@ export class WalletService implements OnUnlock {
evm: evmPub?.key,
xp: avmPub?.key,
ed25519: hvmPub?.key,
svm: secrets.account.addressSVM,
});
}

Expand Down
19 changes: 18 additions & 1 deletion src/background/services/wallet/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,34 @@ import {
} from '../secrets/models';
import { DistributiveOmit } from '@src/utils/distributiveomit';
import { VMABI, TransactionPayload } from 'hypersdk-client';
import { RpcMethod } from '@avalabs/vm-module-types';

export interface HVMTransactionRequest {
txPayload: TransactionPayload;
abi: VMABI;
}

export type SolanaSigningRequest = {
type:
| RpcMethod.SOLANA_SIGN_AND_SEND_TRANSACTION
| RpcMethod.SOLANA_SIGN_TRANSACTION;
data: string; // Base-64 encoded "Wire Transaction"
account: string;
};

export type SignTransactionRequest =
| TransactionRequest
| BtcTransactionRequest
| AvalancheTransactionRequest
| HVMTransactionRequest;
| HVMTransactionRequest
| SolanaSigningRequest;

export const isSolanaSigningRequest = (
sigReq: SignTransactionRequest,
): sigReq is SolanaSigningRequest =>
'type' in sigReq &&
(sigReq.type === RpcMethod.SOLANA_SIGN_AND_SEND_TRANSACTION ||
sigReq.type === RpcMethod.SOLANA_SIGN_TRANSACTION);

export interface BtcTransactionRequest {
inputs: BitcoinInputUTXO[];
Expand Down
6 changes: 6 additions & 0 deletions src/background/vmModules/ApprovalController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,12 @@ export class ApprovalController implements BatchApprovalController {
network,
action.tabId,
);
case RpcMethod.SOLANA_SIGN_AND_SEND_TRANSACTION:
return await this.#walletService.sign(
signingData,
network,
action.tabId,
);

default:
throw new Error('Unrecognized method: ' + params.request.method);
Expand Down
7 changes: 7 additions & 0 deletions src/background/vmModules/ModuleManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { BitcoinModule } from '@avalabs/bitcoin-module';
import { AvalancheModule } from '@avalabs/avalanche-module';
import { EvmModule } from '@avalabs/evm-module';
import { HvmModule } from '@avalabs/hvm-module';
import { SvmModule } from '@avalabs/svm-module';
import { ethErrors } from 'eth-rpc-errors';
import { singleton } from 'tsyringe';

Expand All @@ -19,6 +20,7 @@ import { isDevelopment } from '@src/utils/environment';
import { NetworkWithCaipId } from '../services/network/models';
import { VMModuleError } from './models';
import { ApprovalController } from './ApprovalController';

import { AvaxCaipId, BitcoinCaipId } from '@src/utils/caipConversion';

// https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-2.md
Expand Down Expand Up @@ -92,6 +94,11 @@ export class ModuleManager {
approvalController: this.#approvalController,
appInfo,
}),
new SvmModule({
environment,
approvalController: this.#approvalController,
appInfo,
}),
];
}

Expand Down
2 changes: 1 addition & 1 deletion src/components/common/header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export function Header() {
const { network } = useNetworkContext();
const address =
network && activeAccount
? getAddressForChain(network?.chainId, activeAccount)
? getAddressForChain(network?.chainId, activeAccount, network.caipId)
: '';
const theme = useTheme();

Expand Down
7 changes: 6 additions & 1 deletion src/contexts/BalancesProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -330,12 +330,17 @@ export function BalancesProvider({ children }: { children: any }) {
}

const chainId = (lookupNetwork ?? network)?.chainId;
const caip2Id = (lookupNetwork ?? network)?.caipId;

if (!chainId) {
return;
}

const addressForChain = getAddressForChain(chainId, activeAccount);
const addressForChain = getAddressForChain(
chainId,
activeAccount,
caip2Id,
);

if (!addressForChain) {
return;
Expand Down
6 changes: 5 additions & 1 deletion src/hooks/useNfts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ export const useNfts = () => {
if (!network || !balances.nfts || !activeAccount) {
return [];
}
const userAddress = getAddressForChain(network.chainId, activeAccount);
const userAddress = getAddressForChain(
network.chainId,
activeAccount,
network.caipId,
);

if (!userAddress) {
return [];
Expand Down
2 changes: 2 additions & 0 deletions src/localization/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -812,6 +812,8 @@
"Skip": "Skip",
"Slippage tolerance": "Slippage tolerance",
"Slow": "Slow",
"Solana": "Solana",
"Solana Address": "Solana Address",
"Some of the required parameters are missing.": "Some of the required parameters are missing.",
"Something Went Wrong": "Something Went Wrong",
"Something bad happened please try again later!": "Something bad happened please try again later!",
Expand Down
14 changes: 14 additions & 0 deletions src/pages/Accounts/AccountDetailsView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,20 @@ export const AccountDetailsView = () => {
)}
/>
)}
{account.addressSVM && (
<AccountDetailsAddressRow
data-testid="account-address-solana"
icon={
<BitcoinColorIcon size={32} />
} /* TODO: This should be a Solana icon */
label={t('Solana')}
address={account.addressSVM}
copyHandler={onAddressCopy(
account.addressSVM,
'AccountDetailsSolanaAddressCopied',
)}
/>
)}
</Stack>
</CardContent>
</Card>
Expand Down
4 changes: 3 additions & 1 deletion src/pages/Accounts/components/AccountItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@ export const AccountItem = forwardRef(
: isManageMode && isAccountSelectable(account);
const balanceTotalUSD = useBalanceTotalInCurrency(account);
const totalBalance = (balanceTotalUSD && balanceTotalUSD.sum) ?? null;
const address = network ? getAddressForChain(network.chainId, account) : '';
const address = network
? getAddressForChain(network.chainId, account, network.caipId)
: '';
const [cardHovered, setCardHovered] = useState(false);
const itemRef = useRef<HTMLDivElement>(null);
const firstPageload = useRef(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ export function NetworkList() {
state: { pendingTransfers: unifiedBridgeTxs },
} = useUnifiedBridgeContext();

function getNetworkValue({ chainId }: Network) {
function getNetworkValue({ chainId, caip2Id }: Network) {
const networkAddress = activeAccount
? getAddressForChain(chainId, activeAccount) || ''
? getAddressForChain(chainId, activeAccount, caip2Id) || ''
: '';
const networkBalances = balances.tokens?.[chainId];
const networkAssetList = networkBalances
Expand Down
Loading
Loading