-
Notifications
You must be signed in to change notification settings - Fork 237
feat: Massa network #739
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
feat: Massa network #739
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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", | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we set this to a prod version? |
||
| "@metamask/eth-sig-util": "^8.2.0", | ||
| "@metaplex-foundation/mpl-bubblegum": "^5.0.2", | ||
| "@metaplex-foundation/umi": "^1.2.0", | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -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)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+38
to
+41
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Bug: requestProvider is initialized with an empty URL, breaking first requests Initializing with '' means early calls to request() will target an invalid endpoint until setRequestProvider() is invoked elsewhere. Also, notification wiring is duplicated here and will be lost on provider replacement. Apply this diff to initialize with the actual network URL and centralize wiring via a helper (see next comment for the helper): - this.requestProvider = getRequestProvider('', this.middlewares);
- this.requestProvider.on('notification', (notif: any) => {
- this.sendNotification(JSON.stringify(notif));
- });
+ this.initRequestProvider(this.network.node);
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.namespace = ProviderName.massa; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.KeyRing = new PublicKeyRing(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.UIRoutes = UIRoutes; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private setMiddleWares(): void { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+47
to
+48
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add a helper to consistently initialize and wire the request provider (incl. notifications) This keeps constructor and network changes DRY and prevents missing event subscriptions when replacing the provider. + private initRequestProvider(node: string): void {
+ this.requestProvider = getRequestProvider(node, this.middlewares);
+ this.requestProvider.on('notification', (notif: unknown) => {
+ void this.sendNotification(JSON.stringify(notif));
+ });
+ }
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+52
to
+55
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainHarden URL parsing; new URL() will throw if network.node lacks a protocol If any Massa network config provides a node without an explicit protocol, this will throw and leave the provider in a bad state. Guard it and fall back to re-init. Proposed hardening: - const prevURL = new URL(this.network.node);
- const newURL = new URL(network.node);
+ let prevURL: URL;
+ let newURL: URL;
+ try {
+ prevURL = new URL(this.network.node);
+ newURL = new URL(network.node);
+ } catch {
+ this.network = network;
+ this.initRequestProvider(network.node);
+ return;
+ }To verify configs contain absolute URLs with protocols, run: 🏁 Script executed: #!/bin/bash
# Inspect Massa network configs to ensure node URLs include protocol (http/https/ws/wss)
set -euo pipefail
echo "Searching Massa networks definitions..."
fd -a --extension ts networks packages/extension/src/providers/massa | while read -r f; do
echo "=== File: $f ==="
rg -n "node\s*:" -A1 -B1 "$f"
done
echo
echo "Heuristic check for protocols:"
rg -n "node\s*:\s*['\"](http|https|ws|wss)://" packages/extension/src/providers/massa || {
echo "WARNING: No nodes with explicit protocols found by heuristic. Please confirm definitions."
}Length of output: 549 Harden URL parsing in setRequestProvider Our Massa network definitions currently don’t include explicit protocols (scan found no • File: setRequestProvider(network: BaseNetwork): void {
- const prevURL = new URL(this.network.node);
- const newURL = new URL(network.node);
+ let prevURL: URL;
+ let newURL: URL;
+ try {
+ prevURL = new URL(this.network.node);
+ newURL = new URL(network.node);
+ } catch {
+ this.network = network;
+ this.initRequestProvider(network.node);
+ return;
+ }
this.network = network;This ensures that if a node URL is missing its protocol, we safely re-init rather than crash the provider. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (prevURL.protocol === newURL.protocol) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.requestProvider.changeNetwork(network.node); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.requestProvider = getRequestProvider(network.node, this.middlewares); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+52
to
+60
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Notifications lost after protocol change; reinitialize provider to re-subscribe listeners In the else branch you replace the provider instance but don’t re-attach the 'notification' handler, so notifications will stop after switching protocol (e.g., http → https). setRequestProvider(network: BaseNetwork): void {
- const prevURL = new URL(this.network.node);
- const newURL = new URL(network.node);
+ 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);
+ this.initRequestProvider(network.node);
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async isPersistentEvent(): Promise<boolean> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async sendNotification(notif: string): Promise<void> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return this.toWindow(notif); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| request(request: ProviderRPCRequest): Promise<OnMessageResponse> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return this.requestProvider | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .request(request) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .then(res => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result: JSON.stringify(res), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .catch(e => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| error: JSON.stringify(e.message), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+70
to
+83
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Preserve structured error info instead of stringifying only e.message Only sending e.message loses error code/data and may hinder UI handling (e.g., EIP-1193-like error codes). Serialize a structured error payload. request(request: ProviderRPCRequest): Promise<OnMessageResponse> {
return this.requestProvider
.request(request)
.then(res => {
return {
result: JSON.stringify(res),
};
})
- .catch(e => {
- return {
- error: JSON.stringify(e.message),
- };
- });
+ .catch((e: unknown) => {
+ const err =
+ e && typeof e === 'object'
+ ? { message: (e as any).message ?? String(e), code: (e as any).code }
+ : { message: String(e) };
+ return { error: JSON.stringify(err) };
+ });
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| getUIPath(page: string): string { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return GetUIPath(page, this.namespace); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| getCurrentNetwork(): BaseNetwork { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return this.network; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| import MassaActivity from './massa'; | ||
|
|
||
| export { MassaActivity }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<Activity[]> => { | ||
| 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; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<void> {} | ||
|
|
||
| async getBalance(address: string): Promise<string> { | ||
| const [account] = await this.provider.balanceOf([address], false); | ||
| if (!account) return '0'; | ||
| return account.balance.toString(); | ||
| } | ||
|
Comment on lines
+28
to
+31
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add input validation for address parameter The async getBalance(address: string): Promise<string> {
+ if (!address || !address.trim()) {
+ return '0';
+ }
const [account] = await this.provider.balanceOf([address], false);
if (!account) return '0';
return account.balance.toString();
}🤖 Prompt for AI Agents |
||
|
|
||
| async getBalanceMRC20(address: string, contract: string): Promise<string> { | ||
| const mrc20 = new MRC20(this.provider, contract); | ||
| const balance = await mrc20.balanceOf(address); | ||
| return balance.toString(); | ||
| } | ||
|
|
||
| async getMinimalFee(): Promise<string> { | ||
| 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<OperationStatus | null> { | ||
| try { | ||
| return this.provider.getOperationStatus(opId); | ||
| } catch (error) { | ||
| return null; | ||
| } | ||
| } | ||
|
|
||
| async getNodeStatus(): Promise<NodeStatusInfo> { | ||
| return this.provider.getNodeStatus(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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(); | ||
| }, | ||
| ]; | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Verify Massa Web3 dev version consistency and stability.
The extension uses the same dev version
5.2.1-dev.20250730141858as the signer package. This development version should be verified for stability and consistency across the codebase.🏁 Script executed:
Length of output: 1416
Standardize @massalabs/massa-web3 dev version across all packages
The extension is pinned to
5.2.1-dev.20250730141858, while both the types and signer packages use^5.2.1-dev. This mismatch can lead to drift or unexpected behavior when newer dev builds are released.Action items:
• If you want automatic updates to the latest dev build, use
^5.2.1-deveverywhere.• If you require a reproducible timestamped build, pin all three packages to
5.2.1-dev.20250730141858.Affected files:
🤖 Prompt for AI Agents