generated from ZeroEkkusu/soldeer-minimal
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
79dc4d5
commit f8d8c80
Showing
13 changed files
with
318 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
*.solx linguist-language=Solidity |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"files.associations": { | ||
"*.solx": "solidity" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
[profile.default] | ||
src = "src" | ||
test = "out/solx" | ||
out = "out" | ||
libs = ["dependencies"] | ||
verbosity = 2 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,8 @@ | ||
{ | ||
"devDependencies": { | ||
"@types/bun": "^1.1.6" | ||
}, | ||
"dependencies": { | ||
"ethers": "^6.13.2" | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# Override for 'forge build' and 'forge test' | ||
forge() { | ||
if [ "$1" = "build" ]; then | ||
bun solx/transpiler.ts && command forge build "${@:2}" | ||
elif [ "$1" = "test" ]; then | ||
bun solx/transpiler.ts && command forge test "${@:2}" | ||
else | ||
command forge "$@" | ||
fi | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,217 @@ | ||
import * as fs from "fs"; | ||
import * as path from "path"; | ||
|
||
interface ProcessedContent { | ||
solidity: string; | ||
typescript: string; | ||
} | ||
|
||
function processSolidityFile(inputPath: string): void { | ||
const content = fs.readFileSync(inputPath, "utf-8"); | ||
const { solidity, typescript } = extractTypeScriptBlock(content); | ||
|
||
const outputDir = path.join(process.cwd(), "out", "solx"); | ||
if (!fs.existsSync(outputDir)) { | ||
fs.mkdirSync(outputDir, { recursive: true }); | ||
} | ||
|
||
const outputSolPath = path.join( | ||
outputDir, | ||
path.basename(inputPath).replace(".solx", ".sol") | ||
); | ||
const outputTsPath = path.join(outputDir, "example.ts"); | ||
|
||
fs.writeFileSync(outputSolPath, solidity); | ||
fs.writeFileSync(outputTsPath, typescript); | ||
|
||
/*console.log(`Processed ${inputPath}`); | ||
console.log(`Generated ${outputSolPath}`); | ||
console.log(`Generated ${outputTsPath}`);*/ | ||
} | ||
|
||
function extractTypeScriptBlock(content: string): ProcessedContent { | ||
const tsBlockRegex = | ||
/\/\/ @typescript-start\s*\((.*?)\)([\s\S]*?)\/\/ @typescript-end\s*\((.*?)\)/; | ||
const match = tsBlockRegex.exec(content); | ||
|
||
if (!match) { | ||
throw new Error("No TypeScript block found in the Solidity file."); | ||
} | ||
|
||
const [fullMatch, inputVars, tsCode, outputVars] = match; | ||
const inputVarList = inputVars | ||
? inputVars.split(",").map((v) => v.trim()) | ||
: []; | ||
const outputVarList = outputVars | ||
? outputVars.split(",").map((v) => v.trim()) | ||
: []; | ||
|
||
const allOutputVars = [...new Set([...outputVarList, ...inputVarList])]; | ||
const newOutputVars = outputVarList.filter((v) => !inputVarList.includes(v)); | ||
const existingOutputVars = allOutputVars.filter( | ||
(v) => !newOutputVars.includes(v) | ||
); | ||
const hasConsoleLog = tsCode.includes("console.log"); | ||
|
||
let solidity = content.replace( | ||
fullMatch, | ||
` | ||
string[] memory cmd = new string[](${inputVarList.length > 0 ? "3" : "2"}); | ||
cmd[0] = "bun"; | ||
cmd[1] = "./out/solx/example.ts"; | ||
${ | ||
inputVarList.length > 0 | ||
? `cmd[2] = vm.toString(abi.encode(${inputVarList | ||
.map((v) => v.split(" ")[1]) | ||
.join(", ")}));` | ||
: "" | ||
} | ||
${ | ||
allOutputVars.length > 0 || hasConsoleLog | ||
? "bytes memory solx_decoded = vm.ffi(cmd);" | ||
: "vm.ffi(cmd);" | ||
} | ||
${ | ||
allOutputVars.length > 0 || hasConsoleLog | ||
? ` | ||
(${newOutputVars | ||
.map((v) => { | ||
const [type, name] = v.split(" "); | ||
return `${addMemoryKeyword(type)} ${name}`; | ||
}) | ||
.join(", ")}${ | ||
existingOutputVars.length > 0 | ||
? (newOutputVars.length > 0 ? ", " : "") + | ||
existingOutputVars | ||
.map((v, i) => { | ||
const [type, name] = v.split(" "); | ||
return `${addMemoryKeyword(type)} solx_temp_${i}`; | ||
}) | ||
.join(", ") | ||
: "" | ||
}${allOutputVars.length > 0 && hasConsoleLog ? ", " : ""}${ | ||
hasConsoleLog ? "string memory solx_logs" : "" | ||
}) = abi.decode(solx_decoded, (${allOutputVars | ||
.map((v) => v.split(" ")[0]) | ||
.join(", ")}${hasConsoleLog ? ", string" : ""})); | ||
${existingOutputVars | ||
.map((v, i) => `${v.split(" ")[1]} = solx_temp_${i};`) | ||
.join("\n ")}` | ||
: "" | ||
} | ||
${hasConsoleLog ? "console.log(solx_logs);" : ""} | ||
` | ||
); | ||
|
||
let typescript = ` | ||
import { ethers } from "ethers"; | ||
${ | ||
hasConsoleLog | ||
? ` | ||
let solx_logs = ""; | ||
let isFirstLog = true; | ||
const originalConsoleLog = console.log; | ||
console.log = (...args) => { | ||
if (isFirstLog) { | ||
solx_logs += args.join(' '); | ||
isFirstLog = false; | ||
} else { | ||
solx_logs += "\\n " + args.join(' '); | ||
} | ||
}; | ||
` | ||
: "" | ||
} | ||
const inputData = Bun.argv[2]; | ||
${ | ||
inputVarList.length > 0 | ||
? ` | ||
if (!inputData) { | ||
console.error("No input data provided"); | ||
process.exit(1); | ||
} | ||
` | ||
: "" | ||
} | ||
async function main() { | ||
try { | ||
const abi = new ethers.AbiCoder(); | ||
${ | ||
inputVarList.length > 0 | ||
? ` | ||
// Decode input based on the types specified in the Solidity FFI call | ||
let [${inputVarList | ||
.map((v) => v.split(" ")[1]) | ||
.join(", ")}] = abi.decode([${inputVarList | ||
.map((v) => `"${v.split(" ")[0]}"`) | ||
.join(", ")}], inputData); | ||
` | ||
: "" | ||
} | ||
// User's TypeScript code | ||
${tsCode} | ||
${ | ||
hasConsoleLog | ||
? ` | ||
// Restore original console.log | ||
console.log = originalConsoleLog; | ||
` | ||
: "" | ||
} | ||
${ | ||
allOutputVars.length > 0 || hasConsoleLog | ||
? ` | ||
// Encode output including logs and all input variables | ||
const encodedOutput = abi.encode( | ||
[...${JSON.stringify(allOutputVars.map((v) => v.split(" ")[0]))}${ | ||
hasConsoleLog ? ', "string"' : "" | ||
}], | ||
[${allOutputVars.map((v) => v.split(" ")[1]).join(", ")}${ | ||
hasConsoleLog ? ", solx_logs" : "" | ||
}] | ||
); | ||
console.log(encodedOutput); | ||
` | ||
: "" | ||
} | ||
} catch (error) { | ||
console.error("An error occurred:", error); | ||
process.exit(1); | ||
} | ||
} | ||
main(); | ||
`; | ||
|
||
return { solidity, typescript }; | ||
} | ||
|
||
function addMemoryKeyword(type: string): string { | ||
// Always add 'memory' for array types | ||
if (type.includes("[]")) { | ||
return `${type} memory`; | ||
} | ||
|
||
// For non-array types, add 'memory' if it's not a value type | ||
if ( | ||
!/^(uint\d*|int\d*|bool|address|bytes([1-9]|[12][0-9]|3[0-2]))(\[\])?$/.test( | ||
type | ||
) | ||
) { | ||
return `${type} memory`; | ||
} | ||
|
||
return type; | ||
} | ||
|
||
// Main execution | ||
const inputFilePath = path.join(process.cwd(), "test", "Example.solx"); | ||
processSolidityFile(inputFilePath); |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
// SPDX-License-Identifier: MIT OR Apache-2.0 | ||
pragma solidity ^0.8.26; | ||
|
||
import "forge-std/Test.sol"; | ||
|
||
contract PikachuTest is Test { | ||
// Let's catch Pikachu ⚡️ in .solx! | ||
|
||
function test_catchPikachu() public { | ||
string memory myPokemon; | ||
uint256 randomness = vm.randomUint(0, 49); | ||
// @typescript-start (string myPokemon, uint256 randomness) | ||
console.log("Wow, I can write TypeScript inside Solidity!"); | ||
const response = await fetch("https://dummyapi.online/api/pokemon"); | ||
const json = await response.json(); | ||
myPokemon = json[randomness].pokemon; | ||
const isPikachu: boolean = myPokemon === "Pikachu"; | ||
// @typescript-end (bool isPikachu) | ||
console.log("I've caught", string.concat(myPokemon, isPikachu ? unicode" 🥹" : ".")); | ||
// Did you notice that isPikachu was not defined? 😱 | ||
} | ||
} |