ethershell 0.1.0-alpha.9 → 0.1.0-beta.0
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/LICENSE +21 -55
- package/README.md +186 -37
- package/bin/cli.js +12 -3
- package/package.json +10 -3
- package/src/services/addContracts.js +38 -25
- package/src/services/build.js +111 -21
- package/src/services/config.js +15 -0
- package/src/services/files.js +3 -2
- package/src/services/network.js +8 -4
- package/src/services/wallet.js +102 -63
- package/src/utils/accounter.js +77 -7
- package/src/utils/builder.js +15 -0
- package/src/utils/configFileUpdate.js +7 -0
- package/src/utils/contractProxy.js +162 -0
- package/src/utils/serialize.js +17 -0
- package/src/utils/typeGenerator.js +247 -0
package/src/utils/accounter.js
CHANGED
|
@@ -7,6 +7,15 @@
|
|
|
7
7
|
|
|
8
8
|
import { allAccounts, accounts, hdAccounts } from '../services/wallet.js';
|
|
9
9
|
import { provider } from '../services/network.js';
|
|
10
|
+
import fs from 'fs';
|
|
11
|
+
import { configFile, configPath } from '../services/build.js';
|
|
12
|
+
import { serializeBigInts } from './serialize.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The path which in wallets json file will be saved.
|
|
16
|
+
* @type {String}
|
|
17
|
+
*/
|
|
18
|
+
const walletJSONPath = './localStorage/wallets.json';
|
|
10
19
|
|
|
11
20
|
/**
|
|
12
21
|
* Delete account(s) by index
|
|
@@ -22,6 +31,8 @@ export function deleteByIndex(index) {
|
|
|
22
31
|
deleteByIndexArr(index);
|
|
23
32
|
} else if (typeof index === 'number') {
|
|
24
33
|
_deleteBySingIndex(index);
|
|
34
|
+
} else if (index === null || index === undefined) {
|
|
35
|
+
_deleteAll();
|
|
25
36
|
}
|
|
26
37
|
}
|
|
27
38
|
|
|
@@ -82,6 +93,47 @@ export function detectDupWallet(privKeyArr) {
|
|
|
82
93
|
}
|
|
83
94
|
}
|
|
84
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Writes/Updates wallets json file
|
|
98
|
+
* @param {Array<string>} walletArr - Account array
|
|
99
|
+
* @example
|
|
100
|
+
* updateWalletJSON([{
|
|
101
|
+
index: allAccounts.length,
|
|
102
|
+
address: newAccount.address,
|
|
103
|
+
privateKey: privKeyArr,
|
|
104
|
+
type: 'user-imported',
|
|
105
|
+
contracts: []
|
|
106
|
+
}]);
|
|
107
|
+
*/
|
|
108
|
+
export function updateWalletJSON(walletArr) {
|
|
109
|
+
const walletObj = serializeBigInts(walletArr);
|
|
110
|
+
fs.writeFileSync(walletJSONPath, JSON.stringify(walletObj, null, 2));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Returns wallets' object from saved json file
|
|
115
|
+
* @returns {Object}
|
|
116
|
+
*/
|
|
117
|
+
export function getWalletJSON() {
|
|
118
|
+
return JSON.parse(fs.readFileSync(walletJSONPath));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function updateAccountMemory(allAccArr) {
|
|
122
|
+
let memAccArr = [];
|
|
123
|
+
let memHDAccArr = [];
|
|
124
|
+
for(let i = 0; i < allAccArr.length; i++) {
|
|
125
|
+
if(allAccArr[i].phrase) {
|
|
126
|
+
memHDAccArr.push(allAccArr[i])
|
|
127
|
+
} else {
|
|
128
|
+
memAccArr.push(allAccArr[i]);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
memAccArr,
|
|
133
|
+
memHDAccArr
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
85
137
|
/**
|
|
86
138
|
* Get information for multiple accounts (internal)
|
|
87
139
|
* @private
|
|
@@ -127,13 +179,6 @@ async function _getAccountInfo(_index) {
|
|
|
127
179
|
* @returns {void}
|
|
128
180
|
*/
|
|
129
181
|
function _deleteBySingIndex(_index) {
|
|
130
|
-
if (_index === null || _index === undefined) {
|
|
131
|
-
// Clear all arrays
|
|
132
|
-
allAccounts.splice(0);
|
|
133
|
-
accounts.splice(0);
|
|
134
|
-
hdAccounts.splice(0);
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
182
|
|
|
138
183
|
// Find and remove from allAccounts
|
|
139
184
|
const accountIndex = allAccounts.findIndex(acc => acc.index === _index);
|
|
@@ -144,6 +189,9 @@ function _deleteBySingIndex(_index) {
|
|
|
144
189
|
for (let i = accountIndex; i < allAccounts.length; i++) {
|
|
145
190
|
allAccounts[i].index = i;
|
|
146
191
|
}
|
|
192
|
+
|
|
193
|
+
// Update wallet json file
|
|
194
|
+
fs.writeFileSync(walletJSONPath, JSON.stringify(allAccounts, null, 2));
|
|
147
195
|
|
|
148
196
|
// Remove from accounts array if it exists there
|
|
149
197
|
const regularIndex = accounts.findIndex(acc => acc.index === _index);
|
|
@@ -210,4 +258,26 @@ function _findDupWalletByArr(privKeyArr) {
|
|
|
210
258
|
return {
|
|
211
259
|
status: false
|
|
212
260
|
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Removes all accounts from storage and memory
|
|
265
|
+
* @private
|
|
266
|
+
* @returns {null}
|
|
267
|
+
*/
|
|
268
|
+
function _deleteAll() {
|
|
269
|
+
allAccounts.splice(0);
|
|
270
|
+
accounts.splice(0);
|
|
271
|
+
hdAccounts.splice(0);
|
|
272
|
+
fs.writeFileSync(walletJSONPath, JSON.stringify([], null, 2));
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Updates default account in config file
|
|
278
|
+
* @param {Object} account - // The given account to set as default account
|
|
279
|
+
*/
|
|
280
|
+
export function setDefaultAccount(account) {
|
|
281
|
+
configFile.defaultWallet = serializeBigInts(account);
|
|
282
|
+
fs.writeFileSync(configPath, JSON.stringify(configFile, null, 2));
|
|
213
283
|
}
|
package/src/utils/builder.js
CHANGED
|
@@ -167,3 +167,18 @@ export function build(fullPath, selectedContracts, buildPath){
|
|
|
167
167
|
});
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
+
/**
|
|
171
|
+
* Extract loadable version format from full version string
|
|
172
|
+
* @param {string} fullVersion - Full version string (e.g., "0.8.20+commit.a1b79de6.Emscripten.clang")
|
|
173
|
+
* @returns {string} Loadable version format (e.g., "v0.8.20+commit.a1b79de6")
|
|
174
|
+
* @example
|
|
175
|
+
* extractLoadableVersion("0.8.20+commit.a1b79de6.Emscripten.clang"); // Returns: "v0.8.20+commit.a1b79de6"
|
|
176
|
+
*/
|
|
177
|
+
export function extractLoadableVersion(fullVersion) {
|
|
178
|
+
// Match version number and commit hash from full version string
|
|
179
|
+
const match = fullVersion.match(/(\d+\.\d+\.\d+)\+commit\.([a-f0-9]+)/);
|
|
180
|
+
if (!match) {
|
|
181
|
+
throw new Error(`Unable to extract version from: ${fullVersion}`);
|
|
182
|
+
}
|
|
183
|
+
return `v${match[1]}+commit.${match[2]}`;
|
|
184
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
// src/utils/contractProxy.js
|
|
2
|
+
import { ethers } from 'ethers';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Creates a proxy wrapper for ethers.js Contract objects
|
|
6
|
+
* Allows dynamic sender changes and comprehensive transaction options
|
|
7
|
+
*
|
|
8
|
+
* @param {ethers.Contract} contract - The ethers.js contract instance
|
|
9
|
+
* @param {ethers.JsonRpcProvider} provider - The blockchain provider
|
|
10
|
+
* @param {Array} allAccounts - Array of available accounts with privateKey property
|
|
11
|
+
* @returns {Proxy} Proxied contract object
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* // Call with comprehensive options
|
|
15
|
+
* await Payment.spend(amount, {
|
|
16
|
+
* from: '0x...',
|
|
17
|
+
* value: ethers.parseEther('1'),
|
|
18
|
+
* gasLimit: 500000,
|
|
19
|
+
* maxFeePerGas: ethers.parseUnits('100', 'gwei'),
|
|
20
|
+
* maxPriorityFeePerGas: ethers.parseUnits('2', 'gwei'),
|
|
21
|
+
* nonce: 42,
|
|
22
|
+
* chainId: 1
|
|
23
|
+
* })
|
|
24
|
+
*/
|
|
25
|
+
export function createContractProxy(contract, provider, allAccounts) {
|
|
26
|
+
return new Proxy(contract, {
|
|
27
|
+
get(target, prop) {
|
|
28
|
+
// Pass through non-function properties
|
|
29
|
+
if (typeof target[prop] !== 'function') {
|
|
30
|
+
return target[prop];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Return a wrapper for contract methods
|
|
34
|
+
return async function(...args) {
|
|
35
|
+
// Extract options from last argument if it's an object with custom properties
|
|
36
|
+
const lastArg = args[args.length - 1];
|
|
37
|
+
|
|
38
|
+
// List of all valid transaction option keys (except 'data', 'to', 'from' which are handled internally)
|
|
39
|
+
const validTxOptions = [
|
|
40
|
+
'value',
|
|
41
|
+
'nonce',
|
|
42
|
+
'gasLimit',
|
|
43
|
+
'gas', // Maps to gasLimit
|
|
44
|
+
'gasPrice',
|
|
45
|
+
'maxFeePerGas',
|
|
46
|
+
'maxPriorityFeePerGas',
|
|
47
|
+
'chainId',
|
|
48
|
+
'accessList',
|
|
49
|
+
'type',
|
|
50
|
+
'customData',
|
|
51
|
+
'from' // Special handling for signer switching
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
const hasOptions =
|
|
55
|
+
lastArg &&
|
|
56
|
+
typeof lastArg === 'object' &&
|
|
57
|
+
!Array.isArray(lastArg) &&
|
|
58
|
+
Object.keys(lastArg).some(key => validTxOptions.includes(key));
|
|
59
|
+
|
|
60
|
+
// Warn if user tries to pass 'data'
|
|
61
|
+
if (lastArg && typeof lastArg === 'object' && lastArg.data) {
|
|
62
|
+
console.warn(
|
|
63
|
+
"Warning: 'data' option is ignored. " +
|
|
64
|
+
"Function calldata is automatically encoded by ethers.js. " +
|
|
65
|
+
"Use .connect() to change the signer if needed."
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const options = hasOptions ? args.pop() : null;
|
|
70
|
+
|
|
71
|
+
let method = target[prop];
|
|
72
|
+
let txOptions = {};
|
|
73
|
+
|
|
74
|
+
// Handle 'from' option - switch signer if specified
|
|
75
|
+
if (options && options.from) {
|
|
76
|
+
const account = allAccounts.find(
|
|
77
|
+
acc => acc.address?.toLowerCase() === options.from.toLowerCase()
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
if (!account) {
|
|
81
|
+
throw new Error(
|
|
82
|
+
`Account ${options.from} not found in registered accounts`
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (!account.privateKey) {
|
|
87
|
+
throw new Error(
|
|
88
|
+
`Account ${options.from} is a node-managed account and cannot be used with {from}`
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Create new signer with the specified account
|
|
93
|
+
const newSigner = new ethers.Wallet(account.privateKey, provider);
|
|
94
|
+
const connectedContract = target.connect(newSigner);
|
|
95
|
+
method = connectedContract[prop];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Build transaction options object with all supported ethers.js v6 options
|
|
99
|
+
if (options) {
|
|
100
|
+
// Value (for payable functions)
|
|
101
|
+
if (options.value !== undefined) {
|
|
102
|
+
txOptions.value = options.value;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Gas limit (handle both 'gas' and 'gasLimit')
|
|
106
|
+
if (options.gasLimit !== undefined) {
|
|
107
|
+
txOptions.gasLimit = options.gasLimit;
|
|
108
|
+
} else if (options.gas !== undefined) {
|
|
109
|
+
txOptions.gasLimit = options.gas; // Map 'gas' to 'gasLimit'
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Legacy gas price
|
|
113
|
+
if (options.gasPrice !== undefined) {
|
|
114
|
+
txOptions.gasPrice = options.gasPrice;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// EIP-1559 options
|
|
118
|
+
if (options.maxFeePerGas !== undefined) {
|
|
119
|
+
txOptions.maxFeePerGas = options.maxFeePerGas;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (options.maxPriorityFeePerGas !== undefined) {
|
|
123
|
+
txOptions.maxPriorityFeePerGas = options.maxPriorityFeePerGas;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Nonce for transaction ordering
|
|
127
|
+
if (options.nonce !== undefined) {
|
|
128
|
+
txOptions.nonce = options.nonce;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Chain ID
|
|
132
|
+
if (options.chainId !== undefined) {
|
|
133
|
+
txOptions.chainId = options.chainId;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// EIP-2930 access list
|
|
137
|
+
if (options.accessList !== undefined) {
|
|
138
|
+
txOptions.accessList = options.accessList;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Transaction type
|
|
142
|
+
if (options.type !== undefined) {
|
|
143
|
+
txOptions.type = options.type;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Custom data (for special networks like zkSync)
|
|
147
|
+
if (options.customData !== undefined) {
|
|
148
|
+
txOptions.customData = options.customData;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// If there are transaction options, pass them as the last argument
|
|
153
|
+
if (Object.keys(txOptions).length > 0) {
|
|
154
|
+
args.push(txOptions);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Call the method with remaining args and tx options
|
|
158
|
+
return method.apply(method, args);
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function serializeBigInts(obj) {
|
|
2
|
+
try{
|
|
3
|
+
if (Array.isArray(obj)) {
|
|
4
|
+
return obj.map(serializeBigInts);
|
|
5
|
+
} else if (obj && typeof obj === 'object') {
|
|
6
|
+
return Object.fromEntries(
|
|
7
|
+
Object.entries(obj).map(([key, value]) => [
|
|
8
|
+
key,
|
|
9
|
+
typeof value === 'bigint' ? value.toString() : serializeBigInts(value)
|
|
10
|
+
])
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
return obj;
|
|
14
|
+
}catch(err){
|
|
15
|
+
console.error(err);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview TypeScript type generation from Solidity ABIs
|
|
3
|
+
* @description Generates type-safe TypeScript interfaces and classes
|
|
4
|
+
* from compiled Solidity contracts for use in TypeScript projects
|
|
5
|
+
* @module typeGenerator
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import fs from 'fs';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Generate TypeScript types from contract ABI
|
|
13
|
+
* @param {string} contractName - Name of the contract
|
|
14
|
+
* @param {Array} abi - Contract ABI array
|
|
15
|
+
* @param {string} outputPath - Output directory for types
|
|
16
|
+
* @returns {string} Path to generated type file
|
|
17
|
+
*/
|
|
18
|
+
export function generateContractTypes(contractName, abi, outputPath) {
|
|
19
|
+
try {
|
|
20
|
+
// Ensure output directory exists
|
|
21
|
+
if (!fs.existsSync(outputPath)) {
|
|
22
|
+
fs.mkdirSync(outputPath, { recursive: true });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Generate TypeScript interfaces
|
|
26
|
+
const interfaces = generateInterfaces(contractName, abi);
|
|
27
|
+
const typePath = path.join(outputPath, `${contractName}.ts`);
|
|
28
|
+
|
|
29
|
+
fs.writeFileSync(typePath, interfaces);
|
|
30
|
+
|
|
31
|
+
return typePath;
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.error(`Error generating types for ${contractName}:`, error.message);
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Generate all TypeScript types from build artifacts
|
|
40
|
+
* @param {string} buildPath - Path to build directory with ABIs
|
|
41
|
+
* @param {string} typesOutputPath - Output directory for TypeScript types
|
|
42
|
+
* @returns {Array} Array of generated type file paths
|
|
43
|
+
*/
|
|
44
|
+
export function generateAllTypes(buildPath, typesOutputPath = './src/types') {
|
|
45
|
+
try {
|
|
46
|
+
const abisPath = path.join(buildPath, 'abis');
|
|
47
|
+
|
|
48
|
+
if (!fs.existsSync(abisPath)) {
|
|
49
|
+
throw new Error(`ABIs directory not found at ${abisPath}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const abiFiles = fs.readdirSync(abisPath).filter(f => f.endsWith('.abi.json'));
|
|
53
|
+
const generatedFiles = [];
|
|
54
|
+
|
|
55
|
+
abiFiles.forEach(file => {
|
|
56
|
+
const contractName = file.replace('.abi.json', '');
|
|
57
|
+
const abiPath = path.join(abisPath, file);
|
|
58
|
+
const abi = JSON.parse(fs.readFileSync(abiPath, 'utf8'));
|
|
59
|
+
|
|
60
|
+
const typePath = generateContractTypes(contractName, abi, typesOutputPath);
|
|
61
|
+
generatedFiles.push(typePath);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Generate index.ts for barrel export
|
|
65
|
+
generateIndexFile(typesOutputPath, abiFiles);
|
|
66
|
+
|
|
67
|
+
return generatedFiles;
|
|
68
|
+
} catch (error) {
|
|
69
|
+
console.error('Error generating types:', error.message);
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Generate TypeScript interface from ABI
|
|
76
|
+
* @private
|
|
77
|
+
*/
|
|
78
|
+
function generateInterfaces(contractName, abi) {
|
|
79
|
+
const functions = abi.filter(item => item.type === 'function');
|
|
80
|
+
const events = abi.filter(item => item.type === 'event');
|
|
81
|
+
const structs = extractStructTypes(abi);
|
|
82
|
+
|
|
83
|
+
let typescript = `/**
|
|
84
|
+
* Auto-generated types for ${contractName} contract
|
|
85
|
+
* Generated from ABI
|
|
86
|
+
*/
|
|
87
|
+
|
|
88
|
+
// ============= TYPES =============
|
|
89
|
+
${generateStructTypes(structs)}
|
|
90
|
+
|
|
91
|
+
// ============= FUNCTION INPUTS =============
|
|
92
|
+
${generateFunctionTypes(functions, 'input')}
|
|
93
|
+
|
|
94
|
+
// ============= FUNCTION OUTPUTS =============
|
|
95
|
+
${generateFunctionTypes(functions, 'output')}
|
|
96
|
+
|
|
97
|
+
// ============= EVENT TYPES =============
|
|
98
|
+
${generateEventTypes(events)}
|
|
99
|
+
|
|
100
|
+
// ============= CONTRACT INTERFACE =============
|
|
101
|
+
export interface I${contractName} {
|
|
102
|
+
${generateContractMethods(functions)}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ============= ABI EXPORT =============
|
|
106
|
+
export const ${contractName}ABI = ${JSON.stringify(abi, null, 2)} as const;
|
|
107
|
+
`;
|
|
108
|
+
|
|
109
|
+
return typescript;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Generate TypeScript types for function inputs/outputs
|
|
114
|
+
* @private
|
|
115
|
+
*/
|
|
116
|
+
function generateFunctionTypes(functions, direction) {
|
|
117
|
+
const lines = [];
|
|
118
|
+
|
|
119
|
+
functions.forEach(func => {
|
|
120
|
+
const params = direction === 'input' ? func.inputs : func.outputs;
|
|
121
|
+
if (!params || params.length === 0) return;
|
|
122
|
+
|
|
123
|
+
const funcName = func.name;
|
|
124
|
+
const typeName = `${funcName.charAt(0).toUpperCase() + funcName.slice(1)}${direction === 'input' ? 'Params' : 'Result'}`;
|
|
125
|
+
|
|
126
|
+
lines.push(`export interface ${typeName} {`);
|
|
127
|
+
params.forEach((param, idx) => {
|
|
128
|
+
const name = param.name || `param${idx}`;
|
|
129
|
+
const type = solToTsType(param.type);
|
|
130
|
+
lines.push(` ${name}: ${type};`);
|
|
131
|
+
});
|
|
132
|
+
lines.push('}\n');
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
return lines.join('\n');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Generate TypeScript types for events
|
|
140
|
+
* @private
|
|
141
|
+
*/
|
|
142
|
+
function generateEventTypes(events) {
|
|
143
|
+
const lines = [];
|
|
144
|
+
|
|
145
|
+
events.forEach(event => {
|
|
146
|
+
const typeName = `${event.name}Event`;
|
|
147
|
+
lines.push(`export interface ${typeName} {`);
|
|
148
|
+
|
|
149
|
+
event.inputs.forEach((input, idx) => {
|
|
150
|
+
const name = input.name || `param${idx}`;
|
|
151
|
+
const type = solToTsType(input.type);
|
|
152
|
+
const indexed = input.indexed ? ' // indexed' : '';
|
|
153
|
+
lines.push(` ${name}: ${type};${indexed}`);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
lines.push('}\n');
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
return lines.join('\n');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Generate contract method signatures
|
|
164
|
+
* @private
|
|
165
|
+
*/
|
|
166
|
+
function generateContractMethods(functions) {
|
|
167
|
+
const lines = [];
|
|
168
|
+
|
|
169
|
+
functions.forEach(func => {
|
|
170
|
+
const inputs = func.inputs.map(inp => {
|
|
171
|
+
const type = solToTsType(inp.type);
|
|
172
|
+
return `${inp.name || 'param'}: ${type}`;
|
|
173
|
+
}).join(', ');
|
|
174
|
+
|
|
175
|
+
let returnType = 'Promise<void>';
|
|
176
|
+
if (func.outputs && func.outputs.length > 0) {
|
|
177
|
+
if (func.outputs.length === 1) {
|
|
178
|
+
returnType = `Promise<${solToTsType(func.outputs[0].type)}>`;
|
|
179
|
+
} else {
|
|
180
|
+
returnType = `Promise<[${func.outputs.map(o => solToTsType(o.type)).join(', ')}]>`;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const stateMutability = func.stateMutability || 'nonpayable';
|
|
185
|
+
lines.push(` ${func.name}(${inputs}): ${returnType}; // ${stateMutability}`);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
return lines.join('\n');
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Convert Solidity type to TypeScript type
|
|
193
|
+
* @private
|
|
194
|
+
*/
|
|
195
|
+
function solToTsType(solidityType) {
|
|
196
|
+
// Handle arrays
|
|
197
|
+
if (solidityType.endsWith(']')) {
|
|
198
|
+
const baseType = solidityType.replace(/\[\d*\]/g, '');
|
|
199
|
+
return `${solToTsType(baseType)}[]`;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Handle base types
|
|
203
|
+
if (solidityType.startsWith('uint')) return 'bigint';
|
|
204
|
+
if (solidityType.startsWith('int')) return 'bigint';
|
|
205
|
+
if (solidityType.startsWith('bytes')) return 'string | Uint8Array';
|
|
206
|
+
if (solidityType === 'bool') return 'boolean';
|
|
207
|
+
if (solidityType === 'address') return 'string'; // EVM address as checksum string
|
|
208
|
+
if (solidityType === 'string') return 'string';
|
|
209
|
+
|
|
210
|
+
// Fallback for custom types
|
|
211
|
+
return 'any';
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Extract struct types from ABI
|
|
216
|
+
* @private
|
|
217
|
+
*/
|
|
218
|
+
function extractStructTypes(abi) {
|
|
219
|
+
// This would require more advanced parsing for tuple types
|
|
220
|
+
return [];
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Generate struct type definitions
|
|
225
|
+
* @private
|
|
226
|
+
*/
|
|
227
|
+
function generateStructTypes(structs) {
|
|
228
|
+
if (structs.length === 0) return '// No custom structs\n';
|
|
229
|
+
|
|
230
|
+
return structs.map(struct => `export interface ${struct.name} {\n // struct fields\n}\n`).join('\n');
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Generate barrel export index.ts
|
|
235
|
+
* @private
|
|
236
|
+
*/
|
|
237
|
+
function generateIndexFile(typesPath, abiFiles) {
|
|
238
|
+
const exports = abiFiles
|
|
239
|
+
.map(file => {
|
|
240
|
+
const name = file.replace('.abi.json', '');
|
|
241
|
+
return `export * from './${name}';`;
|
|
242
|
+
})
|
|
243
|
+
.join('\n');
|
|
244
|
+
|
|
245
|
+
const indexPath = path.join(typesPath, 'index.ts');
|
|
246
|
+
fs.writeFileSync(indexPath, `// Auto-generated barrel export\n${exports}\n`);
|
|
247
|
+
}
|