create-stylus 0.0.6-hotfix.1 ā 0.1.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/package.json +1 -1
- package/templates/base/package.json +2 -2
- package/templates/base/packages/stylus/package.json +2 -2
- package/templates/base/packages/stylus/scripts/test.ts +236 -0
- package/templates/base/packages/stylus/scripts/test_network.ts +15 -12
- package/templates/base/packages/stylus/your-contract/Cargo.toml +1 -1
- package/templates/base/packages/stylus/your-contract/rust-toolchain.toml +7 -1
- package/templates/base/packages/stylus/your-contract/src/lib.rs +43 -67
- package/templates/base/readme.md +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ss",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"private": true,
|
|
5
5
|
"workspaces": {
|
|
6
6
|
"packages": [
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"stylus:format": "yarn workspace @ss/stylus format",
|
|
25
25
|
"stylus:test": "yarn workspace @ss/stylus test",
|
|
26
26
|
"test": "yarn stylus:test",
|
|
27
|
-
"
|
|
27
|
+
"info:networks": "yarn workspace @ss/stylus info:networks",
|
|
28
28
|
"format": "yarn next:format",
|
|
29
29
|
"start": "yarn workspace @ss/nextjs dev",
|
|
30
30
|
"next:lint": "yarn workspace @ss/nextjs lint",
|
|
@@ -9,10 +9,10 @@
|
|
|
9
9
|
"dev": "tsc --watch",
|
|
10
10
|
"start": "node dist/index.js",
|
|
11
11
|
"deploy": "ts-node scripts/deploy_wrapper.ts",
|
|
12
|
-
"
|
|
12
|
+
"info:networks": "ts-node scripts/test_network.ts",
|
|
13
13
|
"export-abi": "ts-node scripts/export_abi.ts",
|
|
14
14
|
"clean-contracts": "git clean -xdf -e .env -e your-contract/ -e node_modules/",
|
|
15
|
-
"test": "
|
|
15
|
+
"test": "ts-node scripts/test.ts",
|
|
16
16
|
"lint": "eslint scripts --ext .ts",
|
|
17
17
|
"lint:fix": "eslint scripts --ext .ts --fix",
|
|
18
18
|
"new-module": "bash scripts/new_module.sh"
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
#!/usr/bin/env ts-node
|
|
2
|
+
/**
|
|
3
|
+
* Stylus Cargo Test Runner
|
|
4
|
+
*
|
|
5
|
+
* This script automatically finds and runs tests for all Cargo projects
|
|
6
|
+
* in the stylus directory that have a Cargo.toml file.
|
|
7
|
+
*
|
|
8
|
+
* Features:
|
|
9
|
+
* - Automatically discovers Cargo projects by looking for Cargo.toml files
|
|
10
|
+
* - Excludes scripts, deployments, and node_modules directories
|
|
11
|
+
* - Runs `cargo test` for each project
|
|
12
|
+
* - Shows real-time output during test execution
|
|
13
|
+
* - Provides a comprehensive summary of test results
|
|
14
|
+
* - Exits with appropriate error codes for CI/CD integration
|
|
15
|
+
*
|
|
16
|
+
* Usage:
|
|
17
|
+
* npm run test
|
|
18
|
+
* or
|
|
19
|
+
* ts-node scripts/test.ts
|
|
20
|
+
*/
|
|
21
|
+
import { spawn } from "child_process";
|
|
22
|
+
import { promises as fs } from "fs";
|
|
23
|
+
import * as path from "path";
|
|
24
|
+
|
|
25
|
+
interface TestResult {
|
|
26
|
+
project: string;
|
|
27
|
+
success: boolean;
|
|
28
|
+
output: string;
|
|
29
|
+
error?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Execute a command and return a promise with the result
|
|
34
|
+
*/
|
|
35
|
+
function executeCommand(
|
|
36
|
+
command: string,
|
|
37
|
+
cwd: string,
|
|
38
|
+
): Promise<{ success: boolean; output: string; error?: string }> {
|
|
39
|
+
return new Promise((resolve) => {
|
|
40
|
+
console.log(`\nš Running tests in ${path.basename(cwd)}...`);
|
|
41
|
+
console.log(`Executing: ${command}`);
|
|
42
|
+
|
|
43
|
+
const childProcess = spawn(command, [], {
|
|
44
|
+
cwd,
|
|
45
|
+
shell: true,
|
|
46
|
+
stdio: ["inherit", "pipe", "pipe"],
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
let output = "";
|
|
50
|
+
let errorOutput = "";
|
|
51
|
+
|
|
52
|
+
// Handle stdout
|
|
53
|
+
if (childProcess.stdout) {
|
|
54
|
+
childProcess.stdout.on("data", (data: Buffer) => {
|
|
55
|
+
const chunk = data.toString();
|
|
56
|
+
output += chunk;
|
|
57
|
+
process.stdout.write(chunk); // Show real-time output
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Handle stderr
|
|
62
|
+
if (childProcess.stderr) {
|
|
63
|
+
childProcess.stderr.on("data", (data: Buffer) => {
|
|
64
|
+
const chunk = data.toString();
|
|
65
|
+
errorOutput += chunk;
|
|
66
|
+
process.stderr.write(chunk); // Show real-time errors
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Handle process completion
|
|
71
|
+
childProcess.on("close", (code: number | null) => {
|
|
72
|
+
const success = code === 0;
|
|
73
|
+
if (success) {
|
|
74
|
+
console.log(
|
|
75
|
+
`ā
Tests completed successfully in ${path.basename(cwd)}!`,
|
|
76
|
+
);
|
|
77
|
+
} else {
|
|
78
|
+
console.log(
|
|
79
|
+
`ā Tests failed in ${path.basename(cwd)} with exit code ${code}`,
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
resolve({
|
|
84
|
+
success,
|
|
85
|
+
output,
|
|
86
|
+
...(errorOutput && { error: errorOutput }),
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Handle process errors
|
|
91
|
+
childProcess.on("error", (error: Error) => {
|
|
92
|
+
console.error(
|
|
93
|
+
`ā Error running tests in ${path.basename(cwd)}:`,
|
|
94
|
+
error.message,
|
|
95
|
+
);
|
|
96
|
+
resolve({
|
|
97
|
+
success: false,
|
|
98
|
+
output: "",
|
|
99
|
+
error: error.message,
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Check if a directory contains a Cargo.toml file
|
|
107
|
+
*/
|
|
108
|
+
async function hasCargoToml(dirPath: string): Promise<boolean> {
|
|
109
|
+
try {
|
|
110
|
+
await fs.access(path.join(dirPath, "Cargo.toml"));
|
|
111
|
+
return true;
|
|
112
|
+
} catch {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Find all Cargo projects in the stylus directory
|
|
119
|
+
*/
|
|
120
|
+
async function findCargoProjects(): Promise<string[]> {
|
|
121
|
+
const stylusDir = path.resolve(__dirname, "..");
|
|
122
|
+
const cargoProjects: string[] = [];
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
const entries = await fs.readdir(stylusDir, { withFileTypes: true });
|
|
126
|
+
|
|
127
|
+
for (const entry of entries) {
|
|
128
|
+
if (entry.isDirectory()) {
|
|
129
|
+
const dirName = entry.name;
|
|
130
|
+
|
|
131
|
+
// Skip excluded directories
|
|
132
|
+
if (
|
|
133
|
+
dirName === "scripts" ||
|
|
134
|
+
dirName === "deployments" ||
|
|
135
|
+
dirName === "node_modules"
|
|
136
|
+
) {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const dirPath = path.join(stylusDir, dirName);
|
|
141
|
+
|
|
142
|
+
if (await hasCargoToml(dirPath)) {
|
|
143
|
+
cargoProjects.push(dirPath);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
} catch (error) {
|
|
148
|
+
console.error("Error scanning for Cargo projects:", error);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return cargoProjects;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Run tests for all Cargo projects
|
|
156
|
+
*/
|
|
157
|
+
async function runAllTests(): Promise<void> {
|
|
158
|
+
console.log("š Starting Stylus Cargo Tests...\n");
|
|
159
|
+
|
|
160
|
+
const cargoProjects = await findCargoProjects();
|
|
161
|
+
|
|
162
|
+
if (cargoProjects.length === 0) {
|
|
163
|
+
console.log("ā No Cargo projects found with Cargo.toml files.");
|
|
164
|
+
process.exit(1);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
console.log(`Found ${cargoProjects.length} Stylus Contract(s):`);
|
|
168
|
+
cargoProjects.forEach((project) => {
|
|
169
|
+
console.log(` - ${path.basename(project)}`);
|
|
170
|
+
});
|
|
171
|
+
console.log("");
|
|
172
|
+
|
|
173
|
+
const results: TestResult[] = [];
|
|
174
|
+
|
|
175
|
+
// Run tests for each project
|
|
176
|
+
for (const projectPath of cargoProjects) {
|
|
177
|
+
const projectName = path.basename(projectPath);
|
|
178
|
+
|
|
179
|
+
const result = await executeCommand("cargo test", projectPath);
|
|
180
|
+
|
|
181
|
+
results.push({
|
|
182
|
+
project: projectName,
|
|
183
|
+
success: result.success,
|
|
184
|
+
output: result.output,
|
|
185
|
+
...(result.error && { error: result.error }),
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Print summary
|
|
190
|
+
console.log("\n" + "=".repeat(60));
|
|
191
|
+
console.log("š TEST SUMMARY");
|
|
192
|
+
console.log("=".repeat(60));
|
|
193
|
+
|
|
194
|
+
const successful = results.filter((r) => r.success);
|
|
195
|
+
const failed = results.filter((r) => !r.success);
|
|
196
|
+
|
|
197
|
+
console.log(`ā
Successful: ${successful.length}`);
|
|
198
|
+
console.log(`ā Failed: ${failed.length}`);
|
|
199
|
+
console.log(`š¦ Total projects: ${results.length}`);
|
|
200
|
+
|
|
201
|
+
if (successful.length > 0) {
|
|
202
|
+
console.log("\nā
Successful projects:");
|
|
203
|
+
successful.forEach((result) => {
|
|
204
|
+
console.log(` - ${result.project}`);
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (failed.length > 0) {
|
|
209
|
+
console.log("\nā Failed projects:");
|
|
210
|
+
failed.forEach((result) => {
|
|
211
|
+
console.log(` - ${result.project}`);
|
|
212
|
+
if (result.error) {
|
|
213
|
+
console.log(` Error: ${result.error.split("\n")[0]}`);
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Exit with error code if any tests failed
|
|
219
|
+
if (failed.length > 0) {
|
|
220
|
+
console.log("\nš„ Some tests failed!");
|
|
221
|
+
process.exit(1);
|
|
222
|
+
} else {
|
|
223
|
+
console.log("\nš All tests passed!");
|
|
224
|
+
process.exit(0);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Main execution
|
|
229
|
+
if (require.main === module) {
|
|
230
|
+
runAllTests().catch((error) => {
|
|
231
|
+
console.error("ā Unexpected error:", error);
|
|
232
|
+
process.exit(1);
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export { runAllTests, findCargoProjects };
|
|
@@ -3,13 +3,10 @@ import { SUPPORTED_NETWORKS } from "./utils/";
|
|
|
3
3
|
|
|
4
4
|
function testNetworkFunctionality() {
|
|
5
5
|
console.log("š§Ŗ Testing network functionality...\n");
|
|
6
|
-
|
|
7
|
-
const testNetworks = [
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
];
|
|
11
|
-
|
|
12
|
-
testNetworks.forEach(network => {
|
|
6
|
+
|
|
7
|
+
const testNetworks = [...Object.keys(SUPPORTED_NETWORKS)];
|
|
8
|
+
|
|
9
|
+
testNetworks.forEach((network) => {
|
|
13
10
|
const chain = getChain(network);
|
|
14
11
|
if (chain) {
|
|
15
12
|
console.log(`ā
${network}: ${chain.rpcUrl}`);
|
|
@@ -17,15 +14,21 @@ function testNetworkFunctionality() {
|
|
|
17
14
|
console.log(`ā ${network}: Not found in viem chains`);
|
|
18
15
|
}
|
|
19
16
|
});
|
|
20
|
-
|
|
17
|
+
|
|
21
18
|
console.log("\nš Usage examples:");
|
|
22
|
-
Object.keys(SUPPORTED_NETWORKS).forEach(network => {
|
|
19
|
+
Object.keys(SUPPORTED_NETWORKS).forEach((network) => {
|
|
23
20
|
const chain = getChain(network);
|
|
24
|
-
console.log(
|
|
25
|
-
|
|
21
|
+
console.log(
|
|
22
|
+
` yarn deploy --network ${chain?.name}\t# Deploy to ${chain?.name}`,
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
// TODO: determine which one to use later, for now we use all original names
|
|
26
|
+
// console.log(
|
|
27
|
+
// ` yarn deploy --network ${chain?.alias}\t\t# Deploy to ${chain?.name} (alias)`,
|
|
28
|
+
// );
|
|
26
29
|
});
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
if (require.main === module) {
|
|
30
33
|
testNetworkFunctionality();
|
|
31
|
-
}
|
|
34
|
+
}
|
|
@@ -13,7 +13,7 @@ alloy-primitives = "=0.8.20"
|
|
|
13
13
|
alloy-sol-types = "=0.8.20"
|
|
14
14
|
stylus-sdk = "0.9.0"
|
|
15
15
|
hex = { version = "0.4", default-features = false }
|
|
16
|
-
openzeppelin-stylus = "0.2.0"
|
|
16
|
+
openzeppelin-stylus = "=0.2.0"
|
|
17
17
|
|
|
18
18
|
[dev-dependencies]
|
|
19
19
|
alloy-primitives = { version = "=0.8.20", features = ["sha3-keccak"] }
|
|
@@ -1,2 +1,8 @@
|
|
|
1
|
+
# NOTE: this toolchain is nightly because of openzeppelin requirements
|
|
2
|
+
|
|
1
3
|
[toolchain]
|
|
2
|
-
|
|
4
|
+
# We should use stable here once nitro-testnode is updated and the contracts fit
|
|
5
|
+
# the size limit (issue <https://github.com/OpenZeppelin/rust-contracts-stylus/issues/129>).
|
|
6
|
+
channel = "1.89.0"
|
|
7
|
+
components = ["rust-src", "rustfmt", "clippy"]
|
|
8
|
+
targets = ["wasm32-unknown-unknown"]
|
|
@@ -170,70 +170,46 @@ impl IOwnable for YourContract {
|
|
|
170
170
|
}
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
//
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
//
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
//
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
// // Initialize with owner
|
|
217
|
-
// let owner_addr = vm.addr(); // Use VM address as owner for testing
|
|
218
|
-
// contract.init(owner_addr);
|
|
219
|
-
|
|
220
|
-
// // Test withdraw (in real scenario, contract would have received ETH)
|
|
221
|
-
// let result = contract.withdraw();
|
|
222
|
-
// assert!(result.is_ok()); // Should not panic since caller is owner
|
|
223
|
-
// }
|
|
224
|
-
|
|
225
|
-
// #[test]
|
|
226
|
-
// fn test_withdraw_not_owner() {
|
|
227
|
-
// let vm = TestVM::default();
|
|
228
|
-
// let mut contract = YourContract::from(&vm);
|
|
229
|
-
|
|
230
|
-
// // Initialize with different owner
|
|
231
|
-
// let owner_addr = Address::from([1u8; 20]);
|
|
232
|
-
// contract.init(owner_addr);
|
|
233
|
-
|
|
234
|
-
// // This test should panic with "Not the Owner" when withdraw is called by non-owner
|
|
235
|
-
// // In a real test environment, you'd want to test this more carefully
|
|
236
|
-
// // but for now we'll just verify the function exists
|
|
237
|
-
// assert_eq!(contract.owner(), owner_addr);
|
|
238
|
-
// }
|
|
239
|
-
// }
|
|
173
|
+
#[cfg(test)]
|
|
174
|
+
mod test {
|
|
175
|
+
use super::*;
|
|
176
|
+
use stylus_sdk::testing::*;
|
|
177
|
+
|
|
178
|
+
#[no_mangle]
|
|
179
|
+
pub unsafe extern "C" fn emit_log(_pointer: *const u8, _len: usize, _: usize) {}
|
|
180
|
+
#[no_mangle]
|
|
181
|
+
pub unsafe extern "C" fn msg_sender(_sender: *mut u8) {}
|
|
182
|
+
|
|
183
|
+
#[test]
|
|
184
|
+
fn test_your_contract() {
|
|
185
|
+
let vm = TestVM::default();
|
|
186
|
+
let mut contract = YourContract::from(&vm);
|
|
187
|
+
|
|
188
|
+
// Test initialization
|
|
189
|
+
let owner_addr = Address::from([1u8; 20]);
|
|
190
|
+
let _ = contract.constructor(owner_addr);
|
|
191
|
+
|
|
192
|
+
assert_eq!(contract.owner(), owner_addr);
|
|
193
|
+
assert_eq!(contract.greeting(), "Building Unstoppable Apps!!!");
|
|
194
|
+
assert_eq!(contract.premium(), false);
|
|
195
|
+
assert_eq!(contract.total_counter(), U256::ZERO);
|
|
196
|
+
|
|
197
|
+
// Test setting greeting without payment
|
|
198
|
+
contract.set_greeting("Hello World".to_string());
|
|
199
|
+
assert_eq!(contract.greeting(), "Hello World");
|
|
200
|
+
assert_eq!(contract.premium(), false);
|
|
201
|
+
assert_eq!(contract.total_counter(), U256::from(1));
|
|
202
|
+
|
|
203
|
+
// Test user greeting counter
|
|
204
|
+
let sender = vm.msg_sender();
|
|
205
|
+
assert_eq!(contract.user_greeting_counter(sender), U256::from(1));
|
|
206
|
+
|
|
207
|
+
// Test setting greeting with payment
|
|
208
|
+
vm.set_value(U256::from(100));
|
|
209
|
+
contract.set_greeting("Premium Hello".to_string());
|
|
210
|
+
assert_eq!(contract.greeting(), "Premium Hello");
|
|
211
|
+
assert_eq!(contract.premium(), true);
|
|
212
|
+
assert_eq!(contract.total_counter(), U256::from(2));
|
|
213
|
+
assert_eq!(contract.user_greeting_counter(sender), U256::from(2));
|
|
214
|
+
}
|
|
215
|
+
}
|
package/templates/base/readme.md
CHANGED
|
@@ -189,7 +189,7 @@ To deploy your contracts to other networks (other than the default local Nitro d
|
|
|
189
189
|
This template supports Arbitrum networks only. You can test which networks are available and their RPC URLs:
|
|
190
190
|
|
|
191
191
|
```bash
|
|
192
|
-
yarn
|
|
192
|
+
yarn info:networks
|
|
193
193
|
```
|
|
194
194
|
|
|
195
195
|
This will show you all supported networks and their corresponding RPC endpoints.
|