Skip to content
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
10 changes: 10 additions & 0 deletions packages/macros/src/attribute/with_components/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,16 @@ pub mod warnings {
"
};

/// Warning when the ERC6909TokenSupply hook call is missing.
pub const ERC6909_TOKEN_SUPPLY_HOOKS_MISSING: &str = indoc! {
"The ERC6909TokenSupply component requires calling
`self.erc6909_token_supply.update_token_supply(...)` from an `ERC6909HooksTrait` hook,
and it looks like it is missing.

This may lead to incorrect total supply tracking.
"
};

/// Warning when the Upgradeable component is not used.
pub const UPGRADEABLE_NOT_USED: &str = indoc! {
"It looks like the `self.upgradeable.upgrade(new_class_hash)` function is not used in the contract. If
Expand Down
8 changes: 8 additions & 0 deletions packages/macros/src/attribute/with_components/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,14 @@ fn add_per_component_warnings(
warnings.push(warning);
}
}
AllowedComponents::ERC6909TokenSupply => {
let hook_called = facts.has_call(&["erc6909_token_supply", "update_token_supply"])
|| facts.has_call(&["ERC6909TokenSupplyInternalImpl", "update_token_supply"]);
if !hook_called {
let warning = Diagnostic::warn(warnings::ERC6909_TOKEN_SUPPLY_HOOKS_MISSING);
warnings.push(warning);
}
}
AllowedComponents::Upgradeable => {
// Check that the upgrade function is called
let upgrade_function_called = facts.has_call(&["self", "upgradeable", "upgrade"]);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
---
source: src/tests/test_with_components.rs
expression: result
---
TokenStream:

#[starknet::contract]
pub mod MyToken {
use starknet::ContractAddress;
#[storage]
pub struct Storage {
#[substorage(v0)]
pub erc6909: ERC6909Component::Storage,
#[substorage(v0)]
pub erc6909_token_supply: ERC6909TokenSupplyComponent::Storage,
#[substorage(v0)]
pub src5: SRC5Component::Storage,
}
#[constructor]
fn constructor(ref self: ContractState) {
self.erc6909.initializer();
self.erc6909_token_supply.initializer();
}
impl ERC6909HooksImpl of ERC6909Component::ERC6909HooksTrait<ContractState> {
fn before_update(
ref self: ERC6909Component::ComponentState<ContractState>,
sender: ContractAddress,
receiver: ContractAddress,
id: u256,
amount: u256,
) {
let mut contract_state = self.get_contract_mut();
contract_state.erc6909_token_supply.update_token_supply(sender, receiver, id, amount);
}
}
use openzeppelin_introspection::src5::SRC5Component;
use openzeppelin_token::erc6909::ERC6909Component;
use openzeppelin_token::erc6909::extensions::ERC6909TokenSupplyComponent;

component!(path: ERC6909Component, storage: erc6909, event: ERC6909Event);
component!(
path: ERC6909TokenSupplyComponent,
storage: erc6909_token_supply,
event: ERC6909TokenSupplyEvent,
);
component!(path: SRC5Component, storage: src5, event: SRC5Event);

impl ERC6909InternalImpl = ERC6909Component::InternalImpl<ContractState>;
impl ERC6909TokenSupplyInternalImpl = ERC6909TokenSupplyComponent::InternalImpl<ContractState>;
impl SRC5InternalImpl = SRC5Component::InternalImpl<ContractState>;

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
ERC6909Event: ERC6909Component::Event,
#[flat]
ERC6909TokenSupplyEvent: ERC6909TokenSupplyComponent::Event,
#[flat]
SRC5Event: SRC5Component::Event,
}
}


Diagnostics:

None

AuxData:

None
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
---
source: src/tests/test_with_components.rs
expression: result
---
TokenStream:

#[starknet::contract]
pub mod MyToken {
use openzeppelin_token::erc6909::ERC6909HooksEmptyImpl;
#[storage]
pub struct Storage {
#[substorage(v0)]
pub erc6909: ERC6909Component::Storage,
#[substorage(v0)]
pub erc6909_token_supply: ERC6909TokenSupplyComponent::Storage,
#[substorage(v0)]
pub src5: SRC5Component::Storage,
}
#[constructor]
fn constructor(ref self: ContractState) {
self.erc6909.initializer();
self.erc6909_token_supply.initializer();
}
use openzeppelin_introspection::src5::SRC5Component;
use openzeppelin_token::erc6909::ERC6909Component;
use openzeppelin_token::erc6909::extensions::ERC6909TokenSupplyComponent;

component!(path: ERC6909Component, storage: erc6909, event: ERC6909Event);
component!(
path: ERC6909TokenSupplyComponent,
storage: erc6909_token_supply,
event: ERC6909TokenSupplyEvent,
);
component!(path: SRC5Component, storage: src5, event: SRC5Event);

impl ERC6909InternalImpl = ERC6909Component::InternalImpl<ContractState>;
impl ERC6909TokenSupplyInternalImpl = ERC6909TokenSupplyComponent::InternalImpl<ContractState>;
impl SRC5InternalImpl = SRC5Component::InternalImpl<ContractState>;

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
ERC6909Event: ERC6909Component::Event,
#[flat]
ERC6909TokenSupplyEvent: ERC6909TokenSupplyComponent::Event,
#[flat]
SRC5Event: SRC5Component::Event,
}
}


Diagnostics:

====
Warning: The ERC6909TokenSupply component requires calling
`self.erc6909_token_supply.update_token_supply(...)` from an `ERC6909HooksTrait` hook,
and it looks like it is missing.

This may lead to incorrect total supply tracking.
====

AuxData:

None
59 changes: 59 additions & 0 deletions packages/macros/src/tests/test_with_components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1503,6 +1503,65 @@ fn test_with_timelock_controller_no_initializer() {
assert_snapshot!(result);
}

#[test]
fn test_with_erc6909_token_supply() {
let attribute = quote! { (ERC6909, ERC6909TokenSupply, SRC5) };
let item = quote! {
#[starknet::contract]
pub mod MyToken {
use starknet::ContractAddress;

#[storage]
pub struct Storage {}

#[constructor]
fn constructor(ref self: ContractState) {
self.erc6909.initializer();
self.erc6909_token_supply.initializer();
}

impl ERC6909HooksImpl of ERC6909Component::ERC6909HooksTrait<ContractState> {
fn before_update(
ref self: ERC6909Component::ComponentState<ContractState>,
sender: ContractAddress,
receiver: ContractAddress,
id: u256,
amount: u256,
) {
let mut contract_state = self.get_contract_mut();
contract_state
.erc6909_token_supply
.update_token_supply(sender, receiver, id, amount);
}
}
}
};
let result = get_string_result(attribute, item);
assert_snapshot!(result);
}

#[test]
fn test_with_erc6909_token_supply_no_hook_call() {
let attribute = quote! { (ERC6909, ERC6909TokenSupply, SRC5) };
let item = quote! {
#[starknet::contract]
pub mod MyToken {
use openzeppelin_token::erc6909::ERC6909HooksEmptyImpl;

#[storage]
pub struct Storage {}

#[constructor]
fn constructor(ref self: ContractState) {
self.erc6909.initializer();
self.erc6909_token_supply.initializer();
}
}
};
let result = get_string_result(attribute, item);
assert_snapshot!(result);
}

#[test]
fn test_with_votes() {
let attribute = quote! { (Votes) };
Expand Down