@hardkas/l2 0.2.2-alpha.1 → 0.4.0-alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +50 -4
- package/dist/index.js +108 -37
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -10,9 +10,26 @@ interface L2SecurityAssumptions {
|
|
|
10
10
|
readonly riskProfile: L2RiskProfile;
|
|
11
11
|
readonly notes: readonly string[];
|
|
12
12
|
}
|
|
13
|
+
type L2ProfileSource = "built-in" | "user-config";
|
|
14
|
+
interface L2UserNetworkConfig {
|
|
15
|
+
readonly kind?: "igra" | string;
|
|
16
|
+
readonly chainId: number;
|
|
17
|
+
readonly rpcUrl: string;
|
|
18
|
+
readonly explorerUrl?: string;
|
|
19
|
+
readonly bridgePhase?: L2BridgePhase;
|
|
20
|
+
readonly trustlessExit?: boolean;
|
|
21
|
+
readonly nativeCurrency?: {
|
|
22
|
+
readonly name?: string;
|
|
23
|
+
readonly symbol?: string;
|
|
24
|
+
readonly decimals?: number;
|
|
25
|
+
};
|
|
26
|
+
readonly contracts?: Record<string, string>;
|
|
27
|
+
readonly metadata?: Record<string, unknown>;
|
|
28
|
+
}
|
|
13
29
|
interface L2NetworkProfile {
|
|
14
30
|
readonly schema: "hardkas.l2Profile.v1";
|
|
15
31
|
readonly hardkasVersion: string;
|
|
32
|
+
readonly source: L2ProfileSource;
|
|
16
33
|
readonly name: string;
|
|
17
34
|
readonly displayName: string;
|
|
18
35
|
readonly type: L2NetworkType;
|
|
@@ -27,9 +44,38 @@ interface L2NetworkProfile {
|
|
|
27
44
|
}
|
|
28
45
|
declare const BUILTIN_L2_PROFILES: readonly L2NetworkProfile[];
|
|
29
46
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
47
|
+
interface MetaMaskChainParams {
|
|
48
|
+
chainId: string;
|
|
49
|
+
chainName: string;
|
|
50
|
+
nativeCurrency: {
|
|
51
|
+
name: string;
|
|
52
|
+
symbol: string;
|
|
53
|
+
decimals: number;
|
|
54
|
+
};
|
|
55
|
+
rpcUrls: string[];
|
|
56
|
+
blockExplorerUrls?: string[] | undefined;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Generates the standard wallet_addEthereumChain payload for MetaMask.
|
|
60
|
+
*/
|
|
61
|
+
declare function generateAddEthereumChainPayload(profile: L2NetworkProfile): MetaMaskChainParams;
|
|
62
|
+
/**
|
|
63
|
+
* Generates a JavaScript snippet for browser consoles to add the network to MetaMask.
|
|
64
|
+
*/
|
|
65
|
+
declare function generateMetaMaskSnippet(profile: L2NetworkProfile): string;
|
|
66
|
+
|
|
67
|
+
declare function listL2Profiles(userProfiles?: Record<string, L2UserNetworkConfig>): L2NetworkProfile[];
|
|
68
|
+
declare function getL2Profile(name: string, userProfiles?: Record<string, L2UserNetworkConfig>): L2NetworkProfile | null;
|
|
69
|
+
declare function resolveL2Profile(args: {
|
|
70
|
+
name?: string | undefined;
|
|
71
|
+
userProfiles?: Record<string, L2UserNetworkConfig> | undefined;
|
|
72
|
+
cliOverrides?: {
|
|
73
|
+
url?: string | undefined;
|
|
74
|
+
rpcUrl?: string | undefined;
|
|
75
|
+
chainId?: number | string | undefined;
|
|
76
|
+
[key: string]: unknown;
|
|
77
|
+
} | undefined;
|
|
78
|
+
}): L2NetworkProfile;
|
|
33
79
|
declare function validateL2Profile(profile: any): {
|
|
34
80
|
ok: boolean;
|
|
35
81
|
errors: string[];
|
|
@@ -174,4 +220,4 @@ declare function assertValidL2BridgeAssumptions(input: unknown): L2BridgeAssumpt
|
|
|
174
220
|
*/
|
|
175
221
|
declare function encodeConstructorArgs(bytecode: string, constructorSignature: string, args: string[]): string;
|
|
176
222
|
|
|
177
|
-
export { BUILTIN_L2_PROFILES, type EvmCallRequest, EvmJsonRpcClient, type EvmJsonRpcClientOptions, type EvmRpcHealthResult, type EvmTransactionReceiptSummary, type IgraTxSigner, type IgraTxSigningInput, type IgraTxSigningResult, type L2BridgeAssumptions, type L2BridgePhase, type L2NetworkProfile, type L2NetworkType, type L2RiskProfile, type L2SecurityAssumptions, UnsupportedIgraTxSigner, ViemIgraTxSigner, type ViemIgraTxSignerOptions, assertValidL2BridgeAssumptions, assertValidL2Profile, checkEvmRpcHealth, encodeConstructorArgs, formatWeiAsEtherLike,
|
|
223
|
+
export { BUILTIN_L2_PROFILES, type EvmCallRequest, EvmJsonRpcClient, type EvmJsonRpcClientOptions, type EvmRpcHealthResult, type EvmTransactionReceiptSummary, type IgraTxSigner, type IgraTxSigningInput, type IgraTxSigningResult, type L2BridgeAssumptions, type L2BridgePhase, type L2NetworkProfile, type L2NetworkType, type L2ProfileSource, type L2RiskProfile, type L2SecurityAssumptions, type L2UserNetworkConfig, type MetaMaskChainParams, UnsupportedIgraTxSigner, ViemIgraTxSigner, type ViemIgraTxSignerOptions, assertValidL2BridgeAssumptions, assertValidL2Profile, checkEvmRpcHealth, encodeConstructorArgs, formatWeiAsEtherLike, generateAddEthereumChainPayload, generateMetaMaskSnippet, getL2BridgeAssumptions, resolveL2Profile as getL2NetworkProfile, getL2Profile, listL2BridgeAssumptions, listL2Profiles, normalizeEvmTransactionReceipt, resolveL2Profile, toHexQuantity, validateL2BridgeAssumptions, validateL2Profile, waitForEvmRpcReady };
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ var BUILTIN_L2_PROFILES = [
|
|
|
4
4
|
{
|
|
5
5
|
schema: "hardkas.l2Profile.v1",
|
|
6
6
|
hardkasVersion: HARDKAS_VERSION,
|
|
7
|
+
source: "built-in",
|
|
7
8
|
name: "igra",
|
|
8
9
|
displayName: "Igra",
|
|
9
10
|
type: "evm-based-rollup",
|
|
@@ -11,6 +12,8 @@ var BUILTIN_L2_PROFILES = [
|
|
|
11
12
|
executionLayer: "evm",
|
|
12
13
|
gasToken: "iKAS",
|
|
13
14
|
nativeTokenDecimals: 18,
|
|
15
|
+
chainId: 19416,
|
|
16
|
+
rpcUrl: "http://127.0.0.1:8545",
|
|
14
17
|
security: {
|
|
15
18
|
bridgePhase: "pre-zk",
|
|
16
19
|
trustlessExit: false,
|
|
@@ -27,15 +30,106 @@ var BUILTIN_L2_PROFILES = [
|
|
|
27
30
|
}
|
|
28
31
|
];
|
|
29
32
|
|
|
33
|
+
// src/metamask.ts
|
|
34
|
+
function generateAddEthereumChainPayload(profile) {
|
|
35
|
+
if (!profile.chainId) {
|
|
36
|
+
throw new Error(`Profile "${profile.name}" is missing chainId`);
|
|
37
|
+
}
|
|
38
|
+
if (!profile.rpcUrl) {
|
|
39
|
+
throw new Error(`Profile "${profile.name}" is missing rpcUrl`);
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
chainId: `0x${profile.chainId.toString(16)}`,
|
|
43
|
+
chainName: profile.displayName || profile.name,
|
|
44
|
+
nativeCurrency: {
|
|
45
|
+
name: profile.gasToken || "iKAS",
|
|
46
|
+
symbol: profile.gasToken || "iKAS",
|
|
47
|
+
decimals: profile.nativeTokenDecimals || 18
|
|
48
|
+
},
|
|
49
|
+
rpcUrls: [profile.rpcUrl],
|
|
50
|
+
blockExplorerUrls: profile.explorerUrl ? [profile.explorerUrl] : void 0
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function generateMetaMaskSnippet(profile) {
|
|
54
|
+
const payload = generateAddEthereumChainPayload(profile);
|
|
55
|
+
return `await window.ethereum.request({
|
|
56
|
+
method: "wallet_addEthereumChain",
|
|
57
|
+
params: [
|
|
58
|
+
${JSON.stringify(payload, null, 4).replace(/\n/g, "\n ")}
|
|
59
|
+
]
|
|
60
|
+
});`;
|
|
61
|
+
}
|
|
62
|
+
|
|
30
63
|
// src/registry.ts
|
|
31
|
-
|
|
32
|
-
|
|
64
|
+
import { HARDKAS_VERSION as HARDKAS_VERSION2 } from "@hardkas/artifacts";
|
|
65
|
+
function listL2Profiles(userProfiles) {
|
|
66
|
+
const profiles = [...BUILTIN_L2_PROFILES];
|
|
67
|
+
if (userProfiles) {
|
|
68
|
+
for (const [name, config] of Object.entries(userProfiles)) {
|
|
69
|
+
const existingIndex = profiles.findIndex((p) => p.name === name);
|
|
70
|
+
const profile = mapUserConfigToProfile(name, config);
|
|
71
|
+
if (existingIndex !== -1) {
|
|
72
|
+
profiles[existingIndex] = profile;
|
|
73
|
+
} else {
|
|
74
|
+
profiles.push(profile);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return profiles;
|
|
79
|
+
}
|
|
80
|
+
function getL2Profile(name, userProfiles) {
|
|
81
|
+
return listL2Profiles(userProfiles).find((p) => p.name === name) || null;
|
|
33
82
|
}
|
|
34
|
-
function
|
|
35
|
-
|
|
83
|
+
function resolveL2Profile(args) {
|
|
84
|
+
const name = args.name || "igra";
|
|
85
|
+
const profile = getL2Profile(name, args.userProfiles);
|
|
86
|
+
if (!profile) {
|
|
87
|
+
const available = listL2Profiles(args.userProfiles).map((p) => `${p.name} (${p.source})`).join(", ");
|
|
88
|
+
throw new Error(`L2 profile '${name}' not found. Available profiles: ${available}`);
|
|
89
|
+
}
|
|
90
|
+
let rpcUrl = args.cliOverrides?.rpcUrl || args.cliOverrides?.url || profile.rpcUrl;
|
|
91
|
+
if (args.cliOverrides?.rpcUrl && args.cliOverrides?.url && args.cliOverrides.rpcUrl !== args.cliOverrides.url) {
|
|
92
|
+
throw new Error("Conflict: Both --rpc-url and --url provided with different values.");
|
|
93
|
+
}
|
|
94
|
+
let chainId = profile.chainId;
|
|
95
|
+
if (args.cliOverrides?.chainId !== void 0) {
|
|
96
|
+
chainId = typeof args.cliOverrides.chainId === "string" ? parseInt(args.cliOverrides.chainId, 10) : args.cliOverrides.chainId;
|
|
97
|
+
if (isNaN(chainId)) {
|
|
98
|
+
throw new Error(`Invalid chainId: ${args.cliOverrides.chainId}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const resolved = {
|
|
102
|
+
...profile,
|
|
103
|
+
...rpcUrl ? { rpcUrl } : {},
|
|
104
|
+
...chainId !== void 0 ? { chainId } : {}
|
|
105
|
+
};
|
|
106
|
+
return assertValidL2Profile(resolved);
|
|
36
107
|
}
|
|
37
|
-
function
|
|
38
|
-
|
|
108
|
+
function mapUserConfigToProfile(name, config) {
|
|
109
|
+
const bridgePhase = config.bridgePhase || "pre-zk";
|
|
110
|
+
const trustlessExit = config.trustlessExit ?? false;
|
|
111
|
+
return {
|
|
112
|
+
schema: "hardkas.l2Profile.v1",
|
|
113
|
+
hardkasVersion: HARDKAS_VERSION2,
|
|
114
|
+
source: "user-config",
|
|
115
|
+
name,
|
|
116
|
+
displayName: name,
|
|
117
|
+
type: "evm-based-rollup",
|
|
118
|
+
settlementLayer: "kaspa",
|
|
119
|
+
executionLayer: "evm",
|
|
120
|
+
gasToken: config.nativeCurrency?.symbol || (config.kind === "igra" || !config.kind ? "iKAS" : "ETH"),
|
|
121
|
+
nativeTokenDecimals: config.nativeCurrency?.decimals || 18,
|
|
122
|
+
chainId: config.chainId,
|
|
123
|
+
rpcUrl: config.rpcUrl,
|
|
124
|
+
...config.explorerUrl ? { explorerUrl: config.explorerUrl } : {},
|
|
125
|
+
security: {
|
|
126
|
+
bridgePhase,
|
|
127
|
+
trustlessExit,
|
|
128
|
+
custodyModel: "User-defined bridge custody.",
|
|
129
|
+
riskProfile: "unknown",
|
|
130
|
+
notes: ["User-defined network from config"]
|
|
131
|
+
}
|
|
132
|
+
};
|
|
39
133
|
}
|
|
40
134
|
function validateL2Profile(profile) {
|
|
41
135
|
const errors = [];
|
|
@@ -45,36 +139,10 @@ function validateL2Profile(profile) {
|
|
|
45
139
|
if (profile.schema !== "hardkas.l2Profile.v1") {
|
|
46
140
|
errors.push(`Invalid schema: expected 'hardkas.l2Profile.v1', got '${profile.schema}'`);
|
|
47
141
|
}
|
|
48
|
-
if (typeof profile.hardkasVersion !== "string") {
|
|
49
|
-
errors.push("Missing or invalid hardkasVersion");
|
|
50
|
-
}
|
|
51
|
-
if (!profile.name || typeof profile.name !== "string") {
|
|
52
|
-
errors.push("Missing or invalid name");
|
|
53
|
-
}
|
|
54
|
-
if (profile.type !== "evm-based-rollup") {
|
|
55
|
-
errors.push(`Invalid type: expected 'evm-based-rollup', got '${profile.type}'`);
|
|
56
|
-
}
|
|
57
|
-
if (profile.settlementLayer !== "kaspa") {
|
|
58
|
-
errors.push(`Invalid settlementLayer: expected 'kaspa', got '${profile.settlementLayer}'`);
|
|
59
|
-
}
|
|
60
|
-
if (profile.executionLayer !== "evm") {
|
|
61
|
-
errors.push(`Invalid executionLayer: expected 'evm', got '${profile.executionLayer}'`);
|
|
62
|
-
}
|
|
63
|
-
if (!profile.gasToken || typeof profile.gasToken !== "string") {
|
|
64
|
-
errors.push("Missing or invalid gasToken");
|
|
65
|
-
}
|
|
66
142
|
if (profile.security) {
|
|
67
|
-
if (profile.security.bridgePhase
|
|
68
|
-
|
|
69
|
-
if (profile.security.trustlessExit === true) {
|
|
70
|
-
errors.push("Security invariant violation: trustlessExit must be false when bridgePhase is not 'zk'");
|
|
71
|
-
}
|
|
143
|
+
if (profile.security.bridgePhase !== "zk" && profile.security.trustlessExit === true) {
|
|
144
|
+
errors.push(`Security invariant violation: trustlessExit=true is only allowed when bridgePhase='zk'. Current phase: ${profile.security.bridgePhase}`);
|
|
72
145
|
}
|
|
73
|
-
if (!Array.isArray(profile.security.notes) || profile.security.notes.length === 0) {
|
|
74
|
-
errors.push("Security notes must not be empty");
|
|
75
|
-
}
|
|
76
|
-
} else {
|
|
77
|
-
errors.push("Missing security assumptions");
|
|
78
146
|
}
|
|
79
147
|
return {
|
|
80
148
|
ok: errors.length === 0,
|
|
@@ -370,10 +438,10 @@ function normalizeEvmTransactionReceipt(raw) {
|
|
|
370
438
|
}
|
|
371
439
|
|
|
372
440
|
// src/bridge.ts
|
|
373
|
-
import { HARDKAS_VERSION as
|
|
441
|
+
import { HARDKAS_VERSION as HARDKAS_VERSION3 } from "@hardkas/artifacts";
|
|
374
442
|
var IGRA_BRIDGE_ASSUMPTIONS = {
|
|
375
443
|
schema: "hardkas.l2BridgeAssumptions.v1",
|
|
376
|
-
hardkasVersion:
|
|
444
|
+
hardkasVersion: HARDKAS_VERSION3,
|
|
377
445
|
l2Network: "igra",
|
|
378
446
|
bridgePhase: "pre-zk",
|
|
379
447
|
trustlessExit: false,
|
|
@@ -457,12 +525,15 @@ export {
|
|
|
457
525
|
checkEvmRpcHealth,
|
|
458
526
|
encodeConstructorArgs,
|
|
459
527
|
formatWeiAsEtherLike,
|
|
460
|
-
|
|
528
|
+
generateAddEthereumChainPayload,
|
|
529
|
+
generateMetaMaskSnippet,
|
|
461
530
|
getL2BridgeAssumptions,
|
|
531
|
+
resolveL2Profile as getL2NetworkProfile,
|
|
462
532
|
getL2Profile,
|
|
463
533
|
listL2BridgeAssumptions,
|
|
464
534
|
listL2Profiles,
|
|
465
535
|
normalizeEvmTransactionReceipt,
|
|
536
|
+
resolveL2Profile,
|
|
466
537
|
toHexQuantity,
|
|
467
538
|
validateL2BridgeAssumptions,
|
|
468
539
|
validateL2Profile,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hardkas/l2",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0-alpha",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dist/index.js"
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"types": "./dist/index.d.ts",
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"viem": "^2.48.8",
|
|
11
|
-
"@hardkas/artifacts": "0.
|
|
11
|
+
"@hardkas/artifacts": "0.4.0-alpha"
|
|
12
12
|
},
|
|
13
13
|
"devDependencies": {
|
|
14
14
|
"tsup": "^8.3.5",
|