diff --git a/packages/extension/package.json b/packages/extension/package.json index 8218c3344..ff7af2231 100644 --- a/packages/extension/package.json +++ b/packages/extension/package.json @@ -39,6 +39,7 @@ "@kadena/client": "^1.18.0", "@kadena/pactjs-cli": "^1.18.0", "@ledgerhq/hw-transport-webusb": "^6.29.8", + "@massalabs/massa-web3": "5.2.1-dev.20250730141858", "@metamask/eth-sig-util": "^8.2.0", "@metaplex-foundation/mpl-bubblegum": "^5.0.2", "@metaplex-foundation/umi": "^1.2.0", diff --git a/packages/extension/src/libs/background/index.ts b/packages/extension/src/libs/background/index.ts index d00f28109..ca4fdee91 100644 --- a/packages/extension/src/libs/background/index.ts +++ b/packages/extension/src/libs/background/index.ts @@ -48,6 +48,7 @@ class BackgroundHandler { [ProviderName.bitcoin]: {}, [ProviderName.kadena]: {}, [ProviderName.solana]: {}, + [ProviderName.massa]: {}, }; this.#providers = Providers; } diff --git a/packages/extension/src/libs/background/types.ts b/packages/extension/src/libs/background/types.ts index 73cc10eb9..ae2c834cd 100644 --- a/packages/extension/src/libs/background/types.ts +++ b/packages/extension/src/libs/background/types.ts @@ -3,6 +3,7 @@ import type EthereumProvider from '@/providers/ethereum'; import type PolkadotProvider from '@/providers/polkadot'; import type KadenaProvider from '@/providers/kadena'; import SolanaProvider from '@/providers/solana'; +import MassaProvider from '@/providers/massa'; export interface TabProviderType { [key: string]: Record< @@ -12,6 +13,7 @@ export interface TabProviderType { | BitcoinProvider | KadenaProvider | SolanaProvider + | MassaProvider >; } export interface ProviderType { @@ -20,7 +22,8 @@ export interface ProviderType { | typeof PolkadotProvider | typeof BitcoinProvider | typeof KadenaProvider - | typeof SolanaProvider; + | typeof SolanaProvider + | typeof MassaProvider; } export interface ExternalMessageOptions { savePersistentEvents: boolean; diff --git a/packages/extension/src/libs/tokens-state/index.ts b/packages/extension/src/libs/tokens-state/index.ts index 8feb22300..b86a5ec00 100644 --- a/packages/extension/src/libs/tokens-state/index.ts +++ b/packages/extension/src/libs/tokens-state/index.ts @@ -6,6 +6,7 @@ import { CustomToken, CustomErc20Token, TokenType, + CustomMassaToken, } from './types'; import { NetworkNames } from '@enkryptcom/types'; @@ -24,7 +25,7 @@ export class TokensState { */ async addErc20Token( chainName: NetworkNames, - token: CustomErc20Token, + token: CustomErc20Token | CustomMassaToken, ): Promise { let state: IState | null = await this.storage.get(StorageKeys.customTokens); diff --git a/packages/extension/src/libs/tokens-state/types.ts b/packages/extension/src/libs/tokens-state/types.ts index b64799858..8220ae87f 100644 --- a/packages/extension/src/libs/tokens-state/types.ts +++ b/packages/extension/src/libs/tokens-state/types.ts @@ -21,4 +21,8 @@ export interface CustomErc20Token extends CustomToken { address: `0x${string}`; } +export interface CustomMassaToken extends CustomToken { + address: `AS${string}`; +} + export type IState = Partial>; diff --git a/packages/extension/src/libs/utils/initialize-wallet.ts b/packages/extension/src/libs/utils/initialize-wallet.ts index 60d159be4..6b97b9454 100644 --- a/packages/extension/src/libs/utils/initialize-wallet.ts +++ b/packages/extension/src/libs/utils/initialize-wallet.ts @@ -4,6 +4,7 @@ import PolkadotNetworks from '@/providers/polkadot/networks'; import BitcoinNetworks from '@/providers/bitcoin/networks'; import KadenaNetworks from '@/providers/kadena/networks'; import SolanaNetworks from '@/providers/solana/networks'; +import MassaNetworks from '@/providers/massa/networks'; import { NetworkNames, WalletType } from '@enkryptcom/types'; import { getAccountsByNetworkName } from '@/libs/utils/accounts'; import BackupState from '../backup-state'; @@ -23,6 +24,9 @@ export const initAccounts = async (keyring: KeyRing) => { const ed25519sol = ( await getAccountsByNetworkName(NetworkNames.Solana) ).filter(acc => !acc.isTestWallet); + const ed25519massa = ( + await getAccountsByNetworkName(NetworkNames.Massa) + ).filter(acc => !acc.isTestWallet); if (secp256k1.length == 0) await keyring.saveNewAccount({ basePath: EthereumNetworks.ethereum.basePath, @@ -58,6 +62,13 @@ export const initAccounts = async (keyring: KeyRing) => { signerType: SolanaNetworks.solana.signer[0], walletType: WalletType.mnemonic, }); + if (ed25519massa.length == 0) + await keyring.saveNewAccount({ + basePath: MassaNetworks.mainnet.basePath, + name: 'Massa Account 1', + signerType: MassaNetworks.mainnet.signer[0], + walletType: WalletType.mnemonic, + }); }; export const onboardInitializeWallets = async (options: { mnemonic: string; diff --git a/packages/extension/src/libs/utils/networks.ts b/packages/extension/src/libs/utils/networks.ts index 608652fcc..2eccad1da 100644 --- a/packages/extension/src/libs/utils/networks.ts +++ b/packages/extension/src/libs/utils/networks.ts @@ -13,6 +13,8 @@ import Polkadot from '@/providers/polkadot/networks/polkadot'; import Bitcoin from '@/providers/bitcoin/networks/bitcoin'; import Kadena from '@/providers/kadena/networks/kadena'; import Solana from '@/providers/solana/networks/solana'; +import MassaNetworks from '@/providers/massa/networks'; +import Massa from '@/providers/massa/networks/mainnet'; const providerNetworks: Record> = { [ProviderName.ethereum]: EthereumNetworks, @@ -20,6 +22,7 @@ const providerNetworks: Record> = { [ProviderName.bitcoin]: BitcoinNetworks, [ProviderName.kadena]: KadenaNetworks, [ProviderName.solana]: SolanaNetworks, + [ProviderName.massa]: MassaNetworks, [ProviderName.enkrypt]: {}, }; const getAllNetworks = async ( @@ -34,7 +37,9 @@ const getAllNetworks = async ( .concat(Object.values(PolkadotNetworks) as BaseNetwork[]) .concat(Object.values(BitcoinNetworks) as BaseNetwork[]) .concat(Object.values(KadenaNetworks) as BaseNetwork[]) - .concat(Object.values(SolanaNetworks) as BaseNetwork[]); + .concat(Object.values(SolanaNetworks) as BaseNetwork[]) + .concat(Object.values(MassaNetworks) as BaseNetwork[]); + if (!includeCustom) { return allNetworks; } @@ -67,17 +72,20 @@ const DEFAULT_SUBSTRATE_NETWORK_NAME = NetworkNames.Polkadot; const DEFAULT_BTC_NETWORK_NAME = NetworkNames.Bitcoin; const DEFAULT_KADENA_NETWORK_NAME = NetworkNames.Kadena; const DEFAULT_SOLANA_NETWORK_NAME = NetworkNames.Solana; +const DEFAULT_MASSA_NETWORK_NAME = NetworkNames.Massa; const DEFAULT_EVM_NETWORK = Ethereum; const DEFAULT_SUBSTRATE_NETWORK = Polkadot; const DEFAULT_BTC_NETWORK = Bitcoin; const DEFAULT_KADENA_NETWORK = Kadena; const DEFAULT_SOLANA_NETWORK = Solana; +const DEFAULT_MASSA_NETWORK = Massa; const POPULAR_NAMES = [ NetworkNames.Bitcoin, NetworkNames.Ethereum, NetworkNames.Solana, + NetworkNames.Massa, NetworkNames.Matic, NetworkNames.Polkadot, NetworkNames.Binance, @@ -100,4 +108,6 @@ export { DEFAULT_KADENA_NETWORK_NAME, DEFAULT_SOLANA_NETWORK, DEFAULT_SOLANA_NETWORK_NAME, + DEFAULT_MASSA_NETWORK, + DEFAULT_MASSA_NETWORK_NAME, }; diff --git a/packages/extension/src/providers/index.ts b/packages/extension/src/providers/index.ts index f1b8e6106..90498059e 100644 --- a/packages/extension/src/providers/index.ts +++ b/packages/extension/src/providers/index.ts @@ -3,6 +3,7 @@ import PolkadotProvider from '@/providers/polkadot'; import BitcoinProvider from '@/providers/bitcoin'; import KadenaProvider from '@/providers/kadena'; import SolanaProvider from '@/providers/solana'; +import MassaProvider from '@/providers/massa'; import { ProviderName } from '@/types/provider'; export default { @@ -11,4 +12,5 @@ export default { [ProviderName.bitcoin]: BitcoinProvider, [ProviderName.kadena]: KadenaProvider, [ProviderName.solana]: SolanaProvider, + [ProviderName.massa]: MassaProvider, }; diff --git a/packages/extension/src/providers/massa/index.ts b/packages/extension/src/providers/massa/index.ts new file mode 100644 index 000000000..6ed5245a8 --- /dev/null +++ b/packages/extension/src/providers/massa/index.ts @@ -0,0 +1,92 @@ +import { BaseNetwork } from '@/types/base-network'; +import getRequestProvider, { RequestClass } from '@enkryptcom/request'; +import { MiddlewareFunction, OnMessageResponse } from '@enkryptcom/types'; +import Middlewares from './methods'; +import EventEmitter from 'eventemitter3'; +import { + BackgroundProviderInterface, + ProviderName, + ProviderRPCRequest, +} from '@/types/provider'; +import GetUIPath from '@/libs/utils/get-ui-path'; +import PublicKeyRing from '@/libs/keyring/public-keyring'; +import UIRoutes from './ui/routes/names'; +import { RoutesType } from '@/types/ui'; +import massaNetworks from './networks'; +import { NetworkNames } from '@enkryptcom/types'; + +export default class MassaProvider + extends EventEmitter + implements BackgroundProviderInterface +{ + public network: BaseNetwork; + requestProvider: RequestClass; + middlewares: MiddlewareFunction[] = []; + namespace: string; + KeyRing: PublicKeyRing; + UIRoutes: RoutesType; + toWindow: (message: string) => void; + + constructor( + toWindow: (message: string) => void, + network: BaseNetwork = massaNetworks[NetworkNames.Massa], + ) { + super(); + this.network = network; + this.toWindow = toWindow; + this.setMiddleWares(); + this.requestProvider = getRequestProvider('', this.middlewares); + this.requestProvider.on('notification', (notif: any) => { + this.sendNotification(JSON.stringify(notif)); + }); + this.namespace = ProviderName.massa; + this.KeyRing = new PublicKeyRing(); + + this.UIRoutes = UIRoutes; + } + + private setMiddleWares(): void { + this.middlewares = Middlewares(this).map(mw => mw.bind(this)); + } + + setRequestProvider(network: BaseNetwork): void { + const prevURL = new URL(this.network.node); + const newURL = new URL(network.node); + this.network = network; + if (prevURL.protocol === newURL.protocol) + this.requestProvider.changeNetwork(network.node); + else + this.requestProvider = getRequestProvider(network.node, this.middlewares); + } + + async isPersistentEvent(): Promise { + return false; + } + + async sendNotification(notif: string): Promise { + return this.toWindow(notif); + } + + request(request: ProviderRPCRequest): Promise { + return this.requestProvider + .request(request) + .then(res => { + return { + result: JSON.stringify(res), + }; + }) + .catch(e => { + return { + error: JSON.stringify(e.message), + }; + }); + } + + getUIPath(page: string): string { + return GetUIPath(page, this.namespace); + } + + getCurrentNetwork(): BaseNetwork { + return this.network; + } +} diff --git a/packages/extension/src/providers/massa/libs/activity-handlers/index.ts b/packages/extension/src/providers/massa/libs/activity-handlers/index.ts new file mode 100644 index 000000000..301f4ec93 --- /dev/null +++ b/packages/extension/src/providers/massa/libs/activity-handlers/index.ts @@ -0,0 +1,3 @@ +import MassaActivity from './massa'; + +export { MassaActivity }; diff --git a/packages/extension/src/providers/massa/libs/activity-handlers/massa.ts b/packages/extension/src/providers/massa/libs/activity-handlers/massa.ts new file mode 100644 index 000000000..8c1bbc7a9 --- /dev/null +++ b/packages/extension/src/providers/massa/libs/activity-handlers/massa.ts @@ -0,0 +1,27 @@ +import { BaseNetwork } from '@/types/base-network'; +import { Activity, ActivityStatus, ActivityType } from '@/types/activity'; +import { ActivityHandlerType } from '@/libs/activity-state/types'; +import ActivityState from '@/libs/activity-state'; + +const MassaActivity: ActivityHandlerType = async ( + network: BaseNetwork, + address: string, +): Promise => { + try { + // Get activities from local storage + const activityState = new ActivityState(); + const activities = await activityState.getAllActivities({ + address, + network: network.name, + }); + + // For now, return local activities + // In the future, this can be extended to fetch from Massa explorer API + return activities; + } catch (error) { + console.error('Error fetching Massa activities:', error); + return []; + } +}; + +export default MassaActivity; diff --git a/packages/extension/src/providers/massa/libs/api.ts b/packages/extension/src/providers/massa/libs/api.ts new file mode 100644 index 000000000..03c45f4a0 --- /dev/null +++ b/packages/extension/src/providers/massa/libs/api.ts @@ -0,0 +1,60 @@ +import { ProviderAPIInterface } from '@/types/provider'; +import { + JsonRpcPublicProvider, + MRC20, + NodeStatusInfo, + OperationStatus, +} from '@massalabs/massa-web3'; + +export default class MassaAPI extends ProviderAPIInterface { + public provider: JsonRpcPublicProvider; + public node: string; + + constructor(node: string) { + super(node); + this.node = node; + this.provider = JsonRpcPublicProvider.fromRPCUrl( + node, + ) as JsonRpcPublicProvider; + } + + public get api() { + return this; + } + + async init(): Promise {} + + async getBalance(address: string): Promise { + const [account] = await this.provider.balanceOf([address], false); + if (!account) return '0'; + return account.balance.toString(); + } + + async getBalanceMRC20(address: string, contract: string): Promise { + const mrc20 = new MRC20(this.provider, contract); + const balance = await mrc20.balanceOf(address); + return balance.toString(); + } + + async getMinimalFee(): Promise { + try { + const networkInfo = await this.provider.networkInfos(); + return networkInfo.minimalFee.toString(); + } catch (error) { + // Return a default minimal fee if network info is not available + return '10000000'; // 0.01 MAS in base units (9 decimals) + } + } + + async getTransactionStatus(opId: string): Promise { + try { + return this.provider.getOperationStatus(opId); + } catch (error) { + return null; + } + } + + async getNodeStatus(): Promise { + return this.provider.getNodeStatus(); + } +} diff --git a/packages/extension/src/providers/massa/methods/index.ts b/packages/extension/src/providers/massa/methods/index.ts new file mode 100644 index 000000000..1d3a3d8bb --- /dev/null +++ b/packages/extension/src/providers/massa/methods/index.ts @@ -0,0 +1,19 @@ +import { MiddlewareFunction } from '@enkryptcom/types'; +import { BackgroundProviderInterface } from '@/types/provider'; +import massa_getBalance from './massa_getBalance'; +import massa_getNetwork from './massa_getNetwork'; +import massa_setNetwork from './massa_setNetwork'; + +export default ( + provider: BackgroundProviderInterface, +): MiddlewareFunction[] => { + return [ + massa_getBalance, + massa_getNetwork, + massa_setNetwork, + async (request, response, next) => { + // Add any additional Massa-specific middleware logic here + return next(); + }, + ]; +}; diff --git a/packages/extension/src/providers/massa/methods/massa_getBalance.ts b/packages/extension/src/providers/massa/methods/massa_getBalance.ts new file mode 100644 index 000000000..a7ff97509 --- /dev/null +++ b/packages/extension/src/providers/massa/methods/massa_getBalance.ts @@ -0,0 +1,34 @@ +import { MiddlewareFunction } from '@enkryptcom/types'; +import { ProviderRPCRequest } from '@/types/provider'; +import { Address } from '@massalabs/massa-web3'; +import { getCustomError } from '@/libs/error'; +import MassaProvider from '..'; +import { MassaNetwork } from '../networks/massa-base'; + +const method: MiddlewareFunction = async function ( + this: MassaProvider, + payload: ProviderRPCRequest, + res, + next, +): Promise { + if (payload.method !== 'massa_getBalance') return next(); + else { + try { + const address = payload.params?.[0]; + const network = this.network as MassaNetwork; + if (!address || !network.isValidAddress(address)) { + res(getCustomError('Please enter a valid Massa address')); + return; + } + + const api = await network.api(); + const balance = await api.getBalance(address); + + res(null, balance); + } catch (error) { + res(getCustomError('Could not fetch balance from network')); + } + } +}; + +export default method; diff --git a/packages/extension/src/providers/massa/methods/massa_getNetwork.ts b/packages/extension/src/providers/massa/methods/massa_getNetwork.ts new file mode 100644 index 000000000..ac12aea67 --- /dev/null +++ b/packages/extension/src/providers/massa/methods/massa_getNetwork.ts @@ -0,0 +1,22 @@ +import { MiddlewareFunction } from '@enkryptcom/types'; +import { ProviderRPCRequest } from '@/types/provider'; +import { NetworkNames } from '@enkryptcom/types'; +import MassaProvider from '..'; + +const method: MiddlewareFunction = async function ( + this: MassaProvider, + payload: ProviderRPCRequest, + res, + next, +): Promise { + if (payload.method !== 'massa_getNetwork') return next(); + else { + try { + res(null, this.network.name); + } catch (error) { + res(null, NetworkNames.Massa); + } + } +}; + +export default method; diff --git a/packages/extension/src/providers/massa/methods/massa_setNetwork.ts b/packages/extension/src/providers/massa/methods/massa_setNetwork.ts new file mode 100644 index 000000000..d8d895d51 --- /dev/null +++ b/packages/extension/src/providers/massa/methods/massa_setNetwork.ts @@ -0,0 +1,55 @@ +import { MiddlewareFunction } from '@enkryptcom/types'; +import { ProviderRPCRequest } from '@/types/provider'; +import { getCustomError } from '@/libs/error'; +import massaNetworks from '../networks'; +import MassaProvider from '..'; + +const method: MiddlewareFunction = async function ( + this: MassaProvider, + payload: ProviderRPCRequest, + res, + next, +): Promise { + if (payload.method !== 'massa_setNetwork') return next(); + else { + try { + const networkName = payload.params?.[0]; + + if (!networkName) { + res(getCustomError('Network name is required')); + return; + } + + // Check if the network exists + const availableNetworks = Object.keys(massaNetworks); + + if (!availableNetworks.includes(networkName)) { + res( + getCustomError( + `Invalid network name. Available networks: ${availableNetworks.join(', ')}`, + ), + ); + return; + } + + // Get the network object + const network = massaNetworks[networkName as keyof typeof massaNetworks]; + + // Set the network in the provider + if (this.setRequestProvider) { + this.setRequestProvider(network); + res(null, { success: true, network: network.name }); + } else { + res(getCustomError('Network switching not supported')); + } + } catch (error) { + res( + getCustomError( + `Network switching failed: ${(error as Error).message || 'Unknown error'}`, + ), + ); + } + } +}; + +export default method; diff --git a/packages/extension/src/providers/massa/networks/buildnet.ts b/packages/extension/src/providers/massa/networks/buildnet.ts new file mode 100644 index 000000000..81c3feab8 --- /dev/null +++ b/packages/extension/src/providers/massa/networks/buildnet.ts @@ -0,0 +1,19 @@ +import { NetworkNames } from '@enkryptcom/types'; +import { MassaNetwork, createMassaNetworkOptions } from './massa-base'; +import { CHAIN_ID, PublicApiUrl } from '@massalabs/massa-web3'; + +const buildnetOptions = createMassaNetworkOptions({ + name: NetworkNames.MassaBuildnet, + name_long: 'Massa Buildnet', + blockExplorerTX: 'https://www.massexplo.com/tx/[[txHash]]?network=buildnet', + blockExplorerAddr: + 'https://www.massexplo.com/address/[[address]]?network=buildnet', + isTestNetwork: true, + node: PublicApiUrl.Buildnet, + chainId: CHAIN_ID.Buildnet, + coingeckoID: 'massa', +}); + +const buildnet = new MassaNetwork(buildnetOptions); + +export default buildnet; diff --git a/packages/extension/src/providers/massa/networks/icons/Massa_logo.webp b/packages/extension/src/providers/massa/networks/icons/Massa_logo.webp new file mode 100644 index 000000000..8ab091f59 Binary files /dev/null and b/packages/extension/src/providers/massa/networks/icons/Massa_logo.webp differ diff --git a/packages/extension/src/providers/massa/networks/index.ts b/packages/extension/src/providers/massa/networks/index.ts new file mode 100644 index 000000000..b14e25172 --- /dev/null +++ b/packages/extension/src/providers/massa/networks/index.ts @@ -0,0 +1,8 @@ +import mainnet from './mainnet'; +import buildnet from './buildnet'; +import { NetworkNames } from '@enkryptcom/types'; + +export default { + [NetworkNames.Massa]: mainnet, + [NetworkNames.MassaBuildnet]: buildnet, +}; diff --git a/packages/extension/src/providers/massa/networks/mainnet.ts b/packages/extension/src/providers/massa/networks/mainnet.ts new file mode 100644 index 000000000..adb111121 --- /dev/null +++ b/packages/extension/src/providers/massa/networks/mainnet.ts @@ -0,0 +1,18 @@ +import { NetworkNames } from '@enkryptcom/types'; +import { CHAIN_ID, PublicApiUrl } from '@massalabs/massa-web3'; +import { MassaNetwork, createMassaNetworkOptions } from './massa-base'; + +const mainnetOptions = createMassaNetworkOptions({ + name: NetworkNames.Massa, + name_long: 'Massa', + blockExplorerTX: 'https://explorer.massa.net/operation/[[txHash]]', + blockExplorerAddr: 'https://explorer.massa.net/address/[[address]]', + isTestNetwork: false, + node: PublicApiUrl.Mainnet, + chainId: CHAIN_ID.Mainnet, + coingeckoID: 'massa', +}); + +const mainnet = new MassaNetwork(mainnetOptions); + +export default mainnet; diff --git a/packages/extension/src/providers/massa/networks/massa-base.ts b/packages/extension/src/providers/massa/networks/massa-base.ts new file mode 100644 index 000000000..1158a30eb --- /dev/null +++ b/packages/extension/src/providers/massa/networks/massa-base.ts @@ -0,0 +1,289 @@ +import { NetworkNames } from '@enkryptcom/types'; +import { BaseNetwork } from '@/types/base-network'; +import MassaAPI from '../libs/api'; +import { SignerType } from '@enkryptcom/types'; +import { ProviderName } from '@/types/provider'; +import { MassaNetworkOptions } from '../types'; +import { Activity } from '@/types/activity'; +import { AssetsType } from '@/types/provider'; +import { fromBase } from '@enkryptcom/utils'; +import { formatFloatingPointValue } from '@/libs/utils/number-formatter'; +import MarketData from '@/libs/market-data'; +import BigNumber from 'bignumber.js'; +import icon from './icons/Massa_logo.webp'; +import createIcon from '../../ethereum/libs/blockies'; +import { TokensState } from '@/libs/tokens-state'; +import { + CustomErc20Token, + CustomMassaToken, + TokenType, +} from '@/libs/tokens-state/types'; +import Sparkline from '@/libs/sparkline'; +import { MassaActivity } from '../libs/activity-handlers'; +import wrapActivityHandler from '@/libs/activity-state/wrap-activity-handler'; +import { Address } from '@massalabs/massa-web3'; + +export class MassaNetwork extends BaseNetwork { + chainId: bigint; + private activityHandler?: ( + network: BaseNetwork, + address: string, + ) => Promise; + + constructor(options: MassaNetworkOptions) { + super(options); + this.chainId = options.chainId!; + this.activityHandler = + options.activityHandler || wrapActivityHandler(MassaActivity); + } + + async getAllTokens(): Promise { + return []; + } + + async getAllTokenInfo(address: string): Promise { + try { + // Get native MAS balance + const api = (await this.api()) as MassaAPI; + const balance = await api.getBalance(address); + const balanceFormatted = fromBase(balance, this.decimals); + const balanceDisplayFormatted = + formatFloatingPointValue(balanceFormatted).value; + + // Get MAS price from CoinGecko + let price = '0'; + let priceChangePercentage = 0; + let sparklineData = ''; + + try { + if (this.coingeckoID) { + const marketData = new MarketData(); + const tokenPrice = await marketData.getTokenPrice(this.coingeckoID); + const marketInfos = await marketData.getMarketData([ + this.coingeckoID, + ]); + const marketInfo = marketInfos[0]; + + if (tokenPrice) { + price = tokenPrice; + } + + if (marketInfo) { + priceChangePercentage = marketInfo.price_change_percentage_24h || 0; + if (marketInfo.sparkline_in_24h?.price) { + // Convert sparkline data to string format expected by UI + sparklineData = JSON.stringify( + marketInfo.sparkline_in_24h.price.slice(-25), + ); // Last 25 points + } + } + } + } catch (priceError) { + // Price data not available, continue with default values + } + + // Calculate USD values + const balanceUSD = new BigNumber(balanceDisplayFormatted) + .times(price) + .toNumber(); + const balanceUSDf = new BigNumber(balanceDisplayFormatted) + .times(price) + .toString(); + const priceFormatted = formatFloatingPointValue(price).value; + + const nativeTokenAsset: AssetsType = { + name: this.currencyNameLong, + symbol: this.currencyName, + icon: this.icon, + balance, + balancef: balanceDisplayFormatted, + balanceUSD, + balanceUSDf, + value: price, + valuef: priceFormatted, + decimals: this.decimals, + sparkline: sparklineData, + priceChangePercentage, + }; + + const assets = [nativeTokenAsset]; + + // Add custom tokens support + const tokensState = new TokensState(); + const customTokens = await tokensState + .getTokensByNetwork(this.name) + .then(tokens => { + const erc20Tokens = tokens.filter(token => { + if (token.type !== TokenType.ERC20) { + return false; + } + + for (const a of assets) { + if ( + a.contract && + (token as CustomMassaToken).address && + a.contract.toLowerCase() === + (token as CustomMassaToken).address.toLowerCase() + ) { + return false; + } + } + + return true; + }) as CustomMassaToken[]; + + return erc20Tokens.map( + ({ name, symbol, address, icon, decimals }) => { + return { + name, + symbol, + contract: address, + icon, + decimals, + balance: '0', // Will be fetched below + }; + }, + ); + }); + + // Fetch balances for custom tokens + const balancePromises = customTokens.map(async token => { + try { + return api.getBalanceMRC20(address, token.contract); + } catch (error) { + return '0'; + } + }); + + const balances = await Promise.all(balancePromises); + + // Update token balances with fetched values + customTokens.forEach((token, index) => { + token.balance = balances[index]; + }); + + const marketData = new MarketData(); + const marketInfos = await marketData.getMarketInfoByContracts( + customTokens.map(token => token.contract.toLowerCase()), + this.coingeckoPlatform!, + ); + + const customAssets: AssetsType[] = customTokens.map(token => { + const asset: AssetsType = { + name: token.name, + symbol: token.symbol, + balance: token.balance ?? '0', + balancef: formatFloatingPointValue( + fromBase(token.balance ?? '0', token.decimals), + ).value, + contract: token.contract, + balanceUSD: 0, + balanceUSDf: '0', + value: '0', + valuef: '0', + decimals: token.decimals, + sparkline: '', + priceChangePercentage: 0, + icon: token.icon, + }; + + // Check if token is a stablecoin (contains DAI or USD in symbol) + const isStablecoin = + token.symbol.toUpperCase().includes('DAI') || + token.symbol.toUpperCase().includes('USD'); + + if (isStablecoin) { + // Use $1 price for stablecoins + const usdBalance = new BigNumber( + fromBase(token.balance ?? '0', token.decimals), + ).times(1); + asset.balanceUSD = usdBalance.toNumber(); + asset.balanceUSDf = usdBalance.toString(); + asset.value = '1'; + asset.valuef = '1'; + asset.priceChangePercentage = 0; + } else { + // Use real market data for other tokens + const marketInfo = marketInfos[token.contract.toLowerCase()]; + + if (marketInfo) { + const usdBalance = new BigNumber( + fromBase(token.balance ?? '0', token.decimals), + ).times(marketInfo.current_price ?? 0); + asset.balanceUSD = usdBalance.toNumber(); + asset.balanceUSDf = usdBalance.toString(); + asset.value = marketInfo.current_price?.toString() ?? '0'; + asset.valuef = marketInfo.current_price?.toString() ?? '0'; + asset.sparkline = new Sparkline( + marketInfo.sparkline_in_24h.price, + 25, + ).dataValues; + asset.priceChangePercentage = + marketInfo.price_change_percentage_24h_in_currency || 0; + } + } + + return asset; + }); + + return [...assets, ...customAssets]; + } catch (error) { + return []; + } + } + + async getAllActivity(address: string): Promise { + if (this.activityHandler) { + return this.activityHandler(this, address); + } + return []; + } + + isValidAddress = (address: string): boolean => { + try { + Address.fromString(address); + } catch (error) { + return false; + } + return true; + }; +} + +// Helper function to create standard Massa network options +export function createMassaNetworkOptions(config: { + name: NetworkNames; + name_long: string; + blockExplorerTX: string; + blockExplorerAddr: string; + isTestNetwork: boolean; + node: string; + chainId: bigint; + coingeckoID?: string; +}): MassaNetworkOptions { + return { + name: config.name, + name_long: config.name_long, + homePage: 'https://massa.net/', + blockExplorerTX: config.blockExplorerTX, + blockExplorerAddr: config.blockExplorerAddr, + isTestNetwork: config.isTestNetwork, + currencyName: 'MAS', + currencyNameLong: 'Massa', + node: config.node, + icon, + decimals: 9, + signer: [SignerType.ed25519mas], + displayAddress: (address: string) => address, + provider: ProviderName.massa, + identicon: createIcon, + basePath: "m/44'/632'", + chainId: config.chainId, + coingeckoID: config.coingeckoID, + customTokens: true, + api: async () => { + const api = new MassaAPI(config.node); + await api.init(); + return api; + }, + }; +} diff --git a/packages/extension/src/providers/massa/types/index.ts b/packages/extension/src/providers/massa/types/index.ts new file mode 100644 index 000000000..08d37ad26 --- /dev/null +++ b/packages/extension/src/providers/massa/types/index.ts @@ -0,0 +1,31 @@ +import { NetworkName, CHAIN_ID } from '@massalabs/massa-web3'; +import { BaseNetworkOptions } from '@/types/base-network'; +import { ToTokenData } from '@/ui/action/types/token'; +import { BaseNetwork } from '@/types/base-network'; +import { Activity } from '@/types/activity'; + +export const MassaNetworks = NetworkName; + +export interface MassaNetworkOptions extends BaseNetworkOptions { + chainId?: (typeof CHAIN_ID)[keyof typeof CHAIN_ID]; + activityHandler?: ( + network: BaseNetwork, + address: string, + ) => Promise; +} + +export interface TxFeeInfo { + nativeValue: string; + fiatValue: string; + nativeSymbol: string; + fiatSymbol: string; +} + +export interface VerifyTransactionParams { + fromAddress: string; + fromAddressName: string; + toAddress: string; + toToken: ToTokenData; + toTokenAddress?: string; + txFee: TxFeeInfo; +} diff --git a/packages/extension/src/providers/massa/ui/index.ts b/packages/extension/src/providers/massa/ui/index.ts new file mode 100644 index 000000000..9d0e66586 --- /dev/null +++ b/packages/extension/src/providers/massa/ui/index.ts @@ -0,0 +1,9 @@ +import { ProviderName, UIExportOptions } from '@/types/provider'; +import getRoutes from './routes'; + +const uiExport: UIExportOptions = { + providerName: ProviderName.massa, + routes: getRoutes(ProviderName.massa), +}; + +export default uiExport; diff --git a/packages/extension/src/providers/massa/ui/libs/signer.ts b/packages/extension/src/providers/massa/ui/libs/signer.ts new file mode 100644 index 000000000..69dab7896 --- /dev/null +++ b/packages/extension/src/providers/massa/ui/libs/signer.ts @@ -0,0 +1,132 @@ +import { InternalMethods, InternalOnMessageResponse } from '@/types/messenger'; +import { bufferToHex, hexToBuffer } from '@enkryptcom/utils'; +import sendUsingInternalMessengers from '@/libs/messenger/internal-messenger'; +import { BaseNetwork } from '@/types/base-network'; +import { EnkryptAccount } from '@enkryptcom/types'; +import { + Address, + CallOperation, + MAX_GAS_CALL, + OperationManager, + OperationType, + PublicKey, + TransferOperation, +} from '@massalabs/massa-web3'; +import { MassaNetworkOptions } from '../../types'; + +export interface MassaTransactionOptions { + account: EnkryptAccount; + network: BaseNetwork; + payload: { + type: OperationType; + from: string; + to: string; + amount: string; + fee: string; + expirePeriod: number; + data?: string; + maxGas?: string; + coins?: string; + func?: string; + }; +} + +export interface MassaSignedTransaction { + from: string; + to: string; + amount: string; + fee: string; + expirePeriod: number; + signature: string; + publicKey: string; + serializedHex: string; +} + +/** + * Sign a Massa transaction + */ +export const MassaTransactionSigner = ( + options: MassaTransactionOptions, +): Promise => { + const { account, payload, network } = options; + + let operationDetails: TransferOperation | CallOperation; + if (payload.type === OperationType.Transaction) { + operationDetails = { + type: payload.type, + amount: BigInt(payload.amount), + recipientAddress: Address.fromString(payload.to), + fee: BigInt(payload.fee), + expirePeriod: payload.expirePeriod, + }; + } else if (payload.type === OperationType.CallSmartContractFunction) { + if (!payload.func) { + throw new Error('Invalid payload, target function is required'); + } + operationDetails = { + type: payload.type, + parameter: hexToBuffer(payload.data ?? ''), + fee: BigInt(payload.fee), + expirePeriod: payload.expirePeriod, + maxGas: BigInt(payload.maxGas ?? MAX_GAS_CALL), + coins: BigInt(payload.coins ?? '0'), + address: payload.to, + func: payload.func, + }; + } else { + throw new Error(`Unsupported operation type: ${payload.type}`); + } + + const chainId = (network as MassaNetworkOptions).chainId; + const publicKey = PublicKey.fromString(account.publicKey); + const serialized = OperationManager.canonicalize( + chainId!, + operationDetails, + publicKey, + ); + const bufferTosign = bufferToHex(serialized); + + return sendUsingInternalMessengers({ + method: InternalMethods.sign, + params: [bufferTosign, account], + }).then(res => { + if (res.error) { + return Promise.reject(res); + } else { + const signature = JSON.parse(res.result as string) || ''; + + const signedTx: MassaSignedTransaction = { + ...payload, + signature, + publicKey: account.publicKey, + serializedHex: bufferToHex( + OperationManager.serialize(operationDetails), + ), + }; + + return signedTx; + } + }); +}; + +/** + * Sign a Massa message + */ +export const MassaMessageSigner = (options: { + account: EnkryptAccount; + network: BaseNetwork; + payload: Buffer; +}): Promise => { + const { account, payload } = options; + + const msgHash = bufferToHex(payload); + return sendUsingInternalMessengers({ + method: InternalMethods.sign, + params: [msgHash, account], + }).then(res => { + if (res.error) return res; + return { + result: res.result, + }; + }); +}; diff --git a/packages/extension/src/providers/massa/ui/routes/index.ts b/packages/extension/src/providers/massa/ui/routes/index.ts new file mode 100644 index 000000000..a248cd380 --- /dev/null +++ b/packages/extension/src/providers/massa/ui/routes/index.ts @@ -0,0 +1,12 @@ +import { RouteRecordRaw } from 'vue-router'; +import RouteNames from './names'; + +const routes = Object.assign({}, RouteNames); + +export default (namespace: string): RouteRecordRaw[] => { + return Object.values(routes).map(route => { + route.path = `/${namespace}/${route.path}`; + route.name = `${namespace}-${String(route.name)}`; + return route; + }); +}; diff --git a/packages/extension/src/providers/massa/ui/routes/names.ts b/packages/extension/src/providers/massa/ui/routes/names.ts new file mode 100644 index 000000000..7ab9121ba --- /dev/null +++ b/packages/extension/src/providers/massa/ui/routes/names.ts @@ -0,0 +1,16 @@ +import { RouteRecordRaw } from 'vue-router'; + +const routes: Record = { + send: { + path: 'send', + name: 'massa-send', + component: () => import('../send-transaction/index.vue'), + }, + verify: { + path: 'verify', + name: 'massa-verify', + component: () => import('../send-transaction/verify-transaction/index.vue'), + }, +}; + +export default routes; diff --git a/packages/extension/src/providers/massa/ui/send-transaction/components/send-address-input.vue b/packages/extension/src/providers/massa/ui/send-transaction/components/send-address-input.vue new file mode 100644 index 000000000..6ff6bd897 --- /dev/null +++ b/packages/extension/src/providers/massa/ui/send-transaction/components/send-address-input.vue @@ -0,0 +1,215 @@ + + + + + diff --git a/packages/extension/src/providers/massa/ui/send-transaction/components/send-token-select.vue b/packages/extension/src/providers/massa/ui/send-transaction/components/send-token-select.vue new file mode 100644 index 000000000..998a8de9e --- /dev/null +++ b/packages/extension/src/providers/massa/ui/send-transaction/components/send-token-select.vue @@ -0,0 +1,118 @@ + + + + + diff --git a/packages/extension/src/providers/massa/ui/send-transaction/index.vue b/packages/extension/src/providers/massa/ui/send-transaction/index.vue new file mode 100644 index 000000000..81390c31b --- /dev/null +++ b/packages/extension/src/providers/massa/ui/send-transaction/index.vue @@ -0,0 +1,673 @@ + + + + + diff --git a/packages/extension/src/providers/massa/ui/send-transaction/verify-transaction/index.vue b/packages/extension/src/providers/massa/ui/send-transaction/verify-transaction/index.vue new file mode 100644 index 000000000..9e0517baf --- /dev/null +++ b/packages/extension/src/providers/massa/ui/send-transaction/verify-transaction/index.vue @@ -0,0 +1,477 @@ + + + + + diff --git a/packages/extension/src/types/activity.ts b/packages/extension/src/types/activity.ts index 48eb4fe84..b13efcab1 100644 --- a/packages/extension/src/types/activity.ts +++ b/packages/extension/src/types/activity.ts @@ -6,6 +6,7 @@ import { StatusOptionsResponse, } from '@enkryptcom/swap'; import { ICommandResult } from '@kadena/client'; +import { OperationStatus } from '@massalabs/massa-web3'; interface BTCIns { address: string; @@ -91,6 +92,8 @@ interface KadenaDBInfo { token: string; } +type MassaRawInfo = OperationStatus; + enum ActivityStatus { pending = 'pending', success = 'success', @@ -128,7 +131,8 @@ interface Activity { | BTCRawInfo | SwapRawInfo | KadenaRawInfo - | SOLRawInfo; + | SOLRawInfo + | MassaRawInfo; } export { @@ -143,4 +147,5 @@ export { KadenaRawInfo, KadenaDBInfo, SOLRawInfo, + MassaRawInfo, }; diff --git a/packages/extension/src/types/base-network.ts b/packages/extension/src/types/base-network.ts index d876a0c99..3ca1af7a2 100644 --- a/packages/extension/src/types/base-network.ts +++ b/packages/extension/src/types/base-network.ts @@ -8,6 +8,7 @@ import { CoingeckoPlatform, SignerType, NetworkNames } from '@enkryptcom/types'; import { Activity } from './activity'; import { BaseToken } from './base-token'; import { BNType } from '@/providers/common/types'; +import MassaAPI from '../providers/massa/libs/api'; export interface SubNetworkOptions { id: string; @@ -38,7 +39,8 @@ export interface BaseNetworkOptions { | Promise | Promise | Promise - | Promise; + | Promise + | Promise; customTokens?: boolean; } @@ -80,7 +82,8 @@ export abstract class BaseNetwork { | Promise | Promise | Promise - | Promise; + | Promise + | Promise; public customTokens: boolean; constructor(options: BaseNetworkOptions) { diff --git a/packages/extension/src/types/provider.ts b/packages/extension/src/types/provider.ts index 0f831c71b..b43819ba4 100644 --- a/packages/extension/src/types/provider.ts +++ b/packages/extension/src/types/provider.ts @@ -23,6 +23,7 @@ import { SubscanExtrinsicInfo, KadenaRawInfo, SOLRawInfo, + MassaRawInfo, } from './activity'; export enum ProviderName { @@ -32,6 +33,7 @@ export enum ProviderName { polkadot = 'polkadot', kadena = 'kadena', solana = 'solana', + massa = 'massa', } export enum InternalStorageNamespace { keyring = 'KeyRing', @@ -68,6 +70,7 @@ export enum ProviderType { bitcoin, kadena, solana, + massa, } export type SendMessageHandler = ( @@ -144,6 +147,7 @@ export abstract class ProviderAPIInterface { | BTCRawInfo | KadenaRawInfo | SOLRawInfo + | MassaRawInfo | null >; } diff --git a/packages/extension/src/ui/action/App.vue b/packages/extension/src/ui/action/App.vue index 4b1b6728e..a0efffa29 100644 --- a/packages/extension/src/ui/action/App.vue +++ b/packages/extension/src/ui/action/App.vue @@ -211,6 +211,8 @@ const openBuyPage = () => { case NetworkNames.SyscoinNEVMTest: case NetworkNames.RolluxTest: return (currentNetwork.value as EvmNetwork).options.buyLink; + case NetworkNames.Massa: + return 'https://www.massa.net/get-mas'; default: return `https://ccswap.myetherwallet.com/?to=${currentNetwork.value.displayAddress( accountHeaderData.value.selectedAccount!.address, @@ -316,11 +318,12 @@ const setNetwork = async (network: BaseNetwork) => { currentSubNetwork.value = ''; } const activeAccounts = await getAccountsByNetworkName(network.name); - const inactiveAccounts = await kr.getAccounts( getOtherSigners(network.signer), ); + const selectedAddress = await domainState.getSelectedAddress(); + let selectedAccount = activeAccounts[0]; if (selectedAddress) { const found = activeAccounts.find(acc => acc.address === selectedAddress); diff --git a/packages/extension/src/ui/action/views/network-activity/index.vue b/packages/extension/src/ui/action/views/network-activity/index.vue index b2f33a951..b99514968 100644 --- a/packages/extension/src/ui/action/views/network-activity/index.vue +++ b/packages/extension/src/ui/action/views/network-activity/index.vue @@ -68,6 +68,7 @@ import { SwapRawInfo, KadenaRawInfo, SOLRawInfo, + MassaRawInfo, } from '@/types/activity'; import NetworkActivityLoading from './components/network-activity-loading.vue'; import { ProviderName } from '@/types/provider'; @@ -79,6 +80,7 @@ import Swap, { } from '@enkryptcom/swap'; import EvmAPI from '@/providers/ethereum/libs/api'; import type Web3Eth from 'web3-eth'; +import { OperationStatus } from '@massalabs/massa-web3'; const props = defineProps({ network: { @@ -217,6 +219,17 @@ const handleActivityUpdate = (activity: Activity, info: any, timer: any) => { } else { return; /* Give the transaction more time to be mined */ } + } else if (props.network.provider === ProviderName.massa) { + if (!info) return; + const massaInfo = info as MassaRawInfo; + if (isActivityUpdating) return; + activity.status = + massaInfo === OperationStatus.Success || + massaInfo === OperationStatus.SpeculativeSuccess + ? ActivityStatus.success + : ActivityStatus.failed; + activity.rawInfo = massaInfo; + updateActivitySync(activity).then(() => updateVisibleActivity(activity)); } // If we're this far in then the transaction has reached a terminal status diff --git a/packages/extension/src/ui/action/views/network-assets/components/custom-massa-token.vue b/packages/extension/src/ui/action/views/network-assets/components/custom-massa-token.vue new file mode 100644 index 000000000..1942251af --- /dev/null +++ b/packages/extension/src/ui/action/views/network-assets/components/custom-massa-token.vue @@ -0,0 +1,492 @@ + + + + diff --git a/packages/extension/src/ui/action/views/network-assets/index.vue b/packages/extension/src/ui/action/views/network-assets/index.vue index a4dea7119..56d443572 100644 --- a/packages/extension/src/ui/action/views/network-assets/index.vue +++ b/packages/extension/src/ui/action/views/network-assets/index.vue @@ -59,9 +59,13 @@ /> - + + @@ -83,7 +87,9 @@ import scrollSettings from '@/libs/utils/scroll-settings'; import Deposit from '@action/views/deposit/index.vue'; import BaseButton from '@action/components/base-button/index.vue'; import CustomEvmToken from './components/custom-evm-token.vue'; +import CustomMassaToken from './components/custom-massa-token.vue'; import { EvmNetwork } from '@/providers/ethereum/types/evm-network'; +import { ProviderName } from '@/types/provider'; import NetworkAssetsSolanaStakingBanner from './components/network-assets-solana-staking-banner.vue'; import BannersState from '@/libs/banners-state'; @@ -146,6 +152,14 @@ const showAddCustomTokens = ref(false); const isSolanaStakingBanner = ref(false); const bannersState = new BannersState(); +// Network type checks +const isEvmNetwork = computed( + () => props.network.provider === ProviderName.ethereum, +); +const isMassaNetwork = computed( + () => props.network.provider === ProviderName.massa, +); + watch([selectedAddress, selectedNetworkName, selectedSubnetwork], updateAssets); onMounted(async () => { updateAssets(); diff --git a/packages/extension/src/ui/action/views/send-transaction/index.vue b/packages/extension/src/ui/action/views/send-transaction/index.vue index 5e3e90ffe..4a796a729 100644 --- a/packages/extension/src/ui/action/views/send-transaction/index.vue +++ b/packages/extension/src/ui/action/views/send-transaction/index.vue @@ -12,6 +12,7 @@ import SendTransactionEVM from '@/providers/ethereum/ui/send-transaction/index.v import SendTransactionBTC from '@/providers/bitcoin/ui/send-transaction/index.vue'; import SendTransactionKadena from '@/providers/kadena/ui/send-transaction/index.vue'; import SendTransactionSolana from '@/providers/solana/ui/send-transaction/index.vue'; +import SendTransactionMassa from '@/providers/massa/ui/send-transaction/index.vue'; import { useRoute } from 'vue-router'; import { ProviderName } from '@/types/provider'; import { getNetworkByName } from '@/libs/utils/networks'; @@ -25,6 +26,7 @@ const sendLayouts: Record = { [ProviderName.bitcoin]: SendTransactionBTC, [ProviderName.kadena]: SendTransactionKadena, [ProviderName.solana]: SendTransactionSolana, + [ProviderName.massa]: SendTransactionMassa, [ProviderName.enkrypt]: null, }; diff --git a/packages/extension/src/ui/action/views/verify-transaction/index.vue b/packages/extension/src/ui/action/views/verify-transaction/index.vue index 874fdf894..5760dbe73 100644 --- a/packages/extension/src/ui/action/views/verify-transaction/index.vue +++ b/packages/extension/src/ui/action/views/verify-transaction/index.vue @@ -8,6 +8,7 @@ import VerifyTransactionEVM from '@/providers/ethereum/ui/send-transaction/verif import VerifyTransactionBTC from '@/providers/bitcoin/ui/send-transaction/verify-transaction/index.vue'; import VerifyTransactionKadena from '@/providers/kadena/ui/send-transaction/verify-transaction/index.vue'; import VerifyTransactionSolana from '@/providers/solana/ui/send-transaction/verify-transaction/index.vue'; +import VerifyTransactionMassa from '@/providers/massa/ui/send-transaction/verify-transaction/index.vue'; import { useRoute } from 'vue-router'; import { ProviderName } from '@/types/provider'; import { getNetworkByName } from '@/libs/utils/networks'; @@ -19,6 +20,7 @@ const sendLayouts: Record = { [ProviderName.bitcoin]: VerifyTransactionBTC, [ProviderName.kadena]: VerifyTransactionKadena, [ProviderName.solana]: VerifyTransactionSolana, + [ProviderName.massa]: VerifyTransactionMassa, [ProviderName.enkrypt]: null, }; const layout = shallowRef(); diff --git a/packages/extension/src/ui/provider-pages/routes.ts b/packages/extension/src/ui/provider-pages/routes.ts index 3c50b4f49..6ca72fb7c 100644 --- a/packages/extension/src/ui/provider-pages/routes.ts +++ b/packages/extension/src/ui/provider-pages/routes.ts @@ -6,6 +6,7 @@ import PolkadotUI from '@/providers/polkadot/ui'; import BitcoinUI from '@/providers/bitcoin/ui'; import KadenaUI from '@/providers/kadena/ui'; import SolanaUI from '@/providers/solana/ui'; +import MassaUI from '@/providers/massa/ui'; import EnkryptUI from './enkrypt'; const uiProviders = [ EthereumUI, @@ -14,6 +15,7 @@ const uiProviders = [ EnkryptUI, KadenaUI, SolanaUI, + MassaUI, ]; let uiRoutes: RouteRecordRaw[] = []; uiProviders.forEach(provider => { diff --git a/packages/keyring/package.json b/packages/keyring/package.json index 74f42f8f1..21dc44649 100644 --- a/packages/keyring/package.json +++ b/packages/keyring/package.json @@ -25,6 +25,7 @@ "@enkryptcom/signer-bitcoin": "workspace:^", "@enkryptcom/signer-ethereum": "workspace:^", "@enkryptcom/signer-kadena": "workspace:^", + "@enkryptcom/signer-massa": "workspace:^", "@enkryptcom/signer-polkadot": "workspace:^", "@enkryptcom/storage": "workspace:^", "@enkryptcom/types": "workspace:^", diff --git a/packages/keyring/src/index.ts b/packages/keyring/src/index.ts index 29b8ca683..9200005a9 100644 --- a/packages/keyring/src/index.ts +++ b/packages/keyring/src/index.ts @@ -19,6 +19,7 @@ import { PolkadotSigner } from "@enkryptcom/signer-polkadot"; import { EthereumSigner } from "@enkryptcom/signer-ethereum"; import { BitcoinSigner } from "@enkryptcom/signer-bitcoin"; import { KadenaSigner } from "@enkryptcom/signer-kadena"; +import { MassaSigner } from "@enkryptcom/signer-massa"; import assert from "assert"; import configs from "./configs"; import { pathParser } from "./utils"; @@ -51,6 +52,7 @@ class KeyRing { [SignerType.secp256k1btc]: new BitcoinSigner(), [SignerType.ed25519kda]: new KadenaSigner(), [SignerType.ed25519sol]: new KadenaSigner(), + [SignerType.ed25519mas]: new MassaSigner(), }; } diff --git a/packages/keyring/src/utils.ts b/packages/keyring/src/utils.ts index 9bbca8da7..779033717 100644 --- a/packages/keyring/src/utils.ts +++ b/packages/keyring/src/utils.ts @@ -14,5 +14,8 @@ export const pathParser = ( if (type === SignerType.ed25519sol) { return `${basePath}/${index}'/0`; // Solana uses hardened paths } + if (type === SignerType.ed25519mas) { + return `${basePath}/${index}'/0'`; // Massa uses hardened paths with BIP-44 coin type 632 + } return `${basePath}/${index}`; }; diff --git a/packages/signers/massa/package.json b/packages/signers/massa/package.json new file mode 100644 index 000000000..cb4c35f97 --- /dev/null +++ b/packages/signers/massa/package.json @@ -0,0 +1,60 @@ +{ + "name": "@enkryptcom/signer-massa", + "version": "0.0.1", + "description": "Massa blockchain signer", + "type": "module", + "main": "src/index.ts", + "module": "src/index.ts", + "publishConfig": { + "main": "dist/index.cjs", + "module": "dist/index.js", + "types": "dist/index.d.ts" + }, + "files": [ + "dist" + ], + "scripts": { + "build": "tsup src/index.ts --format esm,cjs --dts --clean", + "lint": "prettier --write .", + "test": "vitest run" + }, + "engines": { + "node": ">=14.15.0" + }, + "dependencies": { + "@enkryptcom/types": "workspace:^", + "@enkryptcom/utils": "workspace:^", + "@massalabs/massa-web3": "^5.2.1-dev", + "bip39": "^3.1.0", + "tweetnacl": "^1.0.3", + "varint": "^6.0.0" + }, + "devDependencies": { + "@polkadot/util-crypto": "^13.5.1", + "@types/hdkey": "^2.1.0", + "@types/node": "^22.15.24", + "@types/varint": "^6", + "@typescript-eslint/eslint-plugin": "^8.33.0", + "@typescript-eslint/parser": "^8.33.0", + "eslint": "^9.27.0", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-config-prettier": "^10.1.5", + "eslint-import-resolver-alias": "^1.1.2", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-module-resolver": "^1.5.0", + "prettier": "^3.5.3", + "ts-node": "^10.9.2", + "tsconfig-paths": "^4.2.0", + "tsup": "^8.5.0", + "typescript": "^5.8.3", + "typescript-eslint": "8.33.0", + "vitest": "^3.1.4" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/enkryptcom/enKrypt/tree/main/packages/signers/massa" + }, + "keywords": [], + "author": "ae.studio", + "license": "MIT" +} diff --git a/packages/signers/massa/src/index.ts b/packages/signers/massa/src/index.ts new file mode 100644 index 000000000..e07093839 --- /dev/null +++ b/packages/signers/massa/src/index.ts @@ -0,0 +1,65 @@ +import { + KeyPair, + MnemonicWithExtraWord, + SignerInterface, +} from "@enkryptcom/types"; +import { bufferToHex, hexToBuffer } from "@enkryptcom/utils"; +import { mnemonicToSeedSync } from "bip39"; +import { sign as tweetSign } from "tweetnacl"; +import { derivePath } from "./libs/ed25519"; +import { + Address, + PublicKey, + PrivateKey, + Signature, +} from "@massalabs/massa-web3"; + +export class MassaSigner implements SignerInterface { + async generate( + mnemonic: MnemonicWithExtraWord, + derivationPath = "", + ): Promise { + const seed = bufferToHex( + mnemonicToSeedSync(mnemonic.mnemonic, mnemonic.extraWord), + true, + ); + const keys = derivePath(derivationPath, seed); + const keyPair = tweetSign.keyPair.fromSeed(keys.key); + + // Prefix the 32-byte private key with version bytes for Massa + const versionBytes = Buffer.from([0]); // KEYS_VERSION_NUMBER = 0 + const privateKeyBytes = Buffer.concat([ + versionBytes, + Buffer.from(keyPair.secretKey.slice(0, 32)), + ]); + const privateKey = PrivateKey.fromBytes(privateKeyBytes); + const publicKey = await PublicKey.fromPrivateKey(privateKey); + const address = Address.fromPublicKey(publicKey); + return { + address: address.toString(), + privateKey: privateKey.toString(), + publicKey: publicKey.toString(), + }; + } + + async verify( + msgHash: string, + sig: string, + publicKey: string, + ): Promise { + try { + const massaPublicKey = PublicKey.fromString(publicKey); + const signature = Signature.fromBytes(hexToBuffer(sig)); + return massaPublicKey.verify(hexToBuffer(msgHash), signature); + } catch (error) { + console.error("Massa signature verification failed:", error); + return false; + } + } + + async sign(msgHash: string, keyPair: KeyPair): Promise { + const privateKey = PrivateKey.fromString(keyPair.privateKey); + const sig = await privateKey.sign(hexToBuffer(msgHash)); + return sig.toString(); + } +} diff --git a/packages/signers/massa/src/libs/ed25519.ts b/packages/signers/massa/src/libs/ed25519.ts new file mode 100644 index 000000000..9b63f3204 --- /dev/null +++ b/packages/signers/massa/src/libs/ed25519.ts @@ -0,0 +1,81 @@ +import { createHmac } from "crypto"; +import { sign } from "tweetnacl"; + +const ED25519_CURVE = "ed25519 seed"; +const HARDENED_OFFSET = 0x80000000; + +type Hex = string; +type Path = string; + +type Keys = { + key: Buffer; + chainCode: Buffer; +}; + +const getMasterKeyFromSeed = (seed: Hex): Keys => { + const hmac = createHmac("sha512", Buffer.from(ED25519_CURVE, "utf8")); + const I = hmac.update(Buffer.from(seed, "hex")).digest(); + const IL = I.subarray(0, 32); + const IR = I.subarray(32); + return { + key: IL, + chainCode: IR, + }; +}; + +const CKDPriv = ({ key, chainCode }: Keys, index: number): Keys => { + const indexBuffer = Buffer.allocUnsafe(4); + indexBuffer.writeUInt32BE(index, 0); + + const data = Buffer.concat([Buffer.alloc(1, 0), key, indexBuffer]); + + const I = createHmac("sha512", chainCode).update(data).digest(); + const IL = I.subarray(0, 32); + const IR = I.subarray(32); + return { + key: IL, + chainCode: IR, + }; +}; + +const getPublicKey = (privateKey: Buffer, withZeroByte = true): Buffer => { + const keyPair = sign.keyPair.fromSeed(privateKey); + const signPk = keyPair.secretKey.subarray(32); + const zero = Buffer.alloc(1, 0); + return withZeroByte + ? Buffer.concat([zero, Buffer.from(signPk)]) + : Buffer.from(signPk); +}; + +const replaceDerive = (val: string): string => val.replace("'", ""); + +const isValidPath = (path: string): boolean => { + if (!/^m(\/[0-9]+('?))*$/.test(path)) { + return false; + } + return !path + .split("/") + .slice(1) + .map(replaceDerive) + .some(isNaN as any); +}; + +const derivePath = (path: Path, seed: Hex, offset = HARDENED_OFFSET): Keys => { + if (!isValidPath(path)) { + throw new Error("Invalid derivation path"); + } + + const { key, chainCode } = getMasterKeyFromSeed(seed); + const segments = path + .split("/") + .slice(1) + .map(replaceDerive) + .map((el) => parseInt(el, 10)); + + return segments.reduce( + (parentKeys, segment) => CKDPriv(parentKeys, segment + offset), + { key, chainCode }, + ); +}; + +export { derivePath, getPublicKey }; diff --git a/packages/signers/massa/tests/sign.test.ts b/packages/signers/massa/tests/sign.test.ts new file mode 100644 index 000000000..2184711fa --- /dev/null +++ b/packages/signers/massa/tests/sign.test.ts @@ -0,0 +1,52 @@ +import { describe, it, expect } from "vitest"; +import { blake2AsU8a } from "@polkadot/util-crypto"; +import { bufferToHex } from "@enkryptcom/utils"; +import { MassaSigner } from "../src"; + +describe("Massa signing", () => { + const MNEMONIC = { + mnemonic: + "error fish boy absent drop next ice keep meadow little air include", + }; + + const msg = + "Everything should be made as simple as possible, but not simpler."; + const msgHash = bufferToHex(blake2AsU8a(msg)); + + it("should generate keypair correctly", async () => { + const signer = new MassaSigner(); + const keypair = await signer.generate(MNEMONIC, "m/44'/632'/0'/0'"); + + expect(keypair.address).toBeDefined(); + expect(keypair.privateKey).toBeDefined(); + expect(keypair.publicKey).toBeDefined(); + + // Massa addresses should start with 'AU' + expect(keypair.address?.startsWith("AU")).toBe(true); + }); + + it("should sign and verify correctly", async () => { + const signer = new MassaSigner(); + const keypair = await signer.generate(MNEMONIC, "m/44'/632'/0'/0'"); + const signature = await signer.sign(msgHash, keypair); + + expect(signature).toBeDefined(); + expect(signature.length).toBeGreaterThan(0); + + const isValid = await signer.verify(msgHash, signature, keypair.publicKey); + expect(isValid).toBe(true); + }); + + it("should reject invalid signatures", async () => { + const signer = new MassaSigner(); + const keypair = await signer.generate(MNEMONIC, "m/44'/632'/0'/0'"); + const invalidSignature = "0x" + "0".repeat(128); // Invalid signature + + const isValid = await signer.verify( + msgHash, + invalidSignature, + keypair.publicKey, + ); + expect(isValid).toBe(false); + }); +}); diff --git a/packages/signers/massa/tsconfig.json b/packages/signers/massa/tsconfig.json new file mode 100644 index 000000000..e8b896562 --- /dev/null +++ b/packages/signers/massa/tsconfig.json @@ -0,0 +1,29 @@ +{ + "extends": "./tsconfig.paths.json", + "compilerOptions": { + "lib": ["ESNext", "dom"], + "moduleResolution": "node", + "noUnusedLocals": true, + "noUnusedParameters": true, + "removeComments": true, + "sourceMap": true, + "target": "es2015", + "module": "commonjs", + "outDir": "dist", + "resolveJsonModule": true, + "esModuleInterop": true, + "declaration": true, + "downlevelIteration": true + }, + "include": ["src/**/*.ts", "tests/**/*.ts"], + "exclude": [ + "node_modules/**/*", + "_warmup/**/*", + ".vscode/**/*", + "tests/**/*" + ], + "ts-node": { + "require": ["tsconfig-paths/register"], + "files": true + } +} diff --git a/packages/signers/massa/tsconfig.paths.json b/packages/signers/massa/tsconfig.paths.json new file mode 100644 index 000000000..6451834c2 --- /dev/null +++ b/packages/signers/massa/tsconfig.paths.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@src/*": ["src/*"] + } + } +} diff --git a/packages/types/package.json b/packages/types/package.json index d013f70ad..8f18f5322 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -24,6 +24,7 @@ "node": ">=14.15.0" }, "devDependencies": { + "@massalabs/massa-web3": "^5.2.1-dev", "@types/node": "^22.16.5", "@typescript-eslint/eslint-plugin": "^8.38.0", "@typescript-eslint/parser": "^8.38.0", diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index a4ad09e70..5cf205151 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -50,6 +50,7 @@ enum SignerType { secp256k1btc = "secp256k1-btc", // bitcoin ed25519kda = "ed25519-kda", // kadena ed25519sol = "ed25519-sol", // solana + ed25519mas = "ed25519-mas", // massa } interface KeyRecordAdd { diff --git a/packages/types/src/networks.ts b/packages/types/src/networks.ts index fd120357e..69b39b292 100755 --- a/packages/types/src/networks.ts +++ b/packages/types/src/networks.ts @@ -112,6 +112,8 @@ export enum NetworkNames { Hemi = "Hemi", CoreDAO = "CORE", AppLayerTestnet = "AppLayerTestnet", + Massa = "Massa", + MassaBuildnet = "MassaBuildnet", } export enum CoingeckoPlatform { @@ -181,4 +183,5 @@ export enum CoingeckoPlatform { UnitZero = "units-network", Conflux = "conflux", CoreDAO = "core", + Massa = "massa", } diff --git a/yarn.lock b/yarn.lock index 9b060103b..0ae842963 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1642,6 +1642,7 @@ __metadata: "@kadena/client": "npm:^1.18.0" "@kadena/pactjs-cli": "npm:^1.18.0" "@ledgerhq/hw-transport-webusb": "npm:^6.29.8" + "@massalabs/massa-web3": "npm:5.2.1-dev.20250730141858" "@metamask/eth-sig-util": "npm:^8.2.0" "@metaplex-foundation/mpl-bubblegum": "npm:^5.0.2" "@metaplex-foundation/umi": "npm:^1.2.0" @@ -1797,6 +1798,7 @@ __metadata: "@enkryptcom/signer-bitcoin": "workspace:^" "@enkryptcom/signer-ethereum": "workspace:^" "@enkryptcom/signer-kadena": "workspace:^" + "@enkryptcom/signer-massa": "workspace:^" "@enkryptcom/signer-polkadot": "workspace:^" "@enkryptcom/storage": "workspace:^" "@enkryptcom/types": "workspace:^" @@ -1973,6 +1975,38 @@ __metadata: languageName: unknown linkType: soft +"@enkryptcom/signer-massa@workspace:^, @enkryptcom/signer-massa@workspace:packages/signers/massa": + version: 0.0.0-use.local + resolution: "@enkryptcom/signer-massa@workspace:packages/signers/massa" + dependencies: + "@enkryptcom/types": "workspace:^" + "@enkryptcom/utils": "workspace:^" + "@massalabs/massa-web3": "npm:^5.2.1-dev" + "@polkadot/util-crypto": "npm:^13.5.1" + "@types/hdkey": "npm:^2.1.0" + "@types/node": "npm:^22.15.24" + "@types/varint": "npm:^6" + "@typescript-eslint/eslint-plugin": "npm:^8.33.0" + "@typescript-eslint/parser": "npm:^8.33.0" + bip39: "npm:^3.1.0" + eslint: "npm:^9.27.0" + eslint-config-airbnb-base: "npm:^15.0.0" + eslint-config-prettier: "npm:^10.1.5" + eslint-import-resolver-alias: "npm:^1.1.2" + eslint-plugin-import: "npm:^2.31.0" + eslint-plugin-module-resolver: "npm:^1.5.0" + prettier: "npm:^3.5.3" + ts-node: "npm:^10.9.2" + tsconfig-paths: "npm:^4.2.0" + tsup: "npm:^8.5.0" + tweetnacl: "npm:^1.0.3" + typescript: "npm:^5.8.3" + typescript-eslint: "npm:8.33.0" + varint: "npm:^6.0.0" + vitest: "npm:^3.1.4" + languageName: unknown + linkType: soft + "@enkryptcom/signer-polkadot@workspace:^, @enkryptcom/signer-polkadot@workspace:packages/signers/polkadot": version: 0.0.0-use.local resolution: "@enkryptcom/signer-polkadot@workspace:packages/signers/polkadot" @@ -2071,6 +2105,7 @@ __metadata: version: 0.0.0-use.local resolution: "@enkryptcom/types@workspace:packages/types" dependencies: + "@massalabs/massa-web3": "npm:^5.2.1-dev" "@types/node": "npm:^22.16.5" "@typescript-eslint/eslint-plugin": "npm:^8.38.0" "@typescript-eslint/parser": "npm:^8.38.0" @@ -2535,6 +2570,15 @@ __metadata: languageName: node linkType: hard +"@eslint/core@npm:^0.14.0": + version: 0.14.0 + resolution: "@eslint/core@npm:0.14.0" + dependencies: + "@types/json-schema": "npm:^7.0.15" + checksum: 10/d9b060cf97468150675ddf4fb3db55edaa32467e0adf9f80919a5bfd15d0835ad7765456f4397ec2d16b0a1bb702af63f6d4712f94194d34fea118231ae1e2db + languageName: node + linkType: hard + "@eslint/core@npm:^0.15.0, @eslint/core@npm:^0.15.1": version: 0.15.1 resolution: "@eslint/core@npm:0.15.1" @@ -2561,6 +2605,13 @@ __metadata: languageName: node linkType: hard +"@eslint/js@npm:9.30.1": + version: 9.30.1 + resolution: "@eslint/js@npm:9.30.1" + checksum: 10/f32c8a42b1343b03613b92a4a7c4745d19a93e66ffb8fac18ccd066deaa200b2fc55f476bf0407d7748f663bc947ca78c0518f13f60cd9ed08d0ba8b7bb74370 + languageName: node + linkType: hard + "@eslint/js@npm:9.32.0": version: 9.32.0 resolution: "@eslint/js@npm:9.32.0" @@ -2575,6 +2626,16 @@ __metadata: languageName: node linkType: hard +"@eslint/plugin-kit@npm:^0.3.1": + version: 0.3.1 + resolution: "@eslint/plugin-kit@npm:0.3.1" + dependencies: + "@eslint/core": "npm:^0.14.0" + levn: "npm:^0.4.1" + checksum: 10/ab0c4cecadc6c38c7ae5f71b9831d3521d08237444d8f327751d1133a4369ccd42093a1c06b26fd6c311015807a27d95a0184a761d1cdd264b090896dcf0addb + languageName: node + linkType: hard + "@eslint/plugin-kit@npm:^0.3.4": version: 0.3.4 resolution: "@eslint/plugin-kit@npm:0.3.4" @@ -5804,6 +5865,58 @@ __metadata: languageName: node linkType: hard +"@massalabs/massa-web3@npm:5.2.1-dev.20250730141858": + version: 5.2.1-dev.20250730141858 + resolution: "@massalabs/massa-web3@npm:5.2.1-dev.20250730141858" + dependencies: + "@noble/ed25519": "npm:^1.7.3" + "@noble/hashes": "npm:^1.2.0" + bs58check: "npm:^4.0.0" + bufferutil: "npm:^4.0.7" + dotenv: "npm:^16.0.3" + eventemitter3: "npm:^5.0.1" + google-protobuf: "npm:^3.21.4" + grpc-web: "npm:^1.5.0" + lodash.isequal: "npm:^4.5.0" + secure-random: "npm:^1.1.2" + tslib: "npm:^2.8.0" + utf-8-validate: "npm:^6.0.2" + varint: "npm:^6.0.0" + dependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 10/56464249fcfbf5f66980eceecef13343b5379bf9badd779be0d6d0ce51882c15310c74d5209ff75d825058eb82ac26e1f7b2ff5fa96a7c690461577710b8e6b1 + languageName: node + linkType: hard + +"@massalabs/massa-web3@npm:^5.2.1-dev": + version: 5.2.1-dev.20250611112554 + resolution: "@massalabs/massa-web3@npm:5.2.1-dev.20250611112554" + dependencies: + "@noble/ed25519": "npm:^1.7.3" + "@noble/hashes": "npm:^1.2.0" + bs58check: "npm:^4.0.0" + bufferutil: "npm:^4.0.7" + dotenv: "npm:^16.0.3" + eventemitter3: "npm:^5.0.1" + google-protobuf: "npm:^3.21.4" + grpc-web: "npm:^1.5.0" + lodash.isequal: "npm:^4.5.0" + secure-random: "npm:^1.1.2" + tslib: "npm:^2.8.0" + utf-8-validate: "npm:^6.0.2" + varint: "npm:^6.0.0" + dependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 10/29f69533e5b9469440c6d329824d59600448ac3de40b44340878a4f883f353de7a858ddea69091d20b5fa5d77dca96d9c39c8c994eb9c635a72c2780096126bf + languageName: node + linkType: hard + "@metamask/abi-utils@npm:^3.0.0": version: 3.0.0 resolution: "@metamask/abi-utils@npm:3.0.0" @@ -7200,6 +7313,17 @@ __metadata: languageName: node linkType: hard +"@polkadot/networks@npm:13.5.1": + version: 13.5.1 + resolution: "@polkadot/networks@npm:13.5.1" + dependencies: + "@polkadot/util": "npm:13.5.1" + "@substrate/ss58-registry": "npm:^1.51.0" + tslib: "npm:^2.8.0" + checksum: 10/836835ffcf58027cb450f17400c733eb84272cb7de0b1a86f72a0c1b1bb35ea3c628f8dd418e185f163ff0963ec931f41618b9405088a1d6fe2fbf5e5ab09709 + languageName: node + linkType: hard + "@polkadot/networks@npm:13.5.3, @polkadot/networks@npm:^13.5.3": version: 13.5.3 resolution: "@polkadot/networks@npm:13.5.3" @@ -7639,6 +7763,26 @@ __metadata: languageName: node linkType: hard +"@polkadot/util-crypto@npm:^13.5.1": + version: 13.5.1 + resolution: "@polkadot/util-crypto@npm:13.5.1" + dependencies: + "@noble/curves": "npm:^1.3.0" + "@noble/hashes": "npm:^1.3.3" + "@polkadot/networks": "npm:13.5.1" + "@polkadot/util": "npm:13.5.1" + "@polkadot/wasm-crypto": "npm:^7.4.1" + "@polkadot/wasm-util": "npm:^7.4.1" + "@polkadot/x-bigint": "npm:13.5.1" + "@polkadot/x-randomvalues": "npm:13.5.1" + "@scure/base": "npm:^1.1.7" + tslib: "npm:^2.8.0" + peerDependencies: + "@polkadot/util": 13.5.1 + checksum: 10/280a8fb838e8ee30da92a73c5c578977c2d0d823dd03143ebdd3a0a9dc276649a6476a1cbf0200140ed116146b7202c7cd5c0be58271e12326d4aff4a42788e4 + languageName: node + linkType: hard + "@polkadot/util@npm:12.6.2, @polkadot/util@npm:^12.6.2": version: 12.6.2 resolution: "@polkadot/util@npm:12.6.2" @@ -7654,6 +7798,21 @@ __metadata: languageName: node linkType: hard +"@polkadot/util@npm:13.5.1": + version: 13.5.1 + resolution: "@polkadot/util@npm:13.5.1" + dependencies: + "@polkadot/x-bigint": "npm:13.5.1" + "@polkadot/x-global": "npm:13.5.1" + "@polkadot/x-textdecoder": "npm:13.5.1" + "@polkadot/x-textencoder": "npm:13.5.1" + "@types/bn.js": "npm:^5.1.6" + bn.js: "npm:^5.2.1" + tslib: "npm:^2.8.0" + checksum: 10/26b7c564012afec2c877dbf265021883335a28f369c84d59eeaf6bba72a9fd4e70d259cf24619332e9c44fc55093d9f51ec212a7d48fcae92e962f42f4310d6f + languageName: node + linkType: hard + "@polkadot/util@npm:13.5.3, @polkadot/util@npm:^13.5.3": version: 13.5.3 resolution: "@polkadot/util@npm:13.5.3" @@ -7759,6 +7918,16 @@ __metadata: languageName: node linkType: hard +"@polkadot/x-bigint@npm:13.5.1": + version: 13.5.1 + resolution: "@polkadot/x-bigint@npm:13.5.1" + dependencies: + "@polkadot/x-global": "npm:13.5.1" + tslib: "npm:^2.8.0" + checksum: 10/726bc63bc9a51dbfc21de3913ac546c296ca82b05a78014a9de7268a22c4db85552c6143ab058e0bf3e92defaa36a7d7a803a3deb6607e5928f408ee245dc55e + languageName: node + linkType: hard + "@polkadot/x-bigint@npm:13.5.3, @polkadot/x-bigint@npm:^13.5.3": version: 13.5.3 resolution: "@polkadot/x-bigint@npm:13.5.3" @@ -7800,6 +7969,15 @@ __metadata: languageName: node linkType: hard +"@polkadot/x-global@npm:13.5.1": + version: 13.5.1 + resolution: "@polkadot/x-global@npm:13.5.1" + dependencies: + tslib: "npm:^2.8.0" + checksum: 10/6db58bbed5c6506c2f6d542cd836b242750a01d93e30fb6f19543a43ca7573a96813df44344df5d9ec938a913e2ddb8af0258f51770d3aa9f9bcbb896d8e5d1b + languageName: node + linkType: hard + "@polkadot/x-global@npm:13.5.3, @polkadot/x-global@npm:^13.5.3": version: 13.5.3 resolution: "@polkadot/x-global@npm:13.5.3" @@ -7822,6 +8000,19 @@ __metadata: languageName: node linkType: hard +"@polkadot/x-randomvalues@npm:13.5.1": + version: 13.5.1 + resolution: "@polkadot/x-randomvalues@npm:13.5.1" + dependencies: + "@polkadot/x-global": "npm:13.5.1" + tslib: "npm:^2.8.0" + peerDependencies: + "@polkadot/util": 13.5.1 + "@polkadot/wasm-util": "*" + checksum: 10/cadd8c7f9e7acf219c7cdc45bd3e7473e2da5577919936eb1721a297bfaeb45498e3f651200b9333aab95666d09671f915c8ac262924a922c23520ae494acf72 + languageName: node + linkType: hard + "@polkadot/x-randomvalues@npm:13.5.3": version: 13.5.3 resolution: "@polkadot/x-randomvalues@npm:13.5.3" @@ -7845,6 +8036,16 @@ __metadata: languageName: node linkType: hard +"@polkadot/x-textdecoder@npm:13.5.1": + version: 13.5.1 + resolution: "@polkadot/x-textdecoder@npm:13.5.1" + dependencies: + "@polkadot/x-global": "npm:13.5.1" + tslib: "npm:^2.8.0" + checksum: 10/705b3f3b3c757cf2f7077e71f6c765c088e6636e5580408a8f95bf9b2e17c59bfa0846d89ba423131422a0bbb381fe0eff13cd7ac3d366272ebe269876789a52 + languageName: node + linkType: hard + "@polkadot/x-textdecoder@npm:13.5.3": version: 13.5.3 resolution: "@polkadot/x-textdecoder@npm:13.5.3" @@ -7865,6 +8066,16 @@ __metadata: languageName: node linkType: hard +"@polkadot/x-textencoder@npm:13.5.1": + version: 13.5.1 + resolution: "@polkadot/x-textencoder@npm:13.5.1" + dependencies: + "@polkadot/x-global": "npm:13.5.1" + tslib: "npm:^2.8.0" + checksum: 10/d5309785674a034e21717236f7f2a438da77d29c514c9cfae8517c8db690e1cff55820cfbac6f2307c9e1d4dc2c4e24ad01f915b585823af5ee73737441d1766 + languageName: node + linkType: hard + "@polkadot/x-textencoder@npm:13.5.3": version: 13.5.3 resolution: "@polkadot/x-textencoder@npm:13.5.3" @@ -11509,6 +11720,15 @@ __metadata: languageName: node linkType: hard +"@types/node@npm:^22.15.24": + version: 22.15.32 + resolution: "@types/node@npm:22.15.32" + dependencies: + undici-types: "npm:~6.21.0" + checksum: 10/10b4c106d0c512a1d35ec08142bd7fb5cf2e1df93fc5627b3c69dd843dec4be07a47f1fa7ede232ad84762d75a372ea35028b79ee1e753b6f2adecd0b2cb2f71 + languageName: node + linkType: hard + "@types/node@npm:^22.16.5": version: 22.16.5 resolution: "@types/node@npm:22.16.5" @@ -11737,6 +11957,15 @@ __metadata: languageName: node linkType: hard +"@types/varint@npm:^6": + version: 6.0.3 + resolution: "@types/varint@npm:6.0.3" + dependencies: + "@types/node": "npm:*" + checksum: 10/cfec85c15e24becb360be6466a41c97b5b6370aec49a403ed28ae52e96b7ef692e9e5aee981ccebab3b7afdf9b84d3809e6ab5bb38cf2e926711c6aebeb8a68d + languageName: node + linkType: hard + "@types/w3c-web-usb@npm:^1.0.6": version: 1.0.10 resolution: "@types/w3c-web-usb@npm:1.0.10" @@ -11822,7 +12051,28 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:8.36.0": +"@typescript-eslint/eslint-plugin@npm:8.33.0": + version: 8.33.0 + resolution: "@typescript-eslint/eslint-plugin@npm:8.33.0" + dependencies: + "@eslint-community/regexpp": "npm:^4.10.0" + "@typescript-eslint/scope-manager": "npm:8.33.0" + "@typescript-eslint/type-utils": "npm:8.33.0" + "@typescript-eslint/utils": "npm:8.33.0" + "@typescript-eslint/visitor-keys": "npm:8.33.0" + graphemer: "npm:^1.4.0" + ignore: "npm:^7.0.0" + natural-compare: "npm:^1.4.0" + ts-api-utils: "npm:^2.1.0" + peerDependencies: + "@typescript-eslint/parser": ^8.33.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <5.9.0" + checksum: 10/b728001208b28c05d024f352d61d6b707c5f9a9d1d9ada8239d3171897ac1545a54401ca340732af9f615af88dae954f7264d73ced1950e02d38225d33492f8d + languageName: node + linkType: hard + +"@typescript-eslint/eslint-plugin@npm:8.36.0, @typescript-eslint/eslint-plugin@npm:^8.33.0": version: 8.36.0 resolution: "@typescript-eslint/eslint-plugin@npm:8.36.0" dependencies: @@ -11864,7 +12114,23 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/parser@npm:8.36.0": +"@typescript-eslint/parser@npm:8.33.0": + version: 8.33.0 + resolution: "@typescript-eslint/parser@npm:8.33.0" + dependencies: + "@typescript-eslint/scope-manager": "npm:8.33.0" + "@typescript-eslint/types": "npm:8.33.0" + "@typescript-eslint/typescript-estree": "npm:8.33.0" + "@typescript-eslint/visitor-keys": "npm:8.33.0" + debug: "npm:^4.3.4" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <5.9.0" + checksum: 10/1a875fdb0e9d0a49cbe0f5a33da16ee95c5a1ddf08bd1a3f9481de5c6b3981aee8ef2be24570a14fbed1b53ee348ee8dac39bcc436e6479ea1ecb39b69ce7f2a + languageName: node + linkType: hard + +"@typescript-eslint/parser@npm:8.36.0, @typescript-eslint/parser@npm:^8.33.0": version: 8.36.0 resolution: "@typescript-eslint/parser@npm:8.36.0" dependencies: @@ -11896,6 +12162,17 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/project-service@npm:8.33.0": + version: 8.33.0 + resolution: "@typescript-eslint/project-service@npm:8.33.0" + dependencies: + "@typescript-eslint/tsconfig-utils": "npm:^8.33.0" + "@typescript-eslint/types": "npm:^8.33.0" + debug: "npm:^4.3.4" + checksum: 10/5fdc829a67092c2b764598facaf515ec114af2fcfdaf68af321aa667e4c4962fa6c19120efbde7ca234488b2bd7299015fb6b221b22253fe4ea3eae0bb72e494 + languageName: node + linkType: hard + "@typescript-eslint/project-service@npm:8.36.0": version: 8.36.0 resolution: "@typescript-eslint/project-service@npm:8.36.0" @@ -11935,6 +12212,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/scope-manager@npm:8.33.0": + version: 8.33.0 + resolution: "@typescript-eslint/scope-manager@npm:8.33.0" + dependencies: + "@typescript-eslint/types": "npm:8.33.0" + "@typescript-eslint/visitor-keys": "npm:8.33.0" + checksum: 10/f52075c9ab3bdc69435f3b36583d2d5eb7bc66cfc8462184c9dc6dba5d0825e4d1d0f2e473ffaab5469fcfc4dc770908c26c1623b882d283739eb8e1869ab759 + languageName: node + linkType: hard + "@typescript-eslint/scope-manager@npm:8.36.0": version: 8.36.0 resolution: "@typescript-eslint/scope-manager@npm:8.36.0" @@ -11965,7 +12252,16 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/tsconfig-utils@npm:8.36.0, @typescript-eslint/tsconfig-utils@npm:^8.36.0": +"@typescript-eslint/tsconfig-utils@npm:8.33.0": + version: 8.33.0 + resolution: "@typescript-eslint/tsconfig-utils@npm:8.33.0" + peerDependencies: + typescript: ">=4.8.4 <5.9.0" + checksum: 10/5bb139be996a16f65c012c083e4c0dc2ddafd1295940203e6c2a1ac9fa0718b1a91f74354f162d3d9614b013e062863414d4478c57ffbf78dfd7cb4f5701abde + languageName: node + linkType: hard + +"@typescript-eslint/tsconfig-utils@npm:8.36.0, @typescript-eslint/tsconfig-utils@npm:^8.33.0, @typescript-eslint/tsconfig-utils@npm:^8.36.0": version: 8.36.0 resolution: "@typescript-eslint/tsconfig-utils@npm:8.36.0" peerDependencies: @@ -11992,6 +12288,21 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/type-utils@npm:8.33.0": + version: 8.33.0 + resolution: "@typescript-eslint/type-utils@npm:8.33.0" + dependencies: + "@typescript-eslint/typescript-estree": "npm:8.33.0" + "@typescript-eslint/utils": "npm:8.33.0" + debug: "npm:^4.3.4" + ts-api-utils: "npm:^2.1.0" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <5.9.0" + checksum: 10/993e7b8f2c8b6e34e175fd57077cb3ffbe9c83da66917b8bd0049e82c39f1ef18ab718a2ca1774db48311c58e4baf5b8e749e6aff5ff50c01b714e9471a673a5 + languageName: node + linkType: hard + "@typescript-eslint/type-utils@npm:8.36.0": version: 8.36.0 resolution: "@typescript-eslint/type-utils@npm:8.36.0" @@ -12023,7 +12334,14 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/types@npm:8.36.0, @typescript-eslint/types@npm:^8.36.0": +"@typescript-eslint/types@npm:8.33.0": + version: 8.33.0 + resolution: "@typescript-eslint/types@npm:8.33.0" + checksum: 10/778e812e2c3543e79168fe1072559d8bfef314a96b90da81805f9359f3b5cdfd202153c161723ccb1a4f8edb2593625aa5003f6235039bf189b39c93ff2605c2 + languageName: node + linkType: hard + +"@typescript-eslint/types@npm:8.36.0, @typescript-eslint/types@npm:^8.33.0, @typescript-eslint/types@npm:^8.36.0": version: 8.36.0 resolution: "@typescript-eslint/types@npm:8.36.0" checksum: 10/14ac09633d6e9947d88b8d714826bb12f4aa71874351e5c92d43fc9b5b48358cd6f58473c12e7bd583bcb3e05993bef89783e67f60746df19553d6e7ee1588af @@ -12044,6 +12362,26 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/typescript-estree@npm:8.33.0": + version: 8.33.0 + resolution: "@typescript-eslint/typescript-estree@npm:8.33.0" + dependencies: + "@typescript-eslint/project-service": "npm:8.33.0" + "@typescript-eslint/tsconfig-utils": "npm:8.33.0" + "@typescript-eslint/types": "npm:8.33.0" + "@typescript-eslint/visitor-keys": "npm:8.33.0" + debug: "npm:^4.3.4" + fast-glob: "npm:^3.3.2" + is-glob: "npm:^4.0.3" + minimatch: "npm:^9.0.4" + semver: "npm:^7.6.0" + ts-api-utils: "npm:^2.1.0" + peerDependencies: + typescript: ">=4.8.4 <5.9.0" + checksum: 10/7cad508e5cc70a1e0bc72ee0448b0cc55e195c93124a25a8330c58fc3fee4e2762cbc8039ad13d40cb0ef2953239af9dbb4d3653636f605ed3f9414995af080c + languageName: node + linkType: hard + "@typescript-eslint/typescript-estree@npm:8.36.0": version: 8.36.0 resolution: "@typescript-eslint/typescript-estree@npm:8.36.0" @@ -12104,6 +12442,21 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/utils@npm:8.33.0": + version: 8.33.0 + resolution: "@typescript-eslint/utils@npm:8.33.0" + dependencies: + "@eslint-community/eslint-utils": "npm:^4.7.0" + "@typescript-eslint/scope-manager": "npm:8.33.0" + "@typescript-eslint/types": "npm:8.33.0" + "@typescript-eslint/typescript-estree": "npm:8.33.0" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <5.9.0" + checksum: 10/096011772d1ba6236413b1a49b8ad4f8999c0dcad1192ab81a13a753a95bfaf18cb138db94302cb00c312d410c7f48bb35ac1521908a55967e1fbba641aebcc5 + languageName: node + linkType: hard + "@typescript-eslint/utils@npm:8.36.0": version: 8.36.0 resolution: "@typescript-eslint/utils@npm:8.36.0" @@ -12149,6 +12502,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/visitor-keys@npm:8.33.0": + version: 8.33.0 + resolution: "@typescript-eslint/visitor-keys@npm:8.33.0" + dependencies: + "@typescript-eslint/types": "npm:8.33.0" + eslint-visitor-keys: "npm:^4.2.0" + checksum: 10/f7f030c296dd83feb144f74aa382a67e4bb521d250507ede839f762bb215036d99d191b2203ac7af9867e434e569e4071ee0737cbde41d0ec38c0197f0a8549d + languageName: node + linkType: hard + "@typescript-eslint/visitor-keys@npm:8.36.0": version: 8.36.0 resolution: "@typescript-eslint/visitor-keys@npm:8.36.0" @@ -14031,6 +14394,20 @@ __metadata: languageName: node linkType: hard +"array-includes@npm:^3.1.8": + version: 3.1.8 + resolution: "array-includes@npm:3.1.8" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.2" + es-object-atoms: "npm:^1.0.0" + get-intrinsic: "npm:^1.2.4" + is-string: "npm:^1.0.7" + checksum: 10/290b206c9451f181fb2b1f79a3bf1c0b66bb259791290ffbada760c79b284eef6f5ae2aeb4bcff450ebc9690edd25732c4c73a3c2b340fcc0f4563aed83bf488 + languageName: node + linkType: hard + "array-includes@npm:^3.1.9": version: 3.1.9 resolution: "array-includes@npm:3.1.9" @@ -14054,6 +14431,20 @@ __metadata: languageName: node linkType: hard +"array.prototype.findlastindex@npm:^1.2.5": + version: 1.2.5 + resolution: "array.prototype.findlastindex@npm:1.2.5" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-abstract: "npm:^1.23.2" + es-errors: "npm:^1.3.0" + es-object-atoms: "npm:^1.0.0" + es-shim-unscopables: "npm:^1.0.2" + checksum: 10/7c5c821f357cd53ab6cc305de8086430dd8d7a2485db87b13f843e868055e9582b1fd338f02338f67fc3a1603ceaf9610dd2a470b0b506f9d18934780f95b246 + languageName: node + linkType: hard + "array.prototype.findlastindex@npm:^1.2.6": version: 1.2.6 resolution: "array.prototype.findlastindex@npm:1.2.6" @@ -14069,6 +14460,18 @@ __metadata: languageName: node linkType: hard +"array.prototype.flat@npm:^1.3.2": + version: 1.3.2 + resolution: "array.prototype.flat@npm:1.3.2" + dependencies: + call-bind: "npm:^1.0.2" + define-properties: "npm:^1.2.0" + es-abstract: "npm:^1.22.1" + es-shim-unscopables: "npm:^1.0.0" + checksum: 10/d9d2f6f27584de92ec7995bc931103e6de722cd2498bdbfc4cba814fc3e52f056050a93be883018811f7c0a35875f5056584a0e940603a5e5934f0279896aebe + languageName: node + linkType: hard + "array.prototype.flat@npm:^1.3.3": version: 1.3.3 resolution: "array.prototype.flat@npm:1.3.3" @@ -14081,6 +14484,18 @@ __metadata: languageName: node linkType: hard +"array.prototype.flatmap@npm:^1.3.2": + version: 1.3.2 + resolution: "array.prototype.flatmap@npm:1.3.2" + dependencies: + call-bind: "npm:^1.0.2" + define-properties: "npm:^1.2.0" + es-abstract: "npm:^1.22.1" + es-shim-unscopables: "npm:^1.0.0" + checksum: 10/33f20006686e0cbe844fde7fd290971e8366c6c5e3380681c2df15738b1df766dd02c7784034aeeb3b037f65c496ee54de665388288edb323a2008bb550f77ea + languageName: node + linkType: hard + "array.prototype.flatmap@npm:^1.3.3": version: 1.3.3 resolution: "array.prototype.flatmap@npm:1.3.3" @@ -17691,7 +18106,7 @@ __metadata: languageName: node linkType: hard -"dotenv@npm:^16.4.5, dotenv@npm:^16.5.0": +"dotenv@npm:^16.0.3, dotenv@npm:^16.4.5, dotenv@npm:^16.5.0": version: 16.5.0 resolution: "dotenv@npm:16.5.0" checksum: 10/e68a16834f1a41cc2dfb01563bc150668ad675e6cd09191211467b5c0806b6ecd6ec438e021aa8e01cd0e72d2b70ef4302bec7cc0fe15b6955f85230b62dc8a9 @@ -18239,7 +18654,7 @@ __metadata: languageName: node linkType: hard -"es-shim-unscopables@npm:^1.0.2": +"es-shim-unscopables@npm:^1.0.0, es-shim-unscopables@npm:^1.0.2": version: 1.0.2 resolution: "es-shim-unscopables@npm:1.0.2" dependencies: @@ -18566,7 +18981,7 @@ __metadata: languageName: node linkType: hard -"eslint-config-prettier@npm:^10.0.1": +"eslint-config-prettier@npm:^10.0.1, eslint-config-prettier@npm:^10.1.5": version: 10.1.5 resolution: "eslint-config-prettier@npm:10.1.5" peerDependencies: @@ -18608,6 +19023,18 @@ __metadata: languageName: node linkType: hard +"eslint-module-utils@npm:^2.12.0": + version: 2.12.0 + resolution: "eslint-module-utils@npm:2.12.0" + dependencies: + debug: "npm:^3.2.7" + peerDependenciesMeta: + eslint: + optional: true + checksum: 10/dd27791147eca17366afcb83f47d6825b6ce164abb256681e5de4ec1d7e87d8605641eb869298a0dbc70665e2446dbcc2f40d3e1631a9475dd64dd23d4ca5dee + languageName: node + linkType: hard + "eslint-module-utils@npm:^2.12.1": version: 2.12.1 resolution: "eslint-module-utils@npm:2.12.1" @@ -18620,6 +19047,35 @@ __metadata: languageName: node linkType: hard +"eslint-plugin-import@npm:^2.31.0": + version: 2.31.0 + resolution: "eslint-plugin-import@npm:2.31.0" + dependencies: + "@rtsao/scc": "npm:^1.1.0" + array-includes: "npm:^3.1.8" + array.prototype.findlastindex: "npm:^1.2.5" + array.prototype.flat: "npm:^1.3.2" + array.prototype.flatmap: "npm:^1.3.2" + debug: "npm:^3.2.7" + doctrine: "npm:^2.1.0" + eslint-import-resolver-node: "npm:^0.3.9" + eslint-module-utils: "npm:^2.12.0" + hasown: "npm:^2.0.2" + is-core-module: "npm:^2.15.1" + is-glob: "npm:^4.0.3" + minimatch: "npm:^3.1.2" + object.fromentries: "npm:^2.0.8" + object.groupby: "npm:^1.0.3" + object.values: "npm:^1.2.0" + semver: "npm:^6.3.1" + string.prototype.trimend: "npm:^1.0.8" + tsconfig-paths: "npm:^3.15.0" + peerDependencies: + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 + checksum: 10/6b76bd009ac2db0615d9019699d18e2a51a86cb8c1d0855a35fb1b418be23b40239e6debdc6e8c92c59f1468ed0ea8d7b85c817117a113d5cc225be8a02ad31c + languageName: node + linkType: hard + "eslint-plugin-import@npm:^2.32.0": version: 2.32.0 resolution: "eslint-plugin-import@npm:2.32.0" @@ -18748,6 +19204,56 @@ __metadata: languageName: node linkType: hard +"eslint@npm:^9.27.0": + version: 9.30.1 + resolution: "eslint@npm:9.30.1" + dependencies: + "@eslint-community/eslint-utils": "npm:^4.2.0" + "@eslint-community/regexpp": "npm:^4.12.1" + "@eslint/config-array": "npm:^0.21.0" + "@eslint/config-helpers": "npm:^0.3.0" + "@eslint/core": "npm:^0.14.0" + "@eslint/eslintrc": "npm:^3.3.1" + "@eslint/js": "npm:9.30.1" + "@eslint/plugin-kit": "npm:^0.3.1" + "@humanfs/node": "npm:^0.16.6" + "@humanwhocodes/module-importer": "npm:^1.0.1" + "@humanwhocodes/retry": "npm:^0.4.2" + "@types/estree": "npm:^1.0.6" + "@types/json-schema": "npm:^7.0.15" + ajv: "npm:^6.12.4" + chalk: "npm:^4.0.0" + cross-spawn: "npm:^7.0.6" + debug: "npm:^4.3.2" + escape-string-regexp: "npm:^4.0.0" + eslint-scope: "npm:^8.4.0" + eslint-visitor-keys: "npm:^4.2.1" + espree: "npm:^10.4.0" + esquery: "npm:^1.5.0" + esutils: "npm:^2.0.2" + fast-deep-equal: "npm:^3.1.3" + file-entry-cache: "npm:^8.0.0" + find-up: "npm:^5.0.0" + glob-parent: "npm:^6.0.2" + ignore: "npm:^5.2.0" + imurmurhash: "npm:^0.1.4" + is-glob: "npm:^4.0.0" + json-stable-stringify-without-jsonify: "npm:^1.0.1" + lodash.merge: "npm:^4.6.2" + minimatch: "npm:^3.1.2" + natural-compare: "npm:^1.4.0" + optionator: "npm:^0.9.3" + peerDependencies: + jiti: "*" + peerDependenciesMeta: + jiti: + optional: true + bin: + eslint: bin/eslint.js + checksum: 10/bd3024f730c88a2d099455f5ec5be2d6293eaf1412eb5111427282e1060b7f87891168ccbded10b71fc7182e05151e42511ec29d4e0d4ece5e3151eaf8d54763 + languageName: node + linkType: hard + "eslint@npm:^9.32.0": version: 9.32.0 resolution: "eslint@npm:9.32.0" @@ -20347,6 +20853,13 @@ __metadata: languageName: node linkType: hard +"google-protobuf@npm:^3.21.4": + version: 3.21.4 + resolution: "google-protobuf@npm:3.21.4" + checksum: 10/0d87fe8ef221d105cbaa808f4024bd577638524d8e461469e3733f2e4933391ad4da86b7fcbd11e8781bee04eacf2e8ba19aaacd5f9deb336a220485841d980f + languageName: node + linkType: hard + "gopd@npm:^1.0.1": version: 1.0.1 resolution: "gopd@npm:1.0.1" @@ -20459,6 +20972,13 @@ __metadata: languageName: node linkType: hard +"grpc-web@npm:^1.5.0": + version: 1.5.0 + resolution: "grpc-web@npm:1.5.0" + checksum: 10/e9791831cf9fc8762d9a832c3df9d885d7a9a5d98333e51e4920c66679cff5f015b2f286f32366eb66cf5347c0d9f02c54f195c55f5cf334c026bc0398a13ce1 + languageName: node + linkType: hard + "gzip-size@npm:^6.0.0": version: 6.0.0 resolution: "gzip-size@npm:6.0.0" @@ -21433,7 +21953,7 @@ __metadata: languageName: node linkType: hard -"is-core-module@npm:^2.13.0": +"is-core-module@npm:^2.13.0, is-core-module@npm:^2.15.1": version: 2.15.1 resolution: "is-core-module@npm:2.15.1" dependencies: @@ -23159,7 +23679,7 @@ __metadata: languageName: node linkType: hard -"lodash.isequal@npm:4.5.0": +"lodash.isequal@npm:4.5.0, lodash.isequal@npm:^4.5.0": version: 4.5.0 resolution: "lodash.isequal@npm:4.5.0" checksum: 10/82fc58a83a1555f8df34ca9a2cd300995ff94018ac12cc47c349655f0ae1d4d92ba346db4c19bbfc90510764e0c00ddcc985a358bdcd4b3b965abf8f2a48a214 @@ -24961,6 +25481,17 @@ __metadata: languageName: node linkType: hard +"object.values@npm:^1.2.0": + version: 1.2.0 + resolution: "object.values@npm:1.2.0" + dependencies: + call-bind: "npm:^1.0.7" + define-properties: "npm:^1.2.1" + es-object-atoms: "npm:^1.0.0" + checksum: 10/db2e498019c354428c5dd30d02980d920ac365b155fce4dcf63eb9433f98ccf0f72624309e182ce7cc227c95e45d474e1d483418e60de2293dd23fa3ebe34903 + languageName: node + linkType: hard + "object.values@npm:^1.2.1": version: 1.2.1 resolution: "object.values@npm:1.2.1" @@ -26324,6 +26855,15 @@ __metadata: languageName: node linkType: hard +"prettier@npm:^3.5.3": + version: 3.5.3 + resolution: "prettier@npm:3.5.3" + bin: + prettier: bin/prettier.cjs + checksum: 10/7050c08f674d9e49fbd9a4c008291d0715471f64e94cc5e4b01729affce221dfc6875c8de7e66b728c64abc9352eefb7eaae071b5f79d30081be207b53774b78 + languageName: node + linkType: hard + "prettier@npm:^3.6.2": version: 3.6.2 resolution: "prettier@npm:3.6.2" @@ -28078,7 +28618,7 @@ __metadata: languageName: node linkType: hard -"secure-random@npm:1.1.2": +"secure-random@npm:1.1.2, secure-random@npm:^1.1.2": version: 1.1.2 resolution: "secure-random@npm:1.1.2" checksum: 10/8b5d32df870346a9400a6a31c0c4da4862df0416289fef7ec8bfda1187cf2ae08a281f57d174287f9a43b5e757478d22631c7dc2582fddcd2cfafc1d92042d72 @@ -30399,6 +30939,20 @@ __metadata: languageName: node linkType: hard +"typescript-eslint@npm:8.33.0": + version: 8.33.0 + resolution: "typescript-eslint@npm:8.33.0" + dependencies: + "@typescript-eslint/eslint-plugin": "npm:8.33.0" + "@typescript-eslint/parser": "npm:8.33.0" + "@typescript-eslint/utils": "npm:8.33.0" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <5.9.0" + checksum: 10/45c3ac8859eea71171f1ffb422fa71c7f5b60ea3b01ed4e655d9604057408b144c961c52fa07c9d75c65d18ffbf3a0c433ff688a48a73b7afe5bcc4811fb401e + languageName: node + linkType: hard + "typescript-eslint@npm:8.38.0": version: 8.38.0 resolution: "typescript-eslint@npm:8.38.0" @@ -30953,6 +31507,16 @@ __metadata: languageName: node linkType: hard +"utf-8-validate@npm:^6.0.2": + version: 6.0.5 + resolution: "utf-8-validate@npm:6.0.5" + dependencies: + node-gyp: "npm:latest" + node-gyp-build: "npm:^4.3.0" + checksum: 10/8c96d342064d3f03d7acf616fe727e484825f4f5f7a455059122787306b2df1a4e23c2d27f16bf7ba21293f4ce6ab3e683b893fe7b4c74ac9d43b871c10001a0 + languageName: node + linkType: hard + "utf-8-validate@npm:^6.0.3": version: 6.0.4 resolution: "utf-8-validate@npm:6.0.4" @@ -31101,6 +31665,13 @@ __metadata: languageName: node linkType: hard +"varint@npm:^6.0.0": + version: 6.0.0 + resolution: "varint@npm:6.0.0" + checksum: 10/7684113c9d497c01e40396e50169c502eb2176203219b96e1c5ac965a3e15b4892bd22b7e48d87148e10fffe638130516b6dbeedd0efde2b2d0395aa1772eea7 + languageName: node + linkType: hard + "varuint-bitcoin@npm:1.1.2, varuint-bitcoin@npm:^1.0.4, varuint-bitcoin@npm:^1.1.2": version: 1.1.2 resolution: "varuint-bitcoin@npm:1.1.2" @@ -31311,7 +31882,7 @@ __metadata: languageName: node linkType: hard -"vitest@npm:^3.2.4": +"vitest@npm:^3.1.4, vitest@npm:^3.2.4": version: 3.2.4 resolution: "vitest@npm:3.2.4" dependencies: