Skip to content

Commit

Permalink
feat: contract template (#15)
Browse files Browse the repository at this point in the history
closes BES-427
closes #12 

Add the following contract template and fill it with the reproducer
function:
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {FuzzTest} from './FuzzTest.t.sol';
import {vm} from './Setup.t.sol';

contract ForgeReproducer is FuzzTest {

}
```

If the contract already exists, append a number after.

Hide this behaviour behind the bool flag `--write-contract`

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- New Features
- Integrated a new templating engine to enable dynamic generation of
contract templates.
- Added functionality for creating and saving synthesized contract files
with automated, unique naming and robust error handling.
- Introduced an optional command-line flag that lets users choose to
output the result as a contract file.
- Provided a new Solidity contract template featuring configurable
placeholders.
- Style
- Adjusted output formatting for generated contracts to yield a more
compact and streamlined appearance.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
  • Loading branch information
simon-something authored Feb 7, 2025
2 parents a89ea3e + c0ffeef commit 3e41667
Show file tree
Hide file tree
Showing 8 changed files with 297 additions and 5 deletions.
130 changes: 129 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ authors = ["simon-something", "Defi-Wonderland"]
name = "youdusa"
description = "Take a Medusa trace as input, parse it and create Foundry reproducer function for every failing properties"
homepage = "https://github.com/defi-wonderland/youdusa-rs"
version = "0.1.0"
version = "0.1.2"
edition = "2021"
repository = "https://github.com/defi-wonderland/youdusa-rs"
license = "MIT"
Expand All @@ -21,6 +21,7 @@ path = "src/main.rs"

[dependencies]
anyhow = "1.0.92"
askama = "0.12.1"
clap = { version = "4.5.21", features = ["cargo", "derive"] }
primitive-types = "0.13.1"
tee = "0.1.0"
35 changes: 35 additions & 0 deletions ForgeReproducer.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {FuzzTest} from './FuzzTest.t.sol';
import {vm} from './Setup.t.sol';

contract ForgeReproducer is FuzzTest {
function test_prop_anyoneCanIncreaseFundInAPool() public {
vm.roll(10429);
vm.warp(19960);
vm.prank(0x0000000000000000000000000000000000050000);
this.prop_alloOwnerCanAlwaysChangePercentFee(15056796);

vm.roll(34180);
vm.warp(321741);
vm.prank(0x0000000000000000000000000000000000070000);
this.prop_anyoneCanIncreaseFundInAPool(13441534537036768485200417184756697876915712920751763869415731560796441041418, 334437,'');

}

function test_prop_anyoneCanIncreaseFundInAPool2() public {
vm.roll(10429);
vm.warp(19960);
vm.prank(0x0000000000000000000000000000000000050000);
this.prop_alloOwnerCanAlwaysChangePercentFee(15056796);

vm.roll(34180);
vm.warp(321741);
vm.prank(0x0000000000000000000000000000000000070000);
this.prop_anyoneCanIncreaseFundInAPool(23, 334437, (1, 2),'');

}


}
35 changes: 35 additions & 0 deletions ForgeReproducer1.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {FuzzTest} from './FuzzTest.t.sol';
import {vm} from './Setup.t.sol';

contract ForgeReproducer1 is FuzzTest {
function test_prop_anyoneCanIncreaseFundInAPool() public {
vm.roll(10429);
vm.warp(19960);
vm.prank(0x0000000000000000000000000000000000050000);
this.prop_alloOwnerCanAlwaysChangePercentFee(15056796);

vm.roll(34180);
vm.warp(321741);
vm.prank(0x0000000000000000000000000000000000070000);
this.prop_anyoneCanIncreaseFundInAPool(13441534537036768485200417184756697876915712920751763869415731560796441041418, 334437,'');

}

function test_prop_anyoneCanIncreaseFundInAPool2() public {
vm.roll(10429);
vm.warp(19960);
vm.prank(0x0000000000000000000000000000000000050000);
this.prop_alloOwnerCanAlwaysChangePercentFee(15056796);

vm.roll(34180);
vm.warp(321741);
vm.prank(0x0000000000000000000000000000000000070000);
this.prop_anyoneCanIncreaseFundInAPool(23, 334437, (1, 2),'');

}


}
51 changes: 51 additions & 0 deletions src/contract_writer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use anyhow::{anyhow, Context, Result};
use askama::Template;
use std::fs::File;
use std::io::Write as WriteIO;
use std::path::Path;

/// The contract template,
#[derive(Template, Debug, Clone, PartialEq)]
#[template(path = "template.sol", escape = "none")]
pub struct Contract {
reproducers: String,
contract_name: String,
}

impl Contract {
pub fn new(reproducers: &[u8]) -> Result<Contract> {
let contract_name =
Contract::find_first_unused_filename().context("Failed to find a filename")?;

Ok(Contract {
reproducers: String::from_utf8_lossy(reproducers).to_string(),
contract_name,
})
}

pub fn write_rendered_contract(&self) -> Result<()> {
let mut f = File::create_new(format!("{}.t.sol", self.contract_name))
.context("Failed to create contract")?;

let rendered = self.render().context("Fail to render contract")?;

f.write_all(rendered.as_bytes())
.context("Failed to write contract")?;

Ok(())
}

fn find_first_unused_filename() -> Result<String> {
// Avoiding Regex intensifies
(0..)
.map(|i| {
if i == 0 {
"ForgeReproducer".to_owned()
} else {
format!("ForgeReproducer{}", i)
}
})
.find(|base| !Path::new(&format!("{}{}", base, ".t.sol")).exists())
.ok_or_else(|| anyhow!("No available filename found"))
}
}
2 changes: 1 addition & 1 deletion src/emitter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ impl Emitter {
pub fn emit(&mut self, ast: &Ast) -> Result<()> {
match ast {
Ast::FunctionDeclaration(fn_declaration) => {
self.emit_function_declaration(fn_declaration)
self.emit_function_declaration(fn_declaration);
}
Ast::Statement(statement) => self.emit_statement(statement),
}
Expand Down
Loading

0 comments on commit 3e41667

Please sign in to comment.