Skip to content

Commit

Permalink
Update testContract and new format
Browse files Browse the repository at this point in the history
  • Loading branch information
Nadai2010 committed Aug 12, 2024
1 parent 9aa8594 commit 1cc67c6
Show file tree
Hide file tree
Showing 8 changed files with 273 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -269,4 +269,4 @@ export const AddressInfoDropdown = ({
</details>
</>
);
};
};
23 changes: 12 additions & 11 deletions packages/snfoundry/contracts/src/DiceGame.cairo
Original file line number Diff line number Diff line change
@@ -1,38 +1,39 @@
use openzeppelin::token::erc20::interface::{IERC20CamelDispatcher, IERC20CamelDispatcherTrait};

#[starknet::interface]
pub trait IDiceGame<T> {
fn roll_dice(ref self: T, amount: u256);
fn last_dice_value(self: @T) -> u256;
fn nonce(self: @T) -> u256;
fn prize(self: @T) -> u256;
fn eth_token(self: @T) -> IERC20CamelDispatcher;
fn eth_token_dispatcher(self: @T) -> IERC20CamelDispatcher;
}

#[starknet::contract]
mod DiceGame {
pub mod DiceGame {
use keccak::keccak_u256s_le_inputs;
use starknet::{ContractAddress, get_contract_address, get_block_number, get_caller_address};
use super::{IERC20CamelDispatcher, IERC20CamelDispatcherTrait, IDiceGame};

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
pub enum Event {
Roll: Roll,
Winner: Winner,
}

#[derive(Drop, starknet::Event)]
struct Roll {
pub struct Roll {
#[key]
player: ContractAddress,
amount: u256,
roll: u256,
pub player: ContractAddress,
pub amount: u256,
pub roll: u256,
}

#[derive(Drop, starknet::Event)]
struct Winner {
winner: ContractAddress,
amount: u256,
pub struct Winner {
pub winner: ContractAddress,
pub amount: u256,
}

#[storage]
Expand Down Expand Up @@ -92,7 +93,7 @@ mod DiceGame {
fn prize(self: @ContractState) -> u256 {
self.prize.read()
}
fn eth_token(self: @ContractState) -> IERC20CamelDispatcher {
fn eth_token_dispatcher(self: @ContractState) -> IERC20CamelDispatcher {
self.eth_token.read()
}
}
Expand Down
10 changes: 5 additions & 5 deletions packages/snfoundry/contracts/src/RiggedRoll.cairo
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use contracts::DiceGame::{IDiceGameDispatcher, IDiceGameDispatcherTrait};
use starknet::ContractAddress;

#[starknet::interface]
Expand All @@ -6,17 +7,16 @@ pub trait IRiggedRoll<T> {
fn withdraw(ref self: T, to: ContractAddress, amount: u256);
fn last_dice_value(self: @T) -> u256;
fn predicted_roll(self: @T) -> u256;
fn dice_game(self: @T) -> ContractAddress;
fn dice_game_dispatcher(self: @T) -> IDiceGameDispatcher;
}

#[starknet::contract]
mod RiggedRoll {
use contracts::DiceGame::{IDiceGameDispatcher, IDiceGameDispatcherTrait};
use keccak::keccak_u256s_le_inputs;
use openzeppelin::access::ownable::OwnableComponent;
use openzeppelin::token::erc20::interface::IERC20CamelDispatcherTrait;
use starknet::{ContractAddress, get_contract_address, get_block_number, get_caller_address};
use super::IRiggedRoll;
use super::{IRiggedRoll, IDiceGameDispatcher, IDiceGameDispatcherTrait};

component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);

Expand Down Expand Up @@ -65,8 +65,8 @@ mod RiggedRoll {
fn predicted_roll(self: @ContractState) -> u256 {
self.predicted_roll.read()
}
fn dice_game(self: @ContractState) -> ContractAddress {
self.dice_game.read().contract_address
fn dice_game_dispatcher(self: @ContractState) -> IDiceGameDispatcher {
self.dice_game.read()
}
}
}
4 changes: 4 additions & 0 deletions packages/snfoundry/contracts/src/lib.cairo
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
mod DiceGame;
mod RiggedRoll;
mod mock_contracts {
pub mod MockETHToken;
}
#[cfg(test)]
mod test {
mod TestContract;
}

34 changes: 34 additions & 0 deletions packages/snfoundry/contracts/src/mock_contracts/MockETHToken.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#[starknet::contract]
pub mod MockETHToken {
use openzeppelin::token::erc20::{ERC20Component, ERC20HooksEmptyImpl};
use starknet::ContractAddress;

component!(path: ERC20Component, storage: erc20, event: ERC20Event);

#[abi(embed_v0)]
impl ERC20Impl = ERC20Component::ERC20MixinImpl<ContractState>;
impl ERC20InternalImpl = ERC20Component::InternalImpl<ContractState>;

#[storage]
struct Storage {
#[substorage(v0)]
erc20: ERC20Component::Storage
}

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
ERC20Event: ERC20Component::Event
}

#[constructor]
fn constructor(ref self: ContractState, initial_supply: u256, recipient: ContractAddress) {
let name = "MockETH";
let symbol = "ETH";

self.erc20.initializer(name, symbol);
let amount_to_mint = initial_supply / 10;
self.erc20.mint(recipient, amount_to_mint);
}
}
215 changes: 215 additions & 0 deletions packages/snfoundry/contracts/src/test/TestContract.cairo
Original file line number Diff line number Diff line change
@@ -1 +1,216 @@
use contracts::DiceGame::{IDiceGameDispatcher, IDiceGameDispatcherTrait, DiceGame};
use contracts::RiggedRoll::{IRiggedRollDispatcher, IRiggedRollDispatcherTrait};

use contracts::mock_contracts::MockETHToken;
use keccak::keccak_u256s_le_inputs;
use openzeppelin::token::erc20::interface::{IERC20CamelDispatcher, IERC20CamelDispatcherTrait};
use openzeppelin::utils::serde::SerializedAppend;
use snforge_std::cheatcodes::events::EventsFilterTrait;
use snforge_std::{
declare, ContractClassTrait, spy_events, EventSpyAssertionsTrait, EventSpyTrait, Event,
cheat_caller_address, cheat_block_timestamp, CheatSpan
};
use starknet::{ContractAddress, get_contract_address, get_block_number, get_caller_address};
use starknet::{contract_address_const, get_block_timestamp};

fn OWNER() -> ContractAddress {
contract_address_const::<'OWNER'>()
}

const ROLL_DICE_AMOUNT: u256 = 2000000000000000; // 0.002_ETH_IN_WEI
// Should deploy the MockETHToken contract
fn deploy_mock_eth_token() -> ContractAddress {
let erc20_class_hash = declare("MockETHToken").unwrap();
let INITIAL_SUPPLY: u256 = 100000000000000000000; // 100_ETH_IN_WEI
let reciever = OWNER();
let mut calldata = array![];
calldata.append_serde(INITIAL_SUPPLY);
calldata.append_serde(reciever);
let (eth_token_address, _) = erc20_class_hash.deploy(@calldata).unwrap();
eth_token_address
}

// Should deploy the DiceGame contract
fn deploy_dice_game_contract() -> ContractAddress {
let eth_token_address = deploy_mock_eth_token();
let dice_game_class_hash = declare("DiceGame").unwrap();
let mut calldata = array![];
calldata.append_serde(eth_token_address);
let (dice_game_contract_address, _) = dice_game_class_hash.deploy(@calldata).unwrap();
println!("-- Dice Game contract deployed on: {:?}", dice_game_contract_address);
dice_game_contract_address
}

fn deploy_rigged_roll_contract() -> ContractAddress {
let dice_game_contract_address = deploy_dice_game_contract();
let rigged_roll_class_hash = declare("RiggedRoll").unwrap();
let mut calldata = array![];
calldata.append_serde(dice_game_contract_address);
calldata.append_serde(OWNER());
let (rigged_roll_contract_address, _) = rigged_roll_class_hash.deploy(@calldata).unwrap();
println!("-- Rigged Roll contract deployed on: {:?}", rigged_roll_contract_address);
rigged_roll_contract_address
}

fn get_roll(get_roll_less_than_5: bool, rigged_roll_dispatcher: IRiggedRollDispatcher) -> u256 {
let mut expected_roll = 0;
let dice_game_dispatcher = rigged_roll_dispatcher.dice_game_dispatcher();
let dice_game_contract_address = dice_game_dispatcher.contract_address;
let tester_address = OWNER();
while true {
let prev_block: u256 = get_block_number().into() - 1;
let array = array![prev_block, dice_game_dispatcher.nonce()];
expected_roll = keccak_u256s_le_inputs(array.span()) % 16;
println!("-- Produced roll: {:?}", expected_roll);
if expected_roll <= 5 == get_roll_less_than_5 {
break;
}
let eth_token_dispatcher = dice_game_dispatcher.eth_token_dispatcher();
cheat_caller_address(
eth_token_dispatcher.contract_address, tester_address, CheatSpan::TargetCalls(1)
);
eth_token_dispatcher.approve(dice_game_contract_address, ROLL_DICE_AMOUNT);
cheat_caller_address(dice_game_contract_address, tester_address, CheatSpan::TargetCalls(1));
dice_game_dispatcher.roll_dice(ROLL_DICE_AMOUNT);
};
expected_roll
}
#[test]
fn test_deploy_dice_game() {
deploy_dice_game_contract();
}

#[test]
fn test_deploy_rigged_roll() {
deploy_rigged_roll_contract();
}

#[test]
#[should_panic(expected: ('Not enough ETH',))]
fn test_rigged_roll_fails() {
let rigged_roll_contract_address = deploy_rigged_roll_contract();
let rigged_roll_dispatcher = IRiggedRollDispatcher {
contract_address: rigged_roll_contract_address
};
let eth_amount_wei: u256 = 1000000000000000; // 0.001_ETH_IN_WEI

let tester_address = OWNER();
let eth_token_dispatcher = rigged_roll_dispatcher.dice_game_dispatcher().eth_token_dispatcher();
cheat_caller_address(
eth_token_dispatcher.contract_address, tester_address, CheatSpan::TargetCalls(1)
);
eth_token_dispatcher.approve(rigged_roll_contract_address, eth_amount_wei);
cheat_caller_address(rigged_roll_contract_address, tester_address, CheatSpan::TargetCalls(1));
rigged_roll_dispatcher.rigged_roll(eth_amount_wei);
}

#[test]
fn test_rigged_roll_call_dice_game() {
let rigged_roll_contract_address = deploy_rigged_roll_contract();
let rigged_roll_dispatcher = IRiggedRollDispatcher {
contract_address: rigged_roll_contract_address
};
let dice_game_dispatcher = rigged_roll_dispatcher.dice_game_dispatcher();

let get_roll_less_than_5 = true;
let expected_roll = get_roll(get_roll_less_than_5, rigged_roll_dispatcher);
println!("-- Expect roll to be less than or equal to 5. DiceGame Roll:: {:?}", expected_roll);
let tester_address = OWNER();
let eth_token_dispatcher = dice_game_dispatcher.eth_token_dispatcher();
cheat_caller_address(
eth_token_dispatcher.contract_address, tester_address, CheatSpan::TargetCalls(1)
);
eth_token_dispatcher.approve(rigged_roll_contract_address, ROLL_DICE_AMOUNT);

cheat_caller_address(rigged_roll_contract_address, tester_address, CheatSpan::TargetCalls(1));

let mut spy = spy_events();
rigged_roll_dispatcher.rigged_roll(ROLL_DICE_AMOUNT);

let dice_game_contract = dice_game_dispatcher.contract_address;
let events = spy.get_events().emitted_by(dice_game_contract);

assert_eq!(events.events.len(), 2, "There should be two events emitted by DiceGame contract");
spy
.assert_emitted(
@array![
(
dice_game_contract,
DiceGame::Event::Roll(
DiceGame::Roll {
player: rigged_roll_contract_address,
amount: ROLL_DICE_AMOUNT,
roll: expected_roll
}
)
)
]
);
let (_, event) = events.events.at(1);
assert(event.keys.at(0) == @selector!("Winner"), 'Expected Winner event');
}

#[test]
fn test_rigged_roll_should_not_call_dice_game() {
let rigged_roll_contract_address = deploy_rigged_roll_contract();
let rigged_roll_dispatcher = IRiggedRollDispatcher {
contract_address: rigged_roll_contract_address
};
let dice_game_dispatcher = rigged_roll_dispatcher.dice_game_dispatcher();

let get_roll_less_than_5 = false;
let expected_roll = get_roll(get_roll_less_than_5, rigged_roll_dispatcher);
println!("-- Expect roll to be greater than 5. DiceGame Roll:: {:?}", expected_roll);
let tester_address = OWNER();
let eth_token_dispatcher = dice_game_dispatcher.eth_token_dispatcher();
cheat_caller_address(
eth_token_dispatcher.contract_address, tester_address, CheatSpan::TargetCalls(1)
);
eth_token_dispatcher.approve(rigged_roll_contract_address, ROLL_DICE_AMOUNT);

cheat_caller_address(rigged_roll_contract_address, tester_address, CheatSpan::TargetCalls(1));

let mut spy = spy_events();

rigged_roll_dispatcher.rigged_roll(ROLL_DICE_AMOUNT);

let dice_game_contract = dice_game_dispatcher.contract_address;
let events = spy.get_events().emitted_by(dice_game_contract);

assert_eq!(events.events.len(), 0, "There should be no events emitted by DiceGame contract");
}

#[test]
fn test_withdraw() {
let rigged_roll_contract_address = deploy_rigged_roll_contract();
let rigged_roll_dispatcher = IRiggedRollDispatcher {
contract_address: rigged_roll_contract_address
};

let get_roll_less_than_5 = true;
let expected_roll = get_roll(get_roll_less_than_5, rigged_roll_dispatcher);
println!("-- Expect roll to be less than or equal to 5. DiceGame Roll:: {:?}", expected_roll);
let tester_address = OWNER();
let eth_token_dispatcher = rigged_roll_dispatcher.dice_game_dispatcher().eth_token_dispatcher();
cheat_caller_address(
eth_token_dispatcher.contract_address, tester_address, CheatSpan::TargetCalls(1)
);
eth_token_dispatcher.approve(rigged_roll_contract_address, ROLL_DICE_AMOUNT);

cheat_caller_address(rigged_roll_contract_address, tester_address, CheatSpan::TargetCalls(1));

rigged_roll_dispatcher.rigged_roll(ROLL_DICE_AMOUNT);

let tester_address_prev_balance = eth_token_dispatcher.balanceOf(tester_address);
cheat_caller_address(rigged_roll_contract_address, tester_address, CheatSpan::TargetCalls(1));
let rigged_roll_balance = eth_token_dispatcher.balanceOf(rigged_roll_contract_address);

cheat_caller_address(rigged_roll_contract_address, tester_address, CheatSpan::TargetCalls(1));
rigged_roll_dispatcher.withdraw(tester_address, rigged_roll_balance);
let tester_address_new_balance = eth_token_dispatcher.balanceOf(tester_address);
assert_eq!(
tester_address_new_balance,
tester_address_prev_balance + rigged_roll_balance,
"Tester address should have the balance of the rigged_roll_contract_address"
);
}
2 changes: 1 addition & 1 deletion packages/snfoundry/scripts-ts/deploy-contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,4 +292,4 @@ export {
exportDeployments,
executeDeployCalls,
resetDeployments,
};
};
2 changes: 1 addition & 1 deletion packages/snfoundry/scripts-ts/helpers/parse-deployments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,4 @@ const generateTsAbis = () => {
);
};

generateTsAbis();
generateTsAbis();

0 comments on commit 1cc67c6

Please sign in to comment.