Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: note type ids #4500

Merged
merged 19 commits into from
Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions boxes/blank/src/contracts/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ contract Blank {
contract_address: AztecAddress,
nonce: Field,
storage_slot: Field,
note_type_id: Field,
serialized_note: [Field; 0]
) -> pub [Field; 4] {
[0, 0, 0, 0]
Expand Down
4 changes: 2 additions & 2 deletions boxes/token/src/contracts/Nargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ compiler_version = ">=0.18.0"
type = "contract"

[dependencies]
value_note = { path = "../../../../yarn-project/aztec-nr/value-note"}
aztec = { path = "../../../../yarn-project/aztec-nr/aztec" }
safe_math = { path = "../../../../yarn-project/aztec-nr/safe-math" }
compressed_string = {path = "../../../../yarn-project/aztec-nr/compressed-string"}
authwit = { path = "../../../../yarn-project/aztec-nr/authwit" }
aztec = { path = "../../../../yarn-project/aztec-nr/aztec" }
101 changes: 84 additions & 17 deletions boxes/token/src/contracts/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ contract Token {
use dep::std::option::Option;

use dep::safe_math::SafeU120;
use dep::compressed_string::FieldCompressedString;

use dep::aztec::{
note::{
Expand All @@ -22,10 +23,10 @@ contract Token {
utils as note_utils,
},
hash::{compute_secret_hash},
state_vars::{map::Map, public_state::PublicState, set::Set},
state_vars::{map::Map, public_state::PublicState, stable_public_state::StablePublicState, set::Set},
protocol_types::{
abis::function_selector::FunctionSelector,
address::AztecAddress,
address::AztecAddress
}
};

Expand All @@ -41,7 +42,7 @@ contract Token {
use crate::types::{
transparent_note::TransparentNote,
token_note::{TokenNote, TOKEN_NOTE_LEN},
balances_map::{BalancesMap},
balances_map::BalancesMap
};
// docs:end::imports

Expand All @@ -51,24 +52,35 @@ contract Token {
admin: PublicState<AztecAddress>,
// docs:end:storage_admin
// docs:start:storage_minters
minters: Map<AztecAddress, PublicState<bool>>,
minters: Map<AztecAddress, PublicState<bool>>,
// docs:end:storage_minters
// docs:start:storage_balances
balances: BalancesMap<TokenNote>,
// docs:end:storage_balances
total_supply: PublicState<SafeU120>,
total_supply: PublicState<SafeU120>,
// docs:start:storage_pending_shields
pending_shields: Set<TransparentNote>,
pending_shields: Set<TransparentNote>,
// docs:end:storage_pending_shields
public_balances: Map<AztecAddress, PublicState<SafeU120>>,
symbol: StablePublicState<FieldCompressedString>,
name: StablePublicState<FieldCompressedString>,
// docs:start:storage_decimals
decimals: StablePublicState<u8>,
// docs:end:storage_decimals
}
// docs:end:storage_struct

// docs:start:constructor
#[aztec(private)]
fn constructor(admin: AztecAddress) {
let selector = FunctionSelector::from_signature("_initialize((Field))");
context.call_public_function(context.this_address(), selector, [admin.to_field()]);
fn constructor(admin: AztecAddress, name: str<31>, symbol: str<31>, decimals: u8) {
let selector = FunctionSelector::from_signature("_initialize((Field),(Field),(Field),u8)");
let name_s = FieldCompressedString::from_string(name);
let symbol_s = FieldCompressedString::from_string(symbol);
context.call_public_function(
context.this_address(),
selector,
[admin.to_field(), name_s.serialize()[0], symbol_s.serialize()[0], decimals as Field]
);
}
// docs:end:constructor

Expand All @@ -82,6 +94,52 @@ contract Token {
}
// docs:end:set_admin

#[aztec(public)]
fn public_get_name() -> pub FieldCompressedString {
storage.name.read_public()
}

#[aztec(private)]
fn private_get_name() -> pub FieldCompressedString {
storage.name.read_private()
}

unconstrained fn un_get_name() -> pub [u8; 31] {
storage.name.read_public().to_bytes()
}

#[aztec(public)]
fn public_get_symbol() -> pub FieldCompressedString {
storage.symbol.read_public()
}

#[aztec(private)]
fn private_get_symbol() -> pub FieldCompressedString {
storage.symbol.read_private()
}

unconstrained fn un_get_symbol() -> pub [u8; 31] {
storage.symbol.read_public().to_bytes()
}

#[aztec(public)]
fn public_get_decimals() -> pub u8 {
// docs:start:read_decimals_public
storage.decimals.read_public()
// docs:end:read_decimals_public
}

#[aztec(private)]
fn private_get_decimals() -> pub u8 {
// docs:start:read_decimals_private
storage.decimals.read_private()
// docs:end:read_decimals_private
}

unconstrained fn un_get_decimals() -> pub u8 {
storage.decimals.read_public()
}

// docs:start:set_minter
#[aztec(public)]
fn set_minter(minter: AztecAddress, approve: bool) {
Expand Down Expand Up @@ -188,12 +246,12 @@ contract Token {
fn redeem_shield(to: AztecAddress, amount: Field, secret: Field) {
let pending_shields = storage.pending_shields;
let secret_hash = compute_secret_hash(secret);
// Get 1 note (set_limit(1)) which has amount stored in field with index 0 (select(0, amount, Option::none())) and secret_hash
// stored in field with index 1 (select(1, secret_hash, Option::none())).
// Get 1 note (set_limit(1)) which has amount stored in field with index 0 (select(0, amount)) and secret_hash
// stored in field with index 1 (select(1, secret_hash)).
let options = NoteGetterOptions::new().select(0, amount, Option::none()).select(1, secret_hash, Option::none()).set_limit(1);
let notes = pending_shields.get_notes(options);
let note = notes[0].unwrap_unchecked();
// Remove the note from the pending shields set
// Remove the note from the pending shields set
pending_shields.remove(note);

// Add the token note to user's balances set
Expand Down Expand Up @@ -254,10 +312,20 @@ contract Token {

// docs:start:initialize
#[aztec(public)]
internal fn _initialize(new_admin: AztecAddress) {
internal fn _initialize(
new_admin: AztecAddress,
name: FieldCompressedString,
symbol: FieldCompressedString,
decimals: u8
) {
assert(!new_admin.is_zero(), "invalid admin");
storage.admin.write(new_admin);
storage.minters.at(new_admin).write(true);
storage.name.initialize(name);
storage.symbol.initialize(symbol);
// docs:start:initialize_decimals
storage.decimals.initialize(decimals);
// docs:end:initialize_decimals
}
// docs:end:initialize

Expand Down Expand Up @@ -312,9 +380,6 @@ contract Token {
}
// docs:end:balance_of_public

// Below this point is the stuff of nightmares.
// This should ideally not be required. What do we do if vastly different types of serialized_notes?

// docs:start:compute_note_hash_and_nullifier
// Computes note hash and nullifier.
// Note 1: Needs to be defined by every contract producing logs.
Expand All @@ -323,10 +388,12 @@ contract Token {
contract_address: AztecAddress,
nonce: Field,
storage_slot: Field,
note_type_id: Field,
serialized_note: [Field; TOKEN_NOTE_LEN]
) -> pub [Field; 4] {
let note_header = NoteHeader::new(contract_address, nonce, storage_slot);
if (storage_slot == storage.pending_shields.get_storage_slot()) {

if (note_type_id == TransparentNote::get_note_type_id()) {
note_utils::compute_note_hash_and_nullifier(
TransparentNote::deserialize_content,
note_header,
Expand Down
1 change: 0 additions & 1 deletion boxes/token/src/contracts/src/types/balances_map.nr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use dep::std::option::Option;
use dep::safe_math::SafeU120;
use dep::aztec::{
context::Context,
hash::pedersen_hash,
protocol_types::{
address::AztecAddress,
constants::MAX_READ_REQUESTS_PER_CALL,
Expand Down
9 changes: 8 additions & 1 deletion boxes/token/src/contracts/src/types/token_note.nr
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,18 @@ impl NoteInterface<TOKEN_NOTE_LEN> for TokenNote {
context,
(*context).this_address(),
slot,
Self::get_note_type_id(),
encryption_pub_key,
self.serialize_content(),
);
}
}
}

fn get_note_type_id() -> Field {
// TODO(#4519): autogenerate
// python -c "print(int(''.join(str(ord(c)) for c in 'TokenNote')))"
8411110710111078111116101
}
}

impl OwnedNote for TokenNote {
Expand Down
6 changes: 6 additions & 0 deletions boxes/token/src/contracts/src/types/transparent_note.nr
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ impl NoteInterface<TRANSPARENT_NOTE_LEN> for TransparentNote {
fn broadcast(self, context: &mut PrivateContext, slot: Field) {
assert(false, "TransparentNote does not support broadcast");
}

fn get_note_type_id() -> Field {
// TODO(#4519): autogenerate
// python -c "print(int(''.join(str(ord(c)) for c in 'TransparentNote')))"
84114971101151129711410111011678111116101
}
}

impl TransparentNote {
Expand Down
17 changes: 15 additions & 2 deletions boxes/token/src/tests/token.contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ import { afterEach, beforeAll, expect, jest } from '@jest/globals';
import { setupEnvironment } from '../environment/index.js';

const TIMEOUT = 60_000;
const TOKEN_NAME = 'Aztec Token';
const TOKEN_SYMBOL = 'AZT';
const TOKEN_DECIMALS = 18n;

describe('e2e_token_contract', () => {
jest.setTimeout(TIMEOUT);
Expand All @@ -36,8 +39,16 @@ describe('e2e_token_contract', () => {

const addPendingShieldNoteToPXE = async (accountIndex: number, amount: bigint, secretHash: Fr, txHash: TxHash) => {
const storageSlot = new Fr(5); // The storage slot of `pending_shields` is 5.
const noteTypeId = new Fr(84114971101151129711410111011678111116101n); // TransparentNote
const note = new Note([new Fr(amount), secretHash]);
const extendedNote = new ExtendedNote(note, accounts[accountIndex].address, asset.address, storageSlot, txHash);
const extendedNote = new ExtendedNote(
note,
accounts[accountIndex].address,
asset.address,
storageSlot,
noteTypeId,
txHash,
);
await wallets[accountIndex].addNote(extendedNote);
};

Expand All @@ -51,7 +62,9 @@ describe('e2e_token_contract', () => {
logger(`Accounts: ${accounts.map(a => a.toReadableString())}`);
logger(`Wallets: ${wallets.map(w => w.getAddress().toString())}`);

asset = await TokenContract.deploy(wallets[0], accounts[0].address).send().deployed();
asset = await TokenContract.deploy(wallets[0], accounts[0], TOKEN_NAME, TOKEN_SYMBOL, TOKEN_DECIMALS)
.send()
.deployed();
logger(`Token deployed to ${asset.address}`);
tokenSim = new TokenSimulator(
asset,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ Add a new function into your contract as shown below:

#include_code nullifier /yarn-project/noir-contracts/contracts/counter_contract/src/main.nr rust

Here, we're computing both the note hash and the nullifier. The nullifier computation uses Aztec’s `compute_note_hash_and_nullifier` function, which takes details about the note's attributes eg contract address, nonce, storage slot, and preimage.
Here, we're computing both the note hash and the nullifier. The nullifier computation uses Aztec’s `compute_note_hash_and_nullifier` function, which takes details about the note's attributes eg contract address, nonce, storage slot, type id, and preimage.

## Getting a counter

Expand Down
2 changes: 1 addition & 1 deletion docs/docs/developers/tutorials/writing_token_contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ contract Token {

unconstrained fn balance_of_public(owner: AztecAddress) -> Field {}

unconstrained fn compute_note_hash_and_nullifier(contract_address: Field, nonce: Field, storage_slot: Field, serialized_note: [Field; VALUE_NOTE_LEN]) -> [Field; 4] {}
unconstrained fn compute_note_hash_and_nullifier(contract_address: Field, nonce: Field, storage_slot: Field, note_type_id: Field, serialized_note: [Field; VALUE_NOTE_LEN]) -> [Field; 4] {}
}
```

Expand Down
Loading
Loading