Skip to content

Commit 4b7678e

Browse files
committed
add session recording and fix some bugs
1 parent 9d31522 commit 4b7678e

File tree

3 files changed

+192
-31
lines changed

3 files changed

+192
-31
lines changed

README.md

Lines changed: 104 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,120 @@
66

77
## Solidity Shell
88

9-
An interactive Solidity shell.
9+
An interactive Solidity shell with lightweight session recording.
1010

1111
[💾](https://www.npmjs.com/package/solidity-shell) `npm install solidity-shell`
1212

13+
14+
```javascript
15+
⇒ solidity-shell
16+
17+
🚀 Entering interactive Solidity shell. Type '.help' for help, '.exit' to exit.
18+
» ℹ️ ganache-mgr: starting temp. ganache instance ...
19+
»
20+
» uint a = 100
21+
» uint b = 200
22+
» a + b + 2 + uint8(50)
23+
352
24+
```
25+
26+
**Note**: The previous session can always be loaded by calling `.session load previous`.
27+
1328
## Examples
1429

30+
1531
![solidity-shell](https://user-images.githubusercontent.com/2865694/131328119-e363f20a-f627-43fc-8801-8d6613ad740f.gif)
1632

17-
____
33+
#### Sample Usage
34+
35+
```shell
36+
» .help
37+
38+
📚 Help:
39+
-----
40+
41+
General:
42+
.help ... this help :)
43+
.exit ... exit the shell
44+
45+
Settings:
46+
.config ... show settings
47+
set <key> <value> ... set setting
48+
unset <key> ... unset setting
49+
Session:
50+
.session ... list sessions
51+
load <id> ... load session
52+
save <id> ... save session
53+
54+
.undo ... undo last command
55+
.reset ... reset cmd history. start from scratch.
56+
57+
Debug:
58+
.dump ... (debug) show template contract
59+
60+
61+
cheers 🙌
62+
@tintinweb
63+
ConsenSys Diligence @ https://diligence.consensys.net/
64+
```
65+
66+
67+
#### Contracts, Structs, Functions
68+
69+
```javascript
70+
⇒ solidity-shell
71+
72+
🚀 Entering interactive Solidity shell. Type '.help' for help, '.exit' to exit.
73+
» ℹ️ ganache-mgr: starting temp. ganache instance ...
74+
»
75+
» contract TestContract {}
76+
» new TestContract()
77+
0xFBC1B2e79D816E36a1E1e923dd6c6fad463F4368
78+
» msg.sender
79+
0x363830C6aee2F0c43922bcB785C570a7cca613b5
80+
» block.timestamp
81+
1630339581
82+
» struct yolo {uint8 x; uint8 y;}
83+
» function mytest(uint x) public pure returns(uint) {
84+
multi> return x -5;
85+
multi> }
86+
» mytest(100)
87+
95
88+
```
1889

1990
![solidity-shell2](https://user-images.githubusercontent.com/2865694/131328490-e211e89b-ac59-4729-972b-3e3b19b75cfc.gif)
2091

92+
#### Advanced usage
93+
94+
```
95+
» struct yolo {uint8 x; uint8 y;}
96+
» .dump
97+
// SPDX-License-Identifier: GPL-2.0-or-later
98+
pragma solidity ^0.8.7;
99+
100+
contract TestContract {}
101+
102+
struct yolo {uint8 x; uint8 y;}
103+
104+
contract MainContract {
105+
106+
107+
108+
function main() public {
109+
uint a = 100;
110+
uint b = 200;
111+
a + b + 2 + uint8(50);
112+
new TestContract();
113+
msg.sender;
114+
block.timestamp;
115+
return ;
116+
}
117+
}
118+
```
119+
____
120+
121+
122+
21123
____
22124

23125
## Acknowledgements

bin/main.js

Lines changed: 62 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,35 +14,43 @@ const { InteractiveSolidityShell, SolidityStatement } = require('../src/handler'
1414
const { convert, multilineInput } = require('../src/utils');
1515

1616
const CONFIG_HOME = path.join(os.homedir(), '.solidity-shell');
17-
const CONFIG_FILE = path.join(CONFIG_HOME, '.config');
17+
const CONFIG_FILE = '.config';
18+
19+
var SESSION = 'previous.session';
1820

1921
/** static funcs */
20-
function tryLoadSettings(){
21-
let settings = {};
22+
function loadFile(name){
23+
let cfgFile = path.join(CONFIG_HOME, name);
2224

23-
if(fs.existsSync(CONFIG_FILE)){
24-
settings = JSON.parse(fs.readFileSync(CONFIG_FILE));
25+
if(fs.existsSync(cfgFile)){
26+
return JSON.parse(fs.readFileSync(cfgFile));
2527
}
26-
return settings;
28+
return {};
2729
}
2830

29-
function trySaveSettings(settings){
31+
function saveFile(name, data){
32+
let cfgFile = path.join(CONFIG_HOME, name);
33+
3034
if(!fs.existsSync(CONFIG_HOME)){
3135
fs.mkdirSync(CONFIG_HOME);
3236
}
33-
fs.writeFileSync(CONFIG_FILE, JSON.stringify(settings));
37+
fs.writeFileSync(cfgFile, JSON.stringify(data));
3438
}
3539

3640
/** MAIN */
3741

38-
const shell = new InteractiveSolidityShell(tryLoadSettings());
42+
const shell = new InteractiveSolidityShell(loadFile(CONFIG_FILE));
3943

4044
const vorpal = new Vorpal()
4145
.delimiter('')
4246
.show()
4347
.parse(process.argv);
4448

45-
process.on('exit', () => { shell.blockchain.stopService(); trySaveSettings(shell.settings) });
49+
process.on('exit', () => {
50+
shell.blockchain.stopService();
51+
saveFile(CONFIG_FILE, shell.settings)
52+
saveFile(SESSION, shell.dumpSession())
53+
});
4654

4755

4856
vorpal
@@ -56,21 +64,31 @@ vorpal
5664
let command = multilineInput(input);
5765
if (command.startsWith('.')) {
5866
let commandParts = command.split(' ');
67+
let ret = undefined;
5968
switch (commandParts[0]) {
6069
case '.help':
6170
cb(`
6271
📚 Help:
6372
-----
6473
65-
.help ... this help :)
66-
.exit ... exit the shell
74+
General:
75+
.help ... this help :)
76+
.exit ... exit the shell
6777
68-
.config ... show settings
69-
.set <key> <value> ... set setting
70-
.unset <key> ... clear setting
71-
72-
.reset ... reset cmd history. start from scratch.
73-
.undo ... undo last command
78+
Settings:
79+
.config ... show settings
80+
set <key> <value> ... set setting
81+
unset <key> ... unset setting
82+
Session:
83+
.session ... list sessions
84+
load <id> ... load session
85+
save <id> ... save session
86+
87+
.undo ... undo last command
88+
.reset ... reset cmd history. start from scratch.
89+
90+
Debug:
91+
.dump ... (debug) show template contract
7492
7593
7694
cheers 🙌
@@ -79,23 +97,44 @@ cheers 🙌
7997
`);
8098

8199
break; //show usage
82-
case '.exit': process.exit(); return; //exit -> no more cb()
100+
case '.exit': process.exit(); //exit -> no more cb()
83101
case '.reset': shell.reset(); break; //reset complete state
84102
case '.undo': shell.revert(); break; //revert last action
85-
case '.set': shell.setSetting(commandParts[1], convert(commandParts[2])); break;
86-
case '.unset': shell.setSetting(commandParts[1], undefined); break;
87-
case '.config': return cb(shell.settings); break;
103+
case '.config':
104+
switch(commandParts[1]){
105+
case 'set': shell.setSetting(commandParts[2], convert(commandParts[3])); break;
106+
case 'del': delete shell.settings[commandParts[2]]; break;
107+
default: return cb(shell.settings);
108+
} break;
109+
case '.session':
110+
switch(commandParts[1]){
111+
default:
112+
let sessions = fs.readdirSync(CONFIG_HOME).filter(file => file.endsWith('.session'));
113+
return cb(' - ' + sessions.map(s => s.replace('.session','')).join('\n - '));
114+
case 'load':
115+
shell.loadSession(loadFile(`${commandParts[2]}.session`))
116+
break;
117+
case 'save':
118+
SESSION = `${commandParts[2]}.session`;
119+
saveFile(SESSION, shell.dumpSession())
120+
break;
121+
}; break;
122+
case '.dump': return cb(shell.template());
123+
88124
default:
89125
console.error(`unknown command: ${command}. type '.help' for a list of commands.`);
90126
}
91127
// meta commands
92-
return cb();
128+
return cb(ret);
93129
}
94130

95131
const statement = new SolidityStatement(command);
96132

97133
/* REPL cmd */
98134
shell.run(statement).then(res => {
135+
if(typeof res === 'object'){
136+
return cb();
137+
}
99138
cb(c.bold(c.yellow(res)));
100139
}).catch(errors => {
101140
console.error(errors)

src/handler.js

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const IGNORE_WARNINGS = [
2222
class SolidityStatement {
2323

2424
constructor(rawCommand, scope) {
25-
this.rawCommand = rawCommand ? rawCommand.trim() : "true";
25+
this.rawCommand = rawCommand ? rawCommand.trim() : "";
2626
this.hasNoReturnValue = (rexAssign.test(this.rawCommand)) || (this.rawCommand.startsWith('delete')) || (this.rawCommand.startsWith('assembly')) || (this.rawCommand.startsWith('revert'))
2727

2828
if (scope) {
@@ -46,14 +46,16 @@ class SolidityStatement {
4646
} else {
4747
this.scope = "main";
4848
this.rawCommand = this.fixStatement(this.rawCommand);
49+
if(this.rawCommand===';'){
50+
this.hasNoReturnValue = true;
51+
}
4952
}
5053
}
5154

52-
//
5355
if (this.hasNoReturnValue) {
5456
// expression
55-
this.returnExpression = 'true;';
56-
this.returnType = 'bool';
57+
this.returnExpression = ';';
58+
this.returnType = '';
5759
} else {
5860
// not an expression
5961
this.returnExpression = this.rawCommand;
@@ -68,6 +70,10 @@ class SolidityStatement {
6870
toString() {
6971
return this.rawCommand;
7072
}
73+
74+
toList() {
75+
return [this.rawCommand, this.scope]
76+
}
7177
}
7278

7379

@@ -95,7 +101,20 @@ class InteractiveSolidityShell {
95101
this.blockchain.connect()
96102
}
97103

104+
loadSession(stmts){
105+
if(!stmts) {
106+
this.session.statements = []
107+
} else {
108+
this.session.statements = stmts.map(s => new SolidityStatement(s[0], s[1]));
109+
}
110+
}
111+
112+
dumpSession(){
113+
return this.session.statements.map(s => s.toList());
114+
}
115+
98116
setSetting(key, value){
117+
if(key === 'installedSolidityVersion') return;
99118
this.settings[key] = value;
100119
}
101120

@@ -120,7 +139,7 @@ class InteractiveSolidityShell {
120139
const contractState = this.session.statements.filter(stm => stm.scope === "contract");
121140
const mainStatements = this.session.statements.filter(stm => stm.scope === "main");
122141

123-
var lastStatement = this.session.statements[this.session.statements.length -1];
142+
var lastStatement = this.session.statements[this.session.statements.length -1] || {}
124143
if(lastStatement.scope !== 'main'){
125144
/* not a main statement, put everything in the body and use a dummy as returnexpression */
126145
var mainBody = mainStatements;
@@ -140,7 +159,7 @@ contract ${this.settings.templateContractName} {
140159
141160
${contractState.join(' \n\n')}
142161
143-
function ${this.settings.templateFuncMain}() public returns (${lastStatement.returnType}) {
162+
function ${this.settings.templateFuncMain}() public ${lastStatement.returnType ? `returns (${lastStatement.returnType})` : ''} {
144163
${mainBody.join('\n ')}
145164
return ${lastStatement.returnExpression}
146165
}
@@ -200,6 +219,7 @@ contract ${this.settings.templateContractName} {
200219
this.revert();
201220
return reject(err)
202221
}
222+
203223
return resolve(retval) // return value
204224
})
205225
}).catch(errors => {

0 commit comments

Comments
 (0)