diff --git a/contracts/src/access_control/access_controller.cairo b/contracts/src/access_control/access_controller.cairo index 676128220..fae799412 100644 --- a/contracts/src/access_control/access_controller.cairo +++ b/contracts/src/access_control/access_controller.cairo @@ -1,70 +1,44 @@ -use starknet::ContractAddress; - #[starknet::contract] mod AccessController { use starknet::ContractAddress; use starknet::class_hash::ClassHash; - use chainlink::libraries::access_control::{AccessControl, IAccessController}; + use chainlink::libraries::access_control::{AccessControlComponent, IAccessController}; use chainlink::libraries::ownable::{OwnableComponent, IOwnable}; use chainlink::libraries::upgradeable::{Upgradeable, IUpgradeable}; component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); + component!(path: AccessControlComponent, storage: access_control, event: AccessControlEvent); #[abi(embed_v0)] impl OwnableImpl = OwnableComponent::OwnableImpl; impl InternalImpl = OwnableComponent::InternalImpl; + #[abi(embed_v0)] + impl AccessControlImpl = AccessControlComponent::AccessControlImpl; + impl AccessControlInternalImpl = AccessControlComponent::InternalImpl; + #[event] #[derive(Drop, starknet::Event)] enum Event { #[flat] OwnableEvent: OwnableComponent::Event, + #[flat] + AccessControlEvent: AccessControlComponent::Event, } #[storage] struct Storage { #[substorage(v0)] ownable: OwnableComponent::Storage, + #[substorage(v0)] + access_control: AccessControlComponent::Storage, } #[constructor] fn constructor(ref self: ContractState, owner_address: ContractAddress) { self.ownable.initializer(owner_address); - let mut access_control = AccessControl::unsafe_new_contract_state(); - AccessControl::constructor(ref access_control); - } - - #[external(v0)] - impl AccessControllerImpl of IAccessController { - fn has_access(self: @ContractState, user: ContractAddress, data: Array) -> bool { - let state = AccessControl::unsafe_new_contract_state(); - AccessControl::has_access(@state, user, data) - } - - fn add_access(ref self: ContractState, user: ContractAddress) { - self.ownable.assert_only_owner(); - let mut state = AccessControl::unsafe_new_contract_state(); - AccessControl::add_access(ref state, user); - } - - fn remove_access(ref self: ContractState, user: ContractAddress) { - self.ownable.assert_only_owner(); - let mut state = AccessControl::unsafe_new_contract_state(); - AccessControl::remove_access(ref state, user); - } - - fn enable_access_check(ref self: ContractState) { - self.ownable.assert_only_owner(); - let mut state = AccessControl::unsafe_new_contract_state(); - AccessControl::enable_access_check(ref state); - } - - fn disable_access_check(ref self: ContractState) { - self.ownable.assert_only_owner(); - let mut state = AccessControl::unsafe_new_contract_state(); - AccessControl::disable_access_check(ref state); - } + self.access_control.initializer(); } /// diff --git a/contracts/src/emergency/sequencer_uptime_feed.cairo b/contracts/src/emergency/sequencer_uptime_feed.cairo index c8f8e9936..9229d2e96 100644 --- a/contracts/src/emergency/sequencer_uptime_feed.cairo +++ b/contracts/src/emergency/sequencer_uptime_feed.cairo @@ -28,23 +28,30 @@ mod SequencerUptimeFeed { use zeroable::Zeroable; use chainlink::libraries::ownable::{OwnableComponent, IOwnable}; - use chainlink::libraries::access_control::{AccessControl, IAccessController}; + use chainlink::libraries::access_control::{AccessControlComponent, IAccessController}; + use chainlink::libraries::access_control::AccessControlComponent::InternalTrait as AccessControlInternalTrait; use chainlink::ocr2::aggregator::Round; use chainlink::ocr2::aggregator::IAggregator; use chainlink::ocr2::aggregator::{Transmission}; use chainlink::libraries::upgradeable::Upgradeable; component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); + component!(path: AccessControlComponent, storage: access_control, event: AccessControlEvent); #[abi(embed_v0)] impl OwnableImpl = OwnableComponent::OwnableImpl; - impl InternalImpl = OwnableComponent::InternalImpl; + impl OwnableInternalImpl = OwnableComponent::InternalImpl; + #[abi(embed_v0)] + impl AccessControlImpl = AccessControlComponent::AccessControlImpl; + impl AccessControlInternalImpl = AccessControlComponent::InternalImpl; #[storage] struct Storage { #[substorage(v0)] ownable: OwnableComponent::Storage, + #[substorage(v0)] + access_control: AccessControlComponent::Storage, // l1 sender is an starknet validator ethereum address _l1_sender: felt252, @@ -58,6 +65,8 @@ mod SequencerUptimeFeed { enum Event { #[flat] OwnableEvent: OwnableComponent::Event, + #[flat] + AccessControlEvent: AccessControlComponent::Event, RoundUpdated: RoundUpdated, NewRound: NewRound, AnswerUpdated: AnswerUpdated, @@ -213,43 +222,6 @@ mod SequencerUptimeFeed { Upgradeable::upgrade(new_impl) } - /// - /// Access Control - /// - - #[external(v0)] - impl AccessControllerImpl of IAccessController { - fn has_access(self: @ContractState, user: ContractAddress, data: Array) -> bool { - let state = AccessControl::unsafe_new_contract_state(); - AccessControl::has_access(@state, user, data) - } - - fn add_access(ref self: ContractState, user: ContractAddress) { - self.ownable.assert_only_owner(); - let mut state = AccessControl::unsafe_new_contract_state(); - AccessControl::add_access(ref state, user) - } - - fn remove_access(ref self: ContractState, user: ContractAddress) { - self.ownable.assert_only_owner(); - let mut state = AccessControl::unsafe_new_contract_state(); - AccessControl::remove_access(ref state, user) - } - - fn enable_access_check(ref self: ContractState) { - self.ownable.assert_only_owner(); - let mut state = AccessControl::unsafe_new_contract_state(); - AccessControl::enable_access_check(ref state) - } - - fn disable_access_check(ref self: ContractState) { - self.ownable.assert_only_owner(); - let mut state = AccessControl::unsafe_new_contract_state(); - AccessControl::disable_access_check(ref state) - } - } - - /// /// Internals /// @@ -258,16 +230,14 @@ mod SequencerUptimeFeed { impl Internals of InternalTrait { fn _require_read_access(self: @ContractState) { let sender = starknet::info::get_caller_address(); - let access_control = AccessControl::unsafe_new_contract_state(); - AccessControl::check_read_access(@access_control, sender); + self.access_control.check_read_access(sender); } fn _initializer( ref self: ContractState, initial_status: u128, owner_address: ContractAddress ) { self.ownable.initializer(owner_address); - let mut access_control = AccessControl::unsafe_new_contract_state(); - AccessControl::constructor(ref access_control); + self.access_control.initializer(); let round_id = 1_u128; let timestamp = starknet::info::get_block_timestamp(); self._record_round(round_id, initial_status, timestamp); diff --git a/contracts/src/libraries/access_control.cairo b/contracts/src/libraries/access_control.cairo index d3454d011..e8fc3ab16 100644 --- a/contracts/src/libraries/access_control.cairo +++ b/contracts/src/libraries/access_control.cairo @@ -2,18 +2,23 @@ use starknet::ContractAddress; #[starknet::interface] trait IAccessController { fn has_access(self: @TContractState, user: ContractAddress, data: Array) -> bool; + fn has_read_access(self: @TContractState, user: ContractAddress, data: Array) -> bool; fn add_access(ref self: TContractState, user: ContractAddress); fn remove_access(ref self: TContractState, user: ContractAddress); fn enable_access_check(ref self: TContractState); fn disable_access_check(ref self: TContractState); } -#[starknet::contract] -mod AccessControl { +// Requires Ownable subcomponent. +#[starknet::component] +mod AccessControlComponent { use starknet::ContractAddress; use starknet::class_hash::ClassHash; use zeroable::Zeroable; + use chainlink::libraries::ownable::{OwnableComponent}; + use OwnableComponent::InternalImpl as OwnableInternalImpl; + #[storage] struct Storage { _check_enabled: bool, @@ -45,83 +50,93 @@ mod AccessControl { #[derive(Drop, starknet::Event)] struct AccessControlDisabled {} - fn has_access(self: @ContractState, user: ContractAddress, data: Array) -> bool { - let has_access = self._access_list.read(user); - if has_access { - return true; + #[embeddable_as(AccessControlImpl)] + impl AccessControl< + TContractState, +HasComponent, impl Ownable: OwnableComponent::HasComponent, +Drop, + > of super::IAccessController> { + fn has_access(self: @ComponentState, user: ContractAddress, data: Array) -> bool { + let has_access = self._access_list.read(user); + if has_access { + return true; + } + + let check_enabled = self._check_enabled.read(); + if !check_enabled { + return true; + } + + false } - let check_enabled = self._check_enabled.read(); - if !check_enabled { - return true; - } + fn has_read_access(self: @ComponentState, user: ContractAddress, data: Array) -> bool { + let _has_access = self.has_access(user, data); + if _has_access { + return true; + } - false - } + // NOTICE: read access is granted to direct calls, to enable off-chain reads. + if user.is_zero() { + return true; + } - fn has_read_access(self: @ContractState, user: ContractAddress, data: Array) -> bool { - let _has_access = has_access(self, user, data); - if _has_access { - return true; + false } - // NOTICE: read access is granted to direct calls, to enable off-chain reads. - if user.is_zero() { - return true; + fn add_access(ref self: ComponentState, user: ContractAddress) { + get_dep_component!(self, Ownable).assert_only_owner(); + let has_access = self._access_list.read(user); + if !has_access { + self._access_list.write(user, true); + self.emit(Event::AddedAccess(AddedAccess { user: user })); + } } - false - } - - fn check_access(self: @ContractState, user: ContractAddress) { - let allowed = has_access(self, user, ArrayTrait::new()); - assert(allowed, 'user does not have access'); - } - - fn check_read_access(self: @ContractState, user: ContractAddress) { - let allowed = has_read_access(self, user, ArrayTrait::new()); - assert(allowed, 'user does not have read access'); - } - - // - // Unprotected - // - - #[constructor] - fn constructor(ref self: ContractState) { - self._check_enabled.write(true); - self.emit(Event::AccessControlEnabled(AccessControlEnabled {})); - } + fn remove_access(ref self: ComponentState, user: ContractAddress) { + get_dep_component!(self, Ownable).assert_only_owner(); + let has_access = self._access_list.read(user); + if has_access { + self._access_list.write(user, false); + self.emit(Event::RemovedAccess(RemovedAccess { user: user })); + } + } - fn add_access(ref self: ContractState, user: ContractAddress) { - let has_access = self._access_list.read(user); - if !has_access { - self._access_list.write(user, true); - self.emit(Event::AddedAccess(AddedAccess { user: user })); + fn enable_access_check(ref self: ComponentState) { + get_dep_component!(self, Ownable).assert_only_owner(); + let check_enabled = self._check_enabled.read(); + if !check_enabled { + self._check_enabled.write(true); + self.emit(Event::AccessControlEnabled(AccessControlEnabled {})); + } } - } - fn remove_access(ref self: ContractState, user: ContractAddress) { - let has_access = self._access_list.read(user); - if has_access { - self._access_list.write(user, false); - self.emit(Event::RemovedAccess(RemovedAccess { user: user })); + fn disable_access_check(ref self: ComponentState) { + get_dep_component!(self, Ownable).assert_only_owner(); + let check_enabled = self._check_enabled.read(); + if check_enabled { + self._check_enabled.write(false); + self.emit(Event::AccessControlDisabled(AccessControlDisabled {})); + } } } - fn enable_access_check(ref self: ContractState) { - let check_enabled = self._check_enabled.read(); - if !check_enabled { + #[generate_trait] + impl InternalImpl< + TContractState, +HasComponent, impl Ownable: OwnableComponent::HasComponent, +Drop, + > of InternalTrait { + fn initializer(ref self: ComponentState) { self._check_enabled.write(true); self.emit(Event::AccessControlEnabled(AccessControlEnabled {})); } - } - fn disable_access_check(ref self: ContractState) { - let check_enabled = self._check_enabled.read(); - if check_enabled { - self._check_enabled.write(false); - self.emit(Event::AccessControlDisabled(AccessControlDisabled {})); + fn check_access(self: @ComponentState, user: ContractAddress) { + let allowed = AccessControl::has_access(self, user, ArrayTrait::new()); + assert(allowed, 'user does not have access'); + } + + fn check_read_access(self: @ComponentState, user: ContractAddress) { + let allowed = AccessControl::has_read_access(self, user, ArrayTrait::new()); + assert(allowed, 'user does not have read access'); } } + } diff --git a/contracts/src/ocr2/aggregator.cairo b/contracts/src/ocr2/aggregator.cairo index 542dfbd38..9705af51c 100644 --- a/contracts/src/ocr2/aggregator.cairo +++ b/contracts/src/ocr2/aggregator.cairo @@ -196,20 +196,24 @@ mod Aggregator { use chainlink::utils::split_felt; use chainlink::libraries::ownable::{OwnableComponent, IOwnable}; - use chainlink::libraries::access_control::AccessControl; + use chainlink::libraries::access_control::{AccessControlComponent, IAccessController}; + use chainlink::libraries::access_control::AccessControlComponent::InternalTrait as AccessControlInternalTrait; use chainlink::libraries::upgradeable::{Upgradeable, IUpgradeable}; use openzeppelin::token::erc20::interface::{IERC20, IERC20Dispatcher, IERC20DispatcherTrait}; - use chainlink::libraries::access_control::{ - IAccessController, IAccessControllerDispatcher, IAccessControllerDispatcherTrait - }; + use chainlink::libraries::access_control::{ IAccessControllerDispatcher, IAccessControllerDispatcherTrait }; component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); + component!(path: AccessControlComponent, storage: access_control, event: AccessControlEvent); #[abi(embed_v0)] impl OwnableImpl = OwnableComponent::OwnableImpl; - impl InternalImpl = OwnableComponent::InternalImpl; + impl OwnableInternalImpl = OwnableComponent::InternalImpl; + + #[abi(embed_v0)] + impl AccessControlImpl = AccessControlComponent::AccessControlImpl; + impl AccessControlInternalImpl = AccessControlComponent::InternalImpl; const GIGA: u128 = 1000000000_u128; @@ -220,6 +224,8 @@ mod Aggregator { enum Event { #[flat] OwnableEvent: OwnableComponent::Event, + #[flat] + AccessControlEvent: AccessControlComponent::Event, NewTransmission: NewTransmission, ConfigSet: ConfigSet, LinkTokenSet: LinkTokenSet, @@ -276,6 +282,8 @@ mod Aggregator { struct Storage { #[substorage(v0)] ownable: OwnableComponent::Storage, + #[substorage(v0)] + access_control: AccessControlComponent::Storage, /// Maximum number of faulty oracles _f: u8, @@ -306,16 +314,18 @@ mod Aggregator { _proposed_payees: LegacyMap // } - fn _require_read_access() { - let caller = starknet::info::get_caller_address(); - let state = AccessControl::unsafe_new_contract_state(); - AccessControl::check_read_access(@state, caller); + #[generate_trait] + impl AccessHelperImpl of AccessHelperTrait { + fn _require_read_access(self: @ContractState) { + let caller = starknet::info::get_caller_address(); + self.access_control.check_read_access(caller); + } } #[external(v0)] impl AggregatorImpl of super::IAggregator { fn latest_round_data(self: @ContractState) -> Round { - _require_read_access(); + self._require_read_access(); let latest_round_id = self._latest_aggregator_round_id.read(); let transmission = self._transmissions.read(latest_round_id); Round { @@ -328,7 +338,7 @@ mod Aggregator { } fn round_data(self: @ContractState, round_id: u128) -> Round { - _require_read_access(); + self._require_read_access(); let transmission = self._transmissions.read(round_id); Round { round_id: round_id.into(), @@ -340,12 +350,12 @@ mod Aggregator { } fn description(self: @ContractState) -> felt252 { - _require_read_access(); + self._require_read_access(); self._description.read() } fn decimals(self: @ContractState) -> u8 { - _require_read_access(); + self._require_read_access(); self._decimals.read() } @@ -368,8 +378,7 @@ mod Aggregator { description: felt252 ) { self.ownable.initializer(owner); - let mut access_control = AccessControl::unsafe_new_contract_state(); - AccessControl::constructor(ref access_control); + self.access_control.initializer(); self._link_token.write(link); self._billing_access_controller.write(billing_access_controller); @@ -391,40 +400,6 @@ mod Aggregator { } } - // -- Access Control -- - - #[external(v0)] - impl AccessControllerImpl of IAccessController { - fn has_access(self: @ContractState, user: ContractAddress, data: Array) -> bool { - let state = AccessControl::unsafe_new_contract_state(); - AccessControl::has_access(@state, user, data) - } - - fn add_access(ref self: ContractState, user: ContractAddress) { - self.ownable.assert_only_owner(); - let mut state = AccessControl::unsafe_new_contract_state(); - AccessControl::add_access(ref state, user) - } - - fn remove_access(ref self: ContractState, user: ContractAddress) { - self.ownable.assert_only_owner(); - let mut state = AccessControl::unsafe_new_contract_state(); - AccessControl::remove_access(ref state, user) - } - - fn enable_access_check(ref self: ContractState) { - self.ownable.assert_only_owner(); - let mut state = AccessControl::unsafe_new_contract_state(); - AccessControl::enable_access_check(ref state) - } - - fn disable_access_check(ref self: ContractState) { - self.ownable.assert_only_owner(); - let mut state = AccessControl::unsafe_new_contract_state(); - AccessControl::disable_access_check(ref state) - } - } - // --- Validation --- // NOTE: Currently unimplemented: diff --git a/contracts/src/ocr2/aggregator_proxy.cairo b/contracts/src/ocr2/aggregator_proxy.cairo index e26332d90..b39f55fae 100644 --- a/contracts/src/ocr2/aggregator_proxy.cairo +++ b/contracts/src/ocr2/aggregator_proxy.cairo @@ -50,7 +50,8 @@ mod AggregatorProxy { use chainlink::ocr2::aggregator::IAggregator; use chainlink::ocr2::aggregator::Round; use chainlink::libraries::ownable::{OwnableComponent, IOwnable}; - use chainlink::libraries::access_control::{AccessControl, IAccessController}; + use chainlink::libraries::access_control::{AccessControlComponent, IAccessController}; + use chainlink::libraries::access_control::AccessControlComponent::InternalTrait as AccessControlInternalTrait; use chainlink::utils::split_felt; use chainlink::libraries::upgradeable::{Upgradeable, IUpgradeable}; @@ -64,15 +65,22 @@ mod AggregatorProxy { } component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); + component!(path: AccessControlComponent, storage: access_control, event: AccessControlEvent); #[abi(embed_v0)] impl OwnableImpl = OwnableComponent::OwnableImpl; - impl InternalImpl = OwnableComponent::InternalImpl; + impl OwnableInternalImpl = OwnableComponent::InternalImpl; + + #[abi(embed_v0)] + impl AccessControlImpl = AccessControlComponent::AccessControlImpl; + impl AccessControlInternalImpl = AccessControlComponent::InternalImpl; #[storage] struct Storage { #[substorage(v0)] ownable: OwnableComponent::Storage, + #[substorage(v0)] + access_control: AccessControlComponent::Storage, _current_phase: Phase, _proposed_aggregator: ContractAddress, @@ -84,6 +92,8 @@ mod AggregatorProxy { enum Event { #[flat] OwnableEvent: OwnableComponent::Event, + #[flat] + AccessControlEvent: AccessControlComponent::Event, } // TODO: refactor these events @@ -148,10 +158,8 @@ mod AggregatorProxy { #[constructor] fn constructor(ref self: ContractState, owner: ContractAddress, address: ContractAddress) { - // TODO: ownable and access control need to share owners and update when needed self.ownable.initializer(owner); - let mut access_control = AccessControl::unsafe_new_contract_state(); - AccessControl::constructor(ref access_control); + self.access_control.initializer(); self._set_aggregator(address); } @@ -165,41 +173,6 @@ mod AggregatorProxy { } } - - // -- Access Control -- - - #[external(v0)] - impl AccessControllerImpl of IAccessController { - fn has_access(self: @ContractState, user: ContractAddress, data: Array) -> bool { - let state = AccessControl::unsafe_new_contract_state(); - AccessControl::has_access(@state, user, data) - } - - fn add_access(ref self: ContractState, user: ContractAddress) { - self.ownable.assert_only_owner(); - let mut state = AccessControl::unsafe_new_contract_state(); - AccessControl::add_access(ref state, user) - } - - fn remove_access(ref self: ContractState, user: ContractAddress) { - self.ownable.assert_only_owner(); - let mut state = AccessControl::unsafe_new_contract_state(); - AccessControl::remove_access(ref state, user) - } - - fn enable_access_check(ref self: ContractState) { - self.ownable.assert_only_owner(); - let mut state = AccessControl::unsafe_new_contract_state(); - AccessControl::enable_access_check(ref state) - } - - fn disable_access_check(ref self: ContractState) { - self.ownable.assert_only_owner(); - let mut state = AccessControl::unsafe_new_contract_state(); - AccessControl::disable_access_check(ref state) - } - } - // #[external(v0)] @@ -268,8 +241,7 @@ mod AggregatorProxy { fn _require_read_access(self: @ContractState) { let caller = starknet::info::get_caller_address(); - let state = AccessControl::unsafe_new_contract_state(); - AccessControl::check_read_access(@state, caller); + self.access_control.check_read_access(caller); } } } diff --git a/contracts/src/tests/test_aggregator_proxy.cairo b/contracts/src/tests/test_aggregator_proxy.cairo index 79d3457bb..b287db3d8 100644 --- a/contracts/src/tests/test_aggregator_proxy.cairo +++ b/contracts/src/tests/test_aggregator_proxy.cairo @@ -17,8 +17,9 @@ use chainlink::ocr2::mocks::mock_aggregator::{ // use chainlink::ocr2::aggregator::{IAggregator, IAggregatorDispatcher, IAggregatorDispatcherTrait}; use chainlink::ocr2::aggregator_proxy::AggregatorProxy; use chainlink::ocr2::aggregator_proxy::AggregatorProxy::{ - AggregatorProxyImpl, AggregatorProxyInternal, AccessControllerImpl, UpgradeableImpl + AggregatorProxyImpl, AggregatorProxyInternal, UpgradeableImpl }; +use AggregatorProxy::AccessControlImpl; use chainlink::ocr2::aggregator::Round; use chainlink::utils::split_felt; use chainlink::tests::test_ownable::should_implement_ownable; @@ -103,7 +104,7 @@ fn test_query_latest_round_data() { let mut state = STATE(); // init aggregator proxy with mock aggregator AggregatorProxy::constructor(ref state, owner, mockAggregatorAddr); - AccessControllerImpl::add_access(ref state, owner); + AccessControlImpl::add_access(ref state, owner); // insert round into mock aggregator mockAggregator.set_latest_round_data(10, 1, 9, 8); // query latest round @@ -125,7 +126,7 @@ fn test_query_latest_round_data_without_access() { let mut state = STATE(); // init aggregator proxy with mock aggregator AggregatorProxy::constructor(ref state, owner, mockAggregatorAddr); - AccessControllerImpl::add_access(ref state, owner); + AccessControlImpl::add_access(ref state, owner); // insert round into mock aggregator mockAggregator.set_latest_round_data(10, 1, 9, 8); // set caller to non-owner address with no read access @@ -142,7 +143,7 @@ fn test_propose_new_aggregator() { let mut state = STATE(); // init aggregator proxy with mock aggregator 1 AggregatorProxy::constructor(ref state, owner, mockAggregatorAddr1); - AccessControllerImpl::add_access(ref state, owner); + AccessControlImpl::add_access(ref state, owner); // insert rounds into mock aggregators mockAggregator1.set_latest_round_data(10, 1, 9, 8); mockAggregator2.set_latest_round_data(12, 2, 10, 11); @@ -171,7 +172,7 @@ fn test_confirm_new_aggregator() { let mut state = STATE(); // init aggregator proxy with mock aggregator 1 AggregatorProxy::constructor(ref state, owner, mockAggregatorAddr1); - AccessControllerImpl::add_access(ref state, owner); + AccessControlImpl::add_access(ref state, owner); // insert rounds into mock aggregators mockAggregator1.set_latest_round_data(10, 1, 9, 8); mockAggregator2.set_latest_round_data(12, 2, 10, 11); diff --git a/contracts/src/token/link_token.cairo b/contracts/src/token/link_token.cairo index 8b12f1eb1..a7e589297 100644 --- a/contracts/src/token/link_token.cairo +++ b/contracts/src/token/link_token.cairo @@ -103,19 +103,6 @@ mod LinkToken { 'LinkToken 1.0.0' } - // - // ERC677 - // - - // TODO: - // #[external(v0)] - // fn transfer_and_call( - // ref self: ContractState, to: ContractAddress, value: u256, data: Array - // ) -> bool { - // let mut erc677 = ERC677::unsafe_new_contract_state(); - // ERC677::transfer_and_call(ref erc677, to, value, data) - // } - // // Upgradeable //