forked from NethermindEth/StarknetByExample
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Random Number Generator (NethermindEth#238)
* feat: dice game vrf application * feat: add summary nav * fix: ran scarb fmt * fix: ran scarb fmt * Fix new lines * Add more info on randomness sources * Rename dice_game_vrf.md->random_number_generator.md and update titles * minor rewording of 1 entropy source * remove anchors * Minor changes to fn names * Implement dice game scaffold * Implement Pragma randomness * minor refactor in randomness request * Implement powerball scaffold * Turn Dice Game into CoinFlip * Implement coin_flip test * Add more tests * Update titles * Remove redundant blank line * Add premium fee calculation into tests * Assert leftover balance * Remove comment about fees * Increase the expected callback fee, update mock to expose fee calc fn * Unfinished: refunded * Store and use is_refunded flag * Implement logic necessary to successfully perform & test refund * Update callback fee limit based on manual testing + update term to deposit * Format * Use a FlipData struct instead of tuple * Fix refund * Simplify CoinFlip to pay the flips itself * CALLBACK_FEE_DEPOSIT->MAX_CALLBACK_FEE_DEPOSIT * Update tests to test the new CoinFlip contract * Fix compile errors * Increase publish_delay to 1 & remove unused imports * Remove starkli-wallet dir * Generate 3 random words for the 1st test * refactor tests * Add missng newline to scarb.toml * fix typo in md * reword 'manipulation' def * Chainlink->Pragma * link to Commit-reveal chapter issue * list 'shut down' as possible centr. issue with ext. oracles * Turn point 5 into a note * Remove Sideways enum * add contract description * Remove ResultTrait from crowdfunding tests.cairo --------- Co-authored-by: Tony Stark <[email protected]> Co-authored-by: Nenad <[email protected]> Co-authored-by: Nenad <[email protected]>
- Loading branch information
Showing
13 changed files
with
723 additions
and
4 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 |
---|---|---|
|
@@ -9,3 +9,4 @@ output | |
# Others | ||
.snfoundry_cache | ||
.vscode/settings.json | ||
**/starkli-wallet |
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
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 |
---|---|---|
@@ -0,0 +1,2 @@ | ||
target | ||
.snfoundry_cache/ |
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,21 @@ | ||
[package] | ||
name = "coin_flip" | ||
version.workspace = true | ||
edition = "2024_07" | ||
|
||
[lib] | ||
|
||
[dependencies] | ||
starknet.workspace = true | ||
openzeppelin.workspace = true | ||
pragma_lib.workspace = true | ||
snforge_std.workspace = true | ||
|
||
[dev-dependencies] | ||
assert_macros.workspace = true | ||
|
||
[scripts] | ||
test.workspace = true | ||
|
||
[[target.starknet-contract]] | ||
build-external-contracts = ["openzeppelin_presets::erc20::ERC20Upgradeable"] |
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,164 @@ | ||
use starknet::ContractAddress; | ||
|
||
#[starknet::interface] | ||
pub trait ICoinFlip<TContractState> { | ||
fn flip(ref self: TContractState); | ||
} | ||
|
||
// declares just the pragma_lib::abi::IRandomness.receive_random_words function | ||
#[starknet::interface] | ||
pub trait IPragmaVRF<TContractState> { | ||
fn receive_random_words( | ||
ref self: TContractState, | ||
requestor_address: ContractAddress, | ||
request_id: u64, | ||
random_words: Span<felt252>, | ||
calldata: Array<felt252> | ||
); | ||
} | ||
|
||
#[starknet::contract] | ||
pub mod CoinFlip { | ||
use core::num::traits::zero::Zero; | ||
use starknet::{ContractAddress, get_caller_address, get_contract_address,}; | ||
use starknet::storage::{ | ||
Map, StoragePointerReadAccess, StoragePathEntry, StoragePointerWriteAccess | ||
}; | ||
use pragma_lib::abi::{IRandomnessDispatcher, IRandomnessDispatcherTrait}; | ||
use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; | ||
|
||
#[storage] | ||
struct Storage { | ||
eth_dispatcher: IERC20Dispatcher, | ||
flips: Map<u64, ContractAddress>, | ||
nonce: u64, | ||
randomness_contract_address: ContractAddress, | ||
} | ||
|
||
#[event] | ||
#[derive(Drop, starknet::Event)] | ||
pub enum Event { | ||
Flipped: Flipped, | ||
Landed: Landed, | ||
} | ||
|
||
#[derive(Drop, starknet::Event)] | ||
pub struct Flipped { | ||
pub flip_id: u64, | ||
pub flipper: ContractAddress, | ||
} | ||
|
||
#[derive(Drop, starknet::Event)] | ||
pub struct Landed { | ||
pub flip_id: u64, | ||
pub flipper: ContractAddress, | ||
pub side: Side | ||
} | ||
|
||
#[derive(Drop, Debug, PartialEq, Serde)] | ||
pub enum Side { | ||
Heads, | ||
Tails, | ||
} | ||
|
||
pub mod Errors { | ||
pub const CALLER_NOT_RANDOMNESS: felt252 = 'Caller not randomness contract'; | ||
pub const INVALID_ADDRESS: felt252 = 'Invalid address'; | ||
pub const INVALID_FLIP_ID: felt252 = 'No flip with the given ID'; | ||
pub const REQUESTOR_NOT_SELF: felt252 = 'Requestor is not self'; | ||
pub const TRANSFER_FAILED: felt252 = 'Transfer failed'; | ||
} | ||
|
||
pub const PUBLISH_DELAY: u64 = 1; // return the random value asap | ||
pub const NUM_OF_WORDS: u64 = 1; // one random value is sufficient | ||
pub const CALLBACK_FEE_LIMIT: u128 = 100_000_000_000_000; // 0.0001 ETH | ||
pub const MAX_CALLBACK_FEE_DEPOSIT: u256 = | ||
500_000_000_000_000; // CALLBACK_FEE_LIMIT * 5; needs to cover the Premium fee | ||
|
||
#[constructor] | ||
fn constructor( | ||
ref self: ContractState, | ||
randomness_contract_address: ContractAddress, | ||
eth_address: ContractAddress | ||
) { | ||
assert(randomness_contract_address.is_non_zero(), Errors::INVALID_ADDRESS); | ||
assert(eth_address.is_non_zero(), Errors::INVALID_ADDRESS); | ||
self.randomness_contract_address.write(randomness_contract_address); | ||
self.eth_dispatcher.write(IERC20Dispatcher { contract_address: eth_address }); | ||
} | ||
|
||
#[abi(embed_v0)] | ||
impl CoinFlip of super::ICoinFlip<ContractState> { | ||
/// The contract needs to be funded with some ETH in order for this function | ||
/// to be callable. For simplicity, anyone can fund the contract. | ||
fn flip(ref self: ContractState) { | ||
let flip_id = self._request_my_randomness(); | ||
let flipper = get_caller_address(); | ||
self.flips.entry(flip_id).write(flipper); | ||
self.emit(Event::Flipped(Flipped { flip_id, flipper })); | ||
} | ||
} | ||
|
||
#[abi(embed_v0)] | ||
impl PragmaVRF of super::IPragmaVRF<ContractState> { | ||
fn receive_random_words( | ||
ref self: ContractState, | ||
requestor_address: ContractAddress, | ||
request_id: u64, | ||
random_words: Span<felt252>, | ||
calldata: Array<felt252> | ||
) { | ||
let caller = get_caller_address(); | ||
assert( | ||
caller == self.randomness_contract_address.read(), Errors::CALLER_NOT_RANDOMNESS | ||
); | ||
|
||
let this = get_contract_address(); | ||
assert(requestor_address == this, Errors::REQUESTOR_NOT_SELF); | ||
|
||
self._process_coin_flip(request_id, random_words.at(0)); | ||
} | ||
} | ||
|
||
#[generate_trait] | ||
impl Private of PrivateTrait { | ||
fn _request_my_randomness(ref self: ContractState) -> u64 { | ||
let randomness_contract_address = self.randomness_contract_address.read(); | ||
let randomness_dispatcher = IRandomnessDispatcher { | ||
contract_address: randomness_contract_address | ||
}; | ||
|
||
let this = get_contract_address(); | ||
|
||
// Approve the randomness contract to transfer the callback deposit/fee | ||
let eth_dispatcher = self.eth_dispatcher.read(); | ||
eth_dispatcher.approve(randomness_contract_address, MAX_CALLBACK_FEE_DEPOSIT); | ||
|
||
let nonce = self.nonce.read(); | ||
|
||
// Request the randomness to be used to construct the winning combination | ||
let request_id = randomness_dispatcher | ||
.request_random( | ||
nonce, this, CALLBACK_FEE_LIMIT, PUBLISH_DELAY, NUM_OF_WORDS, array![] | ||
); | ||
|
||
self.nonce.write(nonce + 1); | ||
|
||
request_id | ||
} | ||
|
||
fn _process_coin_flip(ref self: ContractState, flip_id: u64, random_value: @felt252) { | ||
let flipper = self.flips.entry(flip_id).read(); | ||
assert(flipper.is_non_zero(), Errors::INVALID_FLIP_ID); | ||
|
||
let random_value: u256 = (*random_value).into(); | ||
let side = if random_value % 2 == 0 { | ||
Side::Heads | ||
} else { | ||
Side::Tails | ||
}; | ||
|
||
self.emit(Event::Landed(Landed { flip_id, flipper, side })); | ||
} | ||
} | ||
} |
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 @@ | ||
mod contract; | ||
mod mock_randomness; | ||
|
||
#[cfg(test)] | ||
mod tests; |
Oops, something went wrong.