-
Notifications
You must be signed in to change notification settings - Fork 265
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Crowdfunding contract implemented during offsite.
- Loading branch information
Showing
15 changed files
with
617 additions
and
2 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
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
9 changes: 9 additions & 0 deletions
9
noir-projects/noir-contracts/contracts/claim_contract/Nargo.toml
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,9 @@ | ||
[package] | ||
name = "claim_contract" | ||
authors = [""] | ||
compiler_version = ">=0.18.0" | ||
type = "contract" | ||
|
||
[dependencies] | ||
aztec = { path = "../../../aztec-nr/aztec" } | ||
value_note = { path = "../../../aztec-nr/value-note" } |
37 changes: 37 additions & 0 deletions
37
noir-projects/noir-contracts/contracts/claim_contract/src/interfaces.nr
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,37 @@ | ||
use dep::aztec::{ | ||
protocol_types::{abis::function_selector::FunctionSelector, address::AztecAddress}, | ||
context::PrivateContext, | ||
}; | ||
|
||
struct Token { | ||
address: AztecAddress, | ||
} | ||
|
||
impl Token { | ||
pub fn at(address: AztecAddress) -> Self { | ||
Self { address } | ||
} | ||
|
||
fn mint_public(self: Self, context: &mut PrivateContext, to: AztecAddress, amount: Field) { | ||
let _ret = context.call_public_function( | ||
self.address, | ||
FunctionSelector::from_signature("mint_public((Field),Field)"), | ||
[to.to_field(), amount] | ||
); | ||
} | ||
|
||
pub fn transfer( | ||
self: Self, | ||
context: &mut PrivateContext, | ||
from: AztecAddress, | ||
to: AztecAddress, | ||
amount: Field, | ||
nonce: Field | ||
) { | ||
let _ret = context.call_private_function( | ||
self.address, | ||
FunctionSelector::from_signature("transfer((Field),(Field),Field,Field)"), | ||
[from.to_field(), to.to_field(), amount, nonce] | ||
); | ||
} | ||
} |
59 changes: 59 additions & 0 deletions
59
noir-projects/noir-contracts/contracts/claim_contract/src/main.nr
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,59 @@ | ||
contract Claim { | ||
mod interfaces; | ||
|
||
use dep::aztec::{ | ||
history::note_inclusion::prove_note_inclusion, | ||
protocol_types::{ | ||
abis::function_selector::FunctionSelector, | ||
address::AztecAddress, | ||
}, | ||
state_vars::SharedImmutable, | ||
}; | ||
use dep::value_note::value_note::ValueNote; | ||
use interfaces::Token; | ||
|
||
struct Storage { | ||
// Address of a contract based on whose notes we distribute the rewards | ||
target_contract: SharedImmutable<AztecAddress>, | ||
// Token to be distributed as a reward when claiming | ||
reward_token: SharedImmutable<AztecAddress>, | ||
} | ||
|
||
#[aztec(private)] | ||
fn constructor(target_contract: AztecAddress, reward_token: AztecAddress) { | ||
let selector = FunctionSelector::from_signature("_initialize((Field),(Field))"); | ||
context.call_public_function( | ||
context.this_address(), | ||
selector, | ||
[target_contract.to_field(), reward_token.to_field()] | ||
); | ||
} | ||
|
||
#[aztec(public)] | ||
#[aztec(internal)] | ||
#[aztec(noinitcheck)] | ||
fn _initialize(target_contract: AztecAddress, reward_token: AztecAddress) { | ||
storage.target_contract.initialize(target_contract); | ||
storage.reward_token.initialize(reward_token); | ||
} | ||
|
||
#[aztec(private)] | ||
fn claim(proof_note: ValueNote) { | ||
// 1) Check that the note corresponds to the target contract | ||
let target_address = storage.target_contract.read_private(); | ||
assert(target_address == proof_note.header.contract_address, "Note does not correspond to the target contract"); | ||
|
||
// 2) Prove that the note hash exists in the note hash tree | ||
prove_note_inclusion(proof_note, context); | ||
|
||
// 3) Compute and emit a nullifier which is unique to the note and this contract to ensure the reward can be | ||
// claimed only once with the given note. | ||
// Note: The nullifier is unique to the note and THIS contract because the protocol siloes all nullifiers with | ||
// the address of a contract it was emitted from. | ||
context.push_new_nullifier(proof_note.compute_nullifier(&mut context), 0); | ||
|
||
// 4) Finally we mint the reward token to the sender of the transaction | ||
let reward_token = Token::at(storage.reward_token.read_private()); | ||
reward_token.mint_public(&mut context, context.msg_sender(), proof_note.value); | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
noir-projects/noir-contracts/contracts/crowdfunding_contract/Nargo.toml
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,9 @@ | ||
[package] | ||
name = "crowdfunding_contract" | ||
authors = [""] | ||
compiler_version = ">=0.18.0" | ||
type = "contract" | ||
|
||
[dependencies] | ||
aztec = { path = "../../../aztec-nr/aztec" } | ||
value_note = { path = "../../../aztec-nr/value-note" } |
27 changes: 27 additions & 0 deletions
27
noir-projects/noir-contracts/contracts/crowdfunding_contract/src/interfaces.nr
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,27 @@ | ||
use dep::aztec::protocol_types::{abis::function_selector::FunctionSelector, address::{AztecAddress, EthAddress}}; | ||
use dep::aztec::{context::{PrivateContext, PublicContext}}; | ||
|
||
struct Token { | ||
address: AztecAddress, | ||
} | ||
|
||
impl Token { | ||
pub fn at(address: AztecAddress) -> Self { | ||
Self { address } | ||
} | ||
|
||
pub fn transfer( | ||
self: Self, | ||
context: &mut PrivateContext, | ||
from: AztecAddress, | ||
to: AztecAddress, | ||
amount: Field, | ||
nonce: Field | ||
) { | ||
let _ret = context.call_private_function( | ||
self.address, | ||
FunctionSelector::from_signature("transfer((Field),(Field),Field,Field)"), | ||
[from.to_field(), to.to_field(), amount, nonce] | ||
); | ||
} | ||
} |
108 changes: 108 additions & 0 deletions
108
noir-projects/noir-contracts/contracts/crowdfunding_contract/src/main.nr
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,108 @@ | ||
contract Crowdfunding { | ||
mod interfaces; | ||
|
||
use dep::aztec::{ | ||
log::emit_unencrypted_log_from_private, | ||
protocol_types::{ | ||
abis::function_selector::FunctionSelector, | ||
address::AztecAddress, | ||
traits::Serialize | ||
}, | ||
state_vars::{PrivateSet, PublicImmutable, SharedImmutable}, | ||
}; | ||
use dep::value_note::value_note::ValueNote; | ||
use interfaces::Token; | ||
|
||
#[event] | ||
struct WithdrawalProcessed { | ||
who: AztecAddress, | ||
amount: u64, | ||
} | ||
|
||
impl Serialize<2> for WithdrawalProcessed { | ||
fn serialize(self: Self) -> [Field; 2] { | ||
[self.who.to_field(), self.amount as Field] | ||
} | ||
} | ||
|
||
struct Storage { | ||
// Token used for donations (e.g. DAI) | ||
donation_token: SharedImmutable<AztecAddress>, | ||
// Crowdfunding campaign operator | ||
operator: SharedImmutable<AztecAddress>, | ||
// End of the crowdfunding campaign after which no more donations are accepted | ||
// TODO(#4990): Make deadline a u64 once the neccessary traits are implemented | ||
deadline: PublicImmutable<Field>, | ||
// Notes emitted to donors when they donate (later on used to claim rewards in the Claim contract) | ||
claim_notes: PrivateSet<ValueNote>, | ||
} | ||
|
||
#[aztec(private)] | ||
fn constructor(donation_token: AztecAddress, operator: AztecAddress, deadline: u64) { | ||
let selector = FunctionSelector::from_signature("_initialize((Field),(Field),Field)"); | ||
context.call_public_function( | ||
context.this_address(), | ||
selector, | ||
[donation_token.to_field(), operator.to_field(), deadline as Field] | ||
); | ||
} | ||
|
||
#[aztec(public)] | ||
#[aztec(internal)] | ||
#[aztec(noinitcheck)] | ||
// TODO(#4990): Make deadline a u64 once the neccessary traits are implemented | ||
fn _initialize(donation_token: AztecAddress, operator: AztecAddress, deadline: Field) { | ||
storage.donation_token.initialize(donation_token); | ||
storage.operator.initialize(operator); | ||
storage.deadline.initialize(deadline); | ||
} | ||
|
||
#[aztec(public)] | ||
#[aztec(internal)] | ||
fn _check_deadline() { | ||
// TODO(#4990): Remove the cast here once u64 is used directly | ||
let deadline = storage.deadline.read() as u64; | ||
assert(context.timestamp() as u64 < deadline, "Deadline has passed"); | ||
} | ||
|
||
#[aztec(private)] | ||
fn donate(amount: u64) { | ||
// 1) Check that the deadline has not passed | ||
context.call_public_function( | ||
context.this_address(), | ||
FunctionSelector::from_signature("_check_deadline()"), | ||
[] | ||
); | ||
|
||
// 2) Transfer the donation tokens from donor to this contract | ||
let donation_token = Token::at(storage.donation_token.read_private()); | ||
donation_token.transfer( | ||
&mut context, | ||
context.msg_sender(), | ||
context.this_address(), | ||
amount as Field, | ||
0 | ||
); | ||
|
||
// 3) Create a value note for the donor so that he can later on claim a rewards token in the Claim | ||
// contract by proving that the hash of this note exists in the note hash tree. | ||
let mut note = ValueNote::new(amount as Field, context.msg_sender()); | ||
storage.claim_notes.insert(&mut note, true); | ||
} | ||
|
||
// Withdraws balance to the operator. Requires that msg_sender() is the operator. | ||
#[aztec(private)] | ||
fn withdraw(amount: u64) { | ||
// 1) Check that msg_sender() is the operator | ||
let operator_address = storage.operator.read_private(); | ||
assert(context.msg_sender() == operator_address, "Not an operator"); | ||
|
||
// 2) Transfer the donation tokens from this contract to the operator | ||
let donation_token = Token::at(storage.donation_token.read_private()); | ||
donation_token.transfer(&mut context, context.this_address(), operator_address, amount as Field, 0); | ||
|
||
// 3) Emit an unencrypted event so that anyone can audit how much the operator has withdrawn | ||
let event = WithdrawalProcessed { amount, who: operator_address }; | ||
emit_unencrypted_log_from_private(&mut context, event.serialize()); | ||
} | ||
} |
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
Oops, something went wrong.