From 08fdb4ade5bf4751ddba8918776ed85db022c019 Mon Sep 17 00:00:00 2001 From: Augustus Chang Date: Fri, 23 Feb 2024 15:46:30 -0500 Subject: [PATCH] update contracts and tests for cairo 2.5.x --- .tool-versions | 2 +- contracts/Scarb.lock | 4 +- contracts/Scarb.toml | 2 +- .../access_control/access_controller.cairo | 10 +- contracts/src/account.cairo | 258 +++++++++-- .../src/emergency/sequencer_uptime_feed.cairo | 12 +- contracts/src/libraries.cairo | 1 + contracts/src/libraries/access_control.cairo | 12 +- .../mocks/mock_non_upgradeable.cairo | 2 +- .../libraries/mocks/mock_upgradeable.cairo | 2 +- contracts/src/libraries/zownable.cairo | 0 contracts/src/multisig.cairo | 4 +- contracts/src/ocr2/aggregator.cairo | 407 +++++++++--------- contracts/src/ocr2/aggregator_proxy.cairo | 13 +- .../src/ocr2/mocks/mock_aggregator.cairo | 4 +- .../src/tests/test_access_controller.cairo | 4 +- contracts/src/tests/test_aggregator.cairo | 19 +- .../src/tests/test_aggregator_proxy.cairo | 14 +- contracts/src/tests/test_erc677.cairo | 7 +- contracts/src/tests/test_link_token.cairo | 8 - .../src/tests/test_mock_aggregator.cairo | 4 +- contracts/src/tests/test_multisig.cairo | 63 +-- contracts/src/tests/test_ownable.cairo | 39 +- .../tests/test_sequencer_uptime_feed.cairo | 6 - contracts/src/tests/test_upgradeable.cairo | 6 +- contracts/src/token/link_token.cairo | 19 +- .../token/mock/invalid_erc667_receiver.cairo | 21 +- .../token/mock/valid_erc667_receiver.cairo | 5 +- 28 files changed, 517 insertions(+), 431 deletions(-) create mode 100644 contracts/src/libraries/zownable.cairo diff --git a/.tool-versions b/.tool-versions index e5901068b..b70529c0c 100644 --- a/.tool-versions +++ b/.tool-versions @@ -9,7 +9,7 @@ mockery 2.22.1 golangci-lint 1.55.0 actionlint 1.6.12 shellcheck 0.8.0 -scarb 0.7.0 +scarb 2.5.4 # Kubernetes k3d 5.4.4 diff --git a/contracts/Scarb.lock b/contracts/Scarb.lock index cc0bca5aa..2ba9d1f72 100644 --- a/contracts/Scarb.lock +++ b/contracts/Scarb.lock @@ -10,5 +10,5 @@ dependencies = [ [[package]] name = "openzeppelin" -version = "0.8.0" -source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.8.0#c23e8e96de60e6e3159b1ff8591a1187269c0eb7" +version = "0.9.0" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.9.0#861fc416f87addbe23a3b47f9d19ab27c10d5dc8" diff --git a/contracts/Scarb.toml b/contracts/Scarb.toml index a5fcf354b..aabbdbf47 100644 --- a/contracts/Scarb.toml +++ b/contracts/Scarb.toml @@ -12,7 +12,7 @@ sierra = "cairo-compile . -r" # Note: currently testing doesn't work with dependencies [dependencies] starknet = ">=1.3.0" -openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "v0.8.0" } +openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "v0.9.0" } [lib] diff --git a/contracts/src/access_control/access_controller.cairo b/contracts/src/access_control/access_controller.cairo index 4bf5c7f3c..bbd8cac8b 100644 --- a/contracts/src/access_control/access_controller.cairo +++ b/contracts/src/access_control/access_controller.cairo @@ -3,8 +3,10 @@ mod AccessController { use starknet::ContractAddress; use starknet::class_hash::ClassHash; + use openzeppelin::access::ownable::ownable::OwnableComponent; + use chainlink::libraries::access_control::{AccessControlComponent, IAccessController}; - use chainlink::libraries::ownable::{OwnableComponent, IOwnable}; + // use chainlink::libraries::ownable::{OwnableComponent, IOwnable}; use chainlink::libraries::type_and_version::ITypeAndVersion; use chainlink::libraries::upgradeable::{Upgradeable, IUpgradeable}; @@ -12,8 +14,8 @@ mod AccessController { component!(path: AccessControlComponent, storage: access_control, event: AccessControlEvent); #[abi(embed_v0)] - impl OwnableImpl = OwnableComponent::OwnableImpl; - impl InternalImpl = OwnableComponent::InternalImpl; + impl OwnableImpl = OwnableComponent::OwnableTwoStepImpl; + impl OwnableInternalImpl = OwnableComponent::InternalImpl; #[abi(embed_v0)] impl AccessControlImpl = @@ -50,7 +52,7 @@ mod AccessController { } } - #[external(v0)] + #[abi(embed_v0)] impl UpgradeableImpl of IUpgradeable { fn upgrade(ref self: ContractState, new_impl: ClassHash) { self.ownable.assert_only_owner(); diff --git a/contracts/src/account.cairo b/contracts/src/account.cairo index f200c7dd4..dccd4fc34 100644 --- a/contracts/src/account.cairo +++ b/contracts/src/account.cairo @@ -1,57 +1,233 @@ -// copied from https://github.com/OpenZeppelin/cairo-contracts/blob/v0.8.1/src/presets/account.cairo +// copied from https://github.com/OpenZeppelin/cairo-contracts/blob/release-v0.9.0/src/access/ownable/ownable.cairo // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.8.1 (presets/account.cairo) +// OpenZeppelin Contracts for Cairo v0.9.0 (access/ownable/ownable.cairo) -/// # Account Preset +/// # Ownable Component /// -/// OpenZeppelin's basic account which can change its public key and declare, deploy, or call contracts. -#[starknet::contract(account)] -mod Account { - use openzeppelin::account::AccountComponent; - use openzeppelin::introspection::src5::SRC5Component; - - component!(path: AccountComponent, storage: account, event: AccountEvent); - component!(path: SRC5Component, storage: src5, event: SRC5Event); - - // Account - #[abi(embed_v0)] - impl SRC6Impl = AccountComponent::SRC6Impl; - #[abi(embed_v0)] - impl SRC6CamelOnlyImpl = AccountComponent::SRC6CamelOnlyImpl; - #[abi(embed_v0)] - impl PublicKeyImpl = AccountComponent::PublicKeyImpl; - #[abi(embed_v0)] - impl PublicKeyCamelImpl = AccountComponent::PublicKeyCamelImpl; - #[abi(embed_v0)] - impl DeclarerImpl = AccountComponent::DeclarerImpl; - #[abi(embed_v0)] - impl DeployableImpl = AccountComponent::DeployableImpl; - impl AccountInternalImpl = AccountComponent::InternalImpl; - - // SRC5 - #[abi(embed_v0)] - impl SRC5Impl = SRC5Component::SRC5Impl; +/// The Ownable component provides a basic access control mechanism, where +/// there is an account (an owner) that can be granted exclusive access to +/// specific functions. +/// +/// The initial owner can be set by using the `initializer` function in +/// construction time. This can later be changed with `transfer_ownership`. +/// +/// The component also offers functionality for a two-step ownership +/// transfer where the new owner first has to accept their ownership to +/// finalize the transfer. +#[starknet::component] +mod OwnableComponent { + use openzeppelin::access::ownable::interface; + use starknet::ContractAddress; + use starknet::get_caller_address; #[storage] struct Storage { - #[substorage(v0)] - account: AccountComponent::Storage, - #[substorage(v0)] - src5: SRC5Component::Storage + Ownable_owner: ContractAddress, + Ownable_pending_owner: ContractAddress } #[event] #[derive(Drop, starknet::Event)] enum Event { - #[flat] - AccountEvent: AccountComponent::Event, - #[flat] - SRC5Event: SRC5Component::Event + OwnershipTransferred: OwnershipTransferred, + OwnershipTransferStarted: OwnershipTransferStarted + } + + #[derive(Drop, starknet::Event)] + struct OwnershipTransferred { + #[key] + previous_owner: ContractAddress, + #[key] + new_owner: ContractAddress, + } + + #[derive(Drop, starknet::Event)] + struct OwnershipTransferStarted { + #[key] + previous_owner: ContractAddress, + #[key] + new_owner: ContractAddress, + } + + mod Errors { + const NOT_OWNER: felt252 = 'Caller is not the owner'; + const NOT_PENDING_OWNER: felt252 = 'Caller is not the pending owner'; + const ZERO_ADDRESS_CALLER: felt252 = 'Caller is the zero address'; + const ZERO_ADDRESS_OWNER: felt252 = 'New owner is the zero address'; + } + + #[embeddable_as(OwnableImpl)] + impl Ownable< + TContractState, +HasComponent + > of interface::IOwnable> { + /// Returns the address of the current owner. + fn owner(self: @ComponentState) -> ContractAddress { + self.Ownable_owner.read() + } + + /// Transfers ownership of the contract to a new address. + /// + /// Requirements: + /// + /// - `new_owner` is not the zero address. + /// - The caller is the contract owner. + /// + /// Emits an `OwnershipTransferred` event. + fn transfer_ownership( + ref self: ComponentState, new_owner: ContractAddress + ) { + assert(!new_owner.is_zero(), Errors::ZERO_ADDRESS_OWNER); + self.assert_only_owner(); + self._transfer_ownership(new_owner); + } + + /// Leaves the contract without owner. It will not be possible to call `assert_only_owner` + /// functions anymore. Can only be called by the current owner. + /// + /// Requirements: + /// + /// - The caller is the contract owner. + /// + /// Emits an `OwnershipTransferred` event. + fn renounce_ownership(ref self: ComponentState) { + self.assert_only_owner(); + self._transfer_ownership(Zeroable::zero()); + } + } + + /// Adds support for two step ownership transfer. + #[embeddable_as(OwnableTwoStepImpl)] + impl OwnableTwoStep< + TContractState, +HasComponent + > of interface::IOwnableTwoStep> { + /// Returns the address of the current owner. + fn owner(self: @ComponentState) -> ContractAddress { + self.Ownable_owner.read() + } + + /// Returns the address of the pending owner. + fn pending_owner(self: @ComponentState) -> ContractAddress { + self.Ownable_pending_owner.read() + } + + /// Finishes the two-step ownership transfer process by accepting the ownership. + /// Can only be called by the pending owner. + fn accept_ownership(ref self: ComponentState) { + let caller = get_caller_address(); + let pending_owner = self.Ownable_pending_owner.read(); + assert(caller == pending_owner, Errors::NOT_PENDING_OWNER); + self._accept_ownership(); + } + + /// Starts the two-step ownership transfer process by setting the pending owner. + fn transfer_ownership( + ref self: ComponentState, new_owner: ContractAddress + ) { + self.assert_only_owner(); + self._propose_owner(new_owner); + } + + /// Leaves the contract without owner. It will not be possible to call `assert_only_owner` + /// functions anymore. Can only be called by the current owner. + fn renounce_ownership(ref self: ComponentState) { + Ownable::renounce_ownership(ref self); + } + } + + /// Adds camelCase support for `IOwnable`. + #[embeddable_as(OwnableCamelOnlyImpl)] + impl OwnableCamelOnly< + TContractState, +HasComponent + > of interface::IOwnableCamelOnly> { + fn transferOwnership(ref self: ComponentState, newOwner: ContractAddress) { + Ownable::transfer_ownership(ref self, newOwner); + } + + fn renounceOwnership(ref self: ComponentState) { + Ownable::renounce_ownership(ref self); + } + } + + /// Adds camelCase support for `IOwnableTwoStep`. + #[embeddable_as(OwnableTwoStepCamelOnlyImpl)] + impl OwnableTwoStepCamelOnly< + TContractState, +HasComponent + > of interface::IOwnableTwoStepCamelOnly> { + fn pendingOwner(self: @ComponentState) -> ContractAddress { + OwnableTwoStep::pending_owner(self) + } + + fn acceptOwnership(ref self: ComponentState) { + self.accept_ownership(); + } + + fn transferOwnership(ref self: ComponentState, newOwner: ContractAddress) { + OwnableTwoStep::transfer_ownership(ref self, newOwner); + } + + fn renounceOwnership(ref self: ComponentState) { + OwnableTwoStep::renounce_ownership(ref self); + } } - #[constructor] - fn constructor(ref self: ContractState, public_key: felt252) { - self.account.initializer(public_key); + #[generate_trait] + impl InternalImpl< + TContractState, +HasComponent + > of InternalTrait { + /// Sets the contract's initial owner. + /// + /// This function should be called at construction time. + fn initializer(ref self: ComponentState, owner: ContractAddress) { + self._transfer_ownership(owner); + } + + /// Panics if called by any account other than the owner. Use this + /// to restrict access to certain functions to the owner. + fn assert_only_owner(self: @ComponentState) { + let owner = self.Ownable_owner.read(); + let caller = get_caller_address(); + assert(!caller.is_zero(), Errors::ZERO_ADDRESS_CALLER); + assert(caller == owner, Errors::NOT_OWNER); + } + + /// Transfers ownership to the pending owner. + /// + /// Internal function without access restriction. + fn _accept_ownership(ref self: ComponentState) { + let pending_owner = self.Ownable_pending_owner.read(); + self.Ownable_pending_owner.write(Zeroable::zero()); + self._transfer_ownership(pending_owner); + } + + /// Sets a new pending owner. + /// + /// Internal function without access restriction. + fn _propose_owner(ref self: ComponentState, new_owner: ContractAddress) { + let previous_owner = self.Ownable_owner.read(); + self.Ownable_pending_owner.write(new_owner); + self + .emit( + OwnershipTransferStarted { + previous_owner: previous_owner, new_owner: new_owner + } + ); + } + + /// Transfers ownership of the contract to a new address. + /// + /// Internal function without access restriction. + /// + /// Emits an `OwnershipTransferred` event. + fn _transfer_ownership( + ref self: ComponentState, new_owner: ContractAddress + ) { + let previous_owner: ContractAddress = self.Ownable_owner.read(); + self.Ownable_owner.write(new_owner); + self + .emit( + OwnershipTransferred { previous_owner: previous_owner, new_owner: new_owner } + ); + } } } diff --git a/contracts/src/emergency/sequencer_uptime_feed.cairo b/contracts/src/emergency/sequencer_uptime_feed.cairo index 06d0a59c6..44125ef1e 100644 --- a/contracts/src/emergency/sequencer_uptime_feed.cairo +++ b/contracts/src/emergency/sequencer_uptime_feed.cairo @@ -27,7 +27,9 @@ mod SequencerUptimeFeed { use option::OptionTrait; use zeroable::Zeroable; - use chainlink::libraries::ownable::{OwnableComponent, IOwnable}; + use openzeppelin::access::ownable::ownable::OwnableComponent; + + // use chainlink::libraries::ownable::{OwnableComponent, IOwnable}; use chainlink::libraries::access_control::{AccessControlComponent, IAccessController}; use chainlink::libraries::access_control::AccessControlComponent::InternalTrait as AccessControlInternalTrait; use chainlink::libraries::type_and_version::ITypeAndVersion; @@ -40,7 +42,7 @@ mod SequencerUptimeFeed { component!(path: AccessControlComponent, storage: access_control, event: AccessControlEvent); #[abi(embed_v0)] - impl OwnableImpl = OwnableComponent::OwnableImpl; + impl OwnableImpl = OwnableComponent::OwnableTwoStepImpl; impl OwnableInternalImpl = OwnableComponent::InternalImpl; #[abi(embed_v0)] @@ -116,7 +118,7 @@ mod SequencerUptimeFeed { } } - #[external(v0)] + #[abi(embed_v0)] impl AggregatorImpl of IAggregator { fn latest_round_data(self: @ContractState) -> Round { self._require_read_access(); @@ -189,7 +191,7 @@ mod SequencerUptimeFeed { } } - #[external(v0)] + #[abi(embed_v0)] impl SequencerUptimeFeedImpl of super::ISequencerUptimeFeed { fn set_l1_sender(ref self: ContractState, address: EthAddress) { self.ownable.assert_only_owner(); @@ -220,7 +222,7 @@ mod SequencerUptimeFeed { /// Upgradeable /// - #[external(v0)] + #[abi(embed_v0)] fn upgrade(ref self: ContractState, new_impl: ClassHash) { self.ownable.assert_only_owner(); Upgradeable::upgrade(new_impl) diff --git a/contracts/src/libraries.cairo b/contracts/src/libraries.cairo index dcfe47c1b..68c4710e6 100644 --- a/contracts/src/libraries.cairo +++ b/contracts/src/libraries.cairo @@ -1,4 +1,5 @@ mod ownable; +mod zownable; mod access_control; mod token; mod upgradeable; diff --git a/contracts/src/libraries/access_control.cairo b/contracts/src/libraries/access_control.cairo index c5af14a78..85fc3082f 100644 --- a/contracts/src/libraries/access_control.cairo +++ b/contracts/src/libraries/access_control.cairo @@ -16,7 +16,9 @@ mod AccessControlComponent { use starknet::class_hash::ClassHash; use zeroable::Zeroable; - use chainlink::libraries::ownable::{OwnableComponent}; + use openzeppelin::access::ownable::ownable::OwnableComponent; + // use chainlink::libraries::ownable::{OwnableComponent}; + use OwnableComponent::InternalImpl as OwnableInternalImpl; #[storage] @@ -90,7 +92,7 @@ mod AccessControlComponent { } fn add_access(ref self: ComponentState, user: ContractAddress) { - get_dep_component!(self, Ownable).assert_only_owner(); + 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); @@ -99,7 +101,7 @@ mod AccessControlComponent { } fn remove_access(ref self: ComponentState, user: ContractAddress) { - get_dep_component!(self, Ownable).assert_only_owner(); + 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); @@ -108,7 +110,7 @@ mod AccessControlComponent { } fn enable_access_check(ref self: ComponentState) { - get_dep_component!(self, Ownable).assert_only_owner(); + get_dep_component!(@self, Ownable).assert_only_owner(); let check_enabled = self._check_enabled.read(); if !check_enabled { self._check_enabled.write(true); @@ -117,7 +119,7 @@ mod AccessControlComponent { } fn disable_access_check(ref self: ComponentState) { - get_dep_component!(self, Ownable).assert_only_owner(); + get_dep_component!(@self, Ownable).assert_only_owner(); let check_enabled = self._check_enabled.read(); if check_enabled { self._check_enabled.write(false); diff --git a/contracts/src/libraries/mocks/mock_non_upgradeable.cairo b/contracts/src/libraries/mocks/mock_non_upgradeable.cairo index 1dc6ac1fb..921943ea3 100644 --- a/contracts/src/libraries/mocks/mock_non_upgradeable.cairo +++ b/contracts/src/libraries/mocks/mock_non_upgradeable.cairo @@ -11,7 +11,7 @@ mod MockNonUpgradeable { #[constructor] fn constructor(ref self: ContractState) {} - #[external(v0)] + #[abi(embed_v0)] impl MockNonUpgradeableImpl of super::IMockNonUpgradeable { fn bar(self: @ContractState) -> bool { true diff --git a/contracts/src/libraries/mocks/mock_upgradeable.cairo b/contracts/src/libraries/mocks/mock_upgradeable.cairo index c46c4258c..b83fa7f84 100644 --- a/contracts/src/libraries/mocks/mock_upgradeable.cairo +++ b/contracts/src/libraries/mocks/mock_upgradeable.cairo @@ -18,7 +18,7 @@ mod MockUpgradeable { #[constructor] fn constructor(ref self: ContractState) {} - #[external(v0)] + #[abi(embed_v0)] impl MockUpgradeableImpl of super::IMockUpgradeable { fn foo(self: @ContractState) -> bool { true diff --git a/contracts/src/libraries/zownable.cairo b/contracts/src/libraries/zownable.cairo new file mode 100644 index 000000000..e69de29bb diff --git a/contracts/src/multisig.cairo b/contracts/src/multisig.cairo index 1728d3bde..5f78e4796 100644 --- a/contracts/src/multisig.cairo +++ b/contracts/src/multisig.cairo @@ -168,7 +168,7 @@ mod Multisig { } } - #[external(v0)] + #[abi(embed_v0)] impl UpgradeableImpl of IUpgradeable { fn upgrade(ref self: ContractState, new_impl: ClassHash) { self._require_multisig(); @@ -176,7 +176,7 @@ mod Multisig { } } - #[external(v0)] + #[abi(embed_v0)] impl MultisigImpl of super::IMultisig { /// Views diff --git a/contracts/src/ocr2/aggregator.cairo b/contracts/src/ocr2/aggregator.cairo index 873585924..f84687f73 100644 --- a/contracts/src/ocr2/aggregator.cairo +++ b/contracts/src/ocr2/aggregator.cairo @@ -107,6 +107,9 @@ trait Billing { fn link_available_for_payment( self: @TContractState ) -> (bool, u128); // (is negative, absolute difference) + fn set_link_token( + ref self: TContractState, link_token: ContractAddress, recipient: ContractAddress + ); } #[derive(Copy, Drop, Serde)] @@ -193,14 +196,15 @@ mod Aggregator { use starknet::storage_address_from_base_and_offset; use starknet::class_hash::ClassHash; + use openzeppelin::access::ownable::ownable::OwnableComponent; + use openzeppelin::token::erc20::interface::{IERC20, IERC20Dispatcher, IERC20DispatcherTrait}; + use chainlink::utils::split_felt; - use chainlink::libraries::ownable::{OwnableComponent, IOwnable}; + // use chainlink::libraries::ownable::{OwnableComponent, IOwnable}; 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::{ IAccessControllerDispatcher, IAccessControllerDispatcherTrait }; @@ -210,7 +214,7 @@ mod Aggregator { component!(path: AccessControlComponent, storage: access_control, event: AccessControlEvent); #[abi(embed_v0)] - impl OwnableImpl = OwnableComponent::OwnableImpl; + impl OwnableImpl = OwnableComponent::OwnableTwoStepImpl; impl OwnableInternalImpl = OwnableComponent::InternalImpl; #[abi(embed_v0)] @@ -333,7 +337,7 @@ mod Aggregator { } } - #[external(v0)] + #[abi(embed_v0)] impl AggregatorImpl of super::IAggregator { fn latest_round_data(self: @ContractState) -> Round { self._require_read_access(); @@ -399,7 +403,7 @@ mod Aggregator { // --- Upgradeable --- - #[external(v0)] + #[abi(embed_v0)] impl UpgradeableImpl of IUpgradeable { fn upgrade(ref self: ContractState, new_impl: ClassHash) { self.ownable.assert_only_owner(); @@ -475,7 +479,7 @@ mod Aggregator { masked } - #[external(v0)] + #[abi(embed_v0)] impl ConfigurationImpl of super::Configuration { fn set_config( ref self: ContractState, @@ -567,10 +571,7 @@ mod Aggregator { let mut index = 1; let mut len = self._oracles_len.read(); let mut result = ArrayTrait::new(); - loop { - if len == 0_usize { - break (); - } + while len > 0_usize { let transmitter = self._transmitters_list.read(index); result.append(transmitter); len -= 1; @@ -584,12 +585,7 @@ mod Aggregator { impl ConfigurationHelperImpl of ConfigurationHelperTrait { fn remove_oracles(ref self: ContractState) { let mut index = self._oracles_len.read(); - loop { - if index == 0_usize { - self._oracles_len.write(0_usize); - break (); - } - + while index > 0_usize { let signer = self._signers_list.read(index); self._signers.write(signer, 0_usize); @@ -599,7 +595,8 @@ mod Aggregator { .write(transmitter, Oracle { index: 0_usize, payment_juels: 0_u128 }); index -= 1; - } + }; + self._oracles_len.write(0_usize); } fn add_oracles( @@ -657,157 +654,159 @@ mod Aggregator { extra_hash: felt252, } - #[external(v0)] - fn transmit( - ref self: ContractState, - report_context: ReportContext, - observation_timestamp: u64, - observers: felt252, - observations: Array, - juels_per_fee_coin: u128, - gas_price: u128, - mut signatures: Array, - ) { - let signatures_len = signatures.len(); - - let epoch_and_round = self._latest_epoch_and_round.read(); - assert(epoch_and_round < report_context.epoch_and_round, 'stale report'); - - // validate transmitter - let caller = starknet::info::get_caller_address(); - let mut oracle = self._transmitters.read(caller); - assert(oracle.index != 0_usize, 'unknown sender'); // 0 index = uninitialized - - // Validate config digest matches latest_config_digest - let config_digest = self._latest_config_digest.read(); - assert(report_context.config_digest == config_digest, 'config digest mismatch'); - - let f = self._f.read(); - assert(signatures_len == (f + 1_u8).into(), 'wrong number of signatures'); - - let msg = hash_report( - @report_context, - observation_timestamp, - observers, - @observations, - juels_per_fee_coin, - gas_price - ); - - // Check all signatures are unique (we only saw each pubkey once) - // NOTE: This relies on protocol-level design constraints (MAX_ORACLES = 31, f = 10) which - // ensures we have enough bits to store a count for each oracle. Whenever the MAX_ORACLES - // is updated, the signed_count parameter should be reconsidered. - // - // Although 31 bits is enough, we use a u128 here for simplicity because BitAnd and BitOr - // operators are defined only for u128 and u256. - assert(MAX_ORACLES == 31_u32, ''); - self.verify_signatures(msg, ref signatures, 0_u128); - - // report(): - - let observations_len = observations.len(); - assert(observations_len <= MAX_ORACLES, ''); - assert(f.into() < observations_len, ''); - - self._latest_epoch_and_round.write(report_context.epoch_and_round); - - let median_idx = observations_len / 2_usize; - let median = *observations[median_idx]; - - // Validate median in min-max range - let min_answer = self._min_answer.read(); - let max_answer = self._max_answer.read(); - assert((min_answer <= median) & (median <= max_answer), 'median is out of min-max range'); - - let prev_round_id = self._latest_aggregator_round_id.read(); - let round_id = prev_round_id + 1_u128; - self._latest_aggregator_round_id.write(round_id); - - let block_info = starknet::info::get_block_info().unbox(); - - self - ._transmissions - .write( - round_id, - Transmission { - answer: median, - block_num: block_info.block_number, - observation_timestamp, - transmission_timestamp: block_info.block_timestamp, - } + #[abi(per_item)] + #[generate_trait] + impl TransmissionHelperImpl of TransmissionHelperTrait { + fn hash_report( + self: @ContractState, + report_context: @ReportContext, + observation_timestamp: u64, + observers: felt252, + observations: @Array, + juels_per_fee_coin: u128, + gas_price: u128 + ) -> felt252 { + let mut state = 0; + state = LegacyHash::hash(state, *report_context.config_digest); + state = LegacyHash::hash(state, *report_context.epoch_and_round); + state = LegacyHash::hash(state, *report_context.extra_hash); + state = LegacyHash::hash(state, observation_timestamp); + state = LegacyHash::hash(state, observers); + state = LegacyHash::hash(state, observations.len()); + state = LegacyHash::hash(state, observations.span()); + state = LegacyHash::hash(state, juels_per_fee_coin); + state = LegacyHash::hash(state, gas_price); + let len: usize = 5 + 1 + observations.len() + 2; + state = LegacyHash::hash(state, len); + state + } + + #[external(v0)] + fn latest_transmission_details(self: @ContractState) -> (felt252, u64, u128, u64) { + let config_digest = self._latest_config_digest.read(); + let latest_round_id = self._latest_aggregator_round_id.read(); + let epoch_and_round = self._latest_epoch_and_round.read(); + let transmission = self._transmissions.read(latest_round_id); + (config_digest, epoch_and_round, transmission.answer, transmission.transmission_timestamp) + } + + #[external(v0)] + fn transmit( + ref self: ContractState, + report_context: ReportContext, + observation_timestamp: u64, + observers: felt252, + observations: Array, + juels_per_fee_coin: u128, + gas_price: u128, + mut signatures: Array, + ) { + let signatures_len = signatures.len(); + + let epoch_and_round = self._latest_epoch_and_round.read(); + assert(epoch_and_round < report_context.epoch_and_round, 'stale report'); + + // validate transmitter + let caller = starknet::info::get_caller_address(); + let mut oracle = self._transmitters.read(caller); + assert(oracle.index != 0_usize, 'unknown sender'); // 0 index = uninitialized + + // Validate config digest matches latest_config_digest + let config_digest = self._latest_config_digest.read(); + assert(report_context.config_digest == config_digest, 'config digest mismatch'); + + let f = self._f.read(); + assert(signatures_len == (f + 1_u8).into(), 'wrong number of signatures'); + + let msg = self.hash_report( + @report_context, + observation_timestamp, + observers, + @observations, + juels_per_fee_coin, + gas_price ); - // NOTE: Usually validating via validator would happen here, currently disabled + // Check all signatures are unique (we only saw each pubkey once) + // NOTE: This relies on protocol-level design constraints (MAX_ORACLES = 31, f = 10) which + // ensures we have enough bits to store a count for each oracle. Whenever the MAX_ORACLES + // is updated, the signed_count parameter should be reconsidered. + // + // Although 31 bits is enough, we use a u128 here for simplicity because BitAnd and BitOr + // operators are defined only for u128 and u256. + assert(MAX_ORACLES == 31_u32, ''); + self.verify_signatures(msg, ref signatures, 0_u128); + + // report(): + + let observations_len = observations.len(); + assert(observations_len <= MAX_ORACLES, ''); + assert(f.into() < observations_len, ''); - let billing = self._billing.read(); - let reimbursement_juels = calculate_reimbursement( - juels_per_fee_coin, signatures_len, gas_price, billing - ); + self._latest_epoch_and_round.write(report_context.epoch_and_round); + + let median_idx = observations_len / 2_usize; + let median = *observations[median_idx]; + + // Validate median in min-max range + let min_answer = self._min_answer.read(); + let max_answer = self._max_answer.read(); + assert((min_answer <= median) & (median <= max_answer), 'median is out of min-max range'); - // end report() + let prev_round_id = self._latest_aggregator_round_id.read(); + let round_id = prev_round_id + 1_u128; + self._latest_aggregator_round_id.write(round_id); - self - .emit( - Event::NewTransmission( - NewTransmission { - round_id: round_id, + let block_info = starknet::info::get_block_info().unbox(); + + self + ._transmissions + .write( + round_id, + Transmission { answer: median, - transmitter: caller, - observation_timestamp: observation_timestamp, - observers: observers, - observations: observations, - juels_per_fee_coin: juels_per_fee_coin, - gas_price: gas_price, - config_digest: report_context.config_digest, - epoch_and_round: report_context.epoch_and_round, - reimbursement: reimbursement_juels, + block_num: block_info.block_number, + observation_timestamp, + transmission_timestamp: block_info.block_timestamp, } - ) + ); + + // NOTE: Usually validating via validator would happen here, currently disabled + + let billing = self._billing.read(); + let reimbursement_juels = calculate_reimbursement( + juels_per_fee_coin, signatures_len, gas_price, billing ); - // pay transmitter - let payment = reimbursement_juels + (billing.transmission_payment_gjuels.into() * GIGA); - // TODO: check overflow + // end report() - oracle.payment_juels += payment; - self._transmitters.write(caller, oracle); - } + self + .emit( + Event::NewTransmission( + NewTransmission { + round_id: round_id, + answer: median, + transmitter: caller, + observation_timestamp: observation_timestamp, + observers: observers, + observations: observations, + juels_per_fee_coin: juels_per_fee_coin, + gas_price: gas_price, + config_digest: report_context.config_digest, + epoch_and_round: report_context.epoch_and_round, + reimbursement: reimbursement_juels, + } + ) + ); - #[external(v0)] - fn latest_transmission_details(self: @ContractState) -> (felt252, u64, u128, u64) { - let config_digest = self._latest_config_digest.read(); - let latest_round_id = self._latest_aggregator_round_id.read(); - let epoch_and_round = self._latest_epoch_and_round.read(); - let transmission = self._transmissions.read(latest_round_id); - (config_digest, epoch_and_round, transmission.answer, transmission.transmission_timestamp) - } + // pay transmitter + let payment = reimbursement_juels + (billing.transmission_payment_gjuels.into() * GIGA); + // TODO: check overflow - fn hash_report( - report_context: @ReportContext, - observation_timestamp: u64, - observers: felt252, - observations: @Array, - juels_per_fee_coin: u128, - gas_price: u128 - ) -> felt252 { - let mut state = 0; - state = LegacyHash::hash(state, *report_context.config_digest); - state = LegacyHash::hash(state, *report_context.epoch_and_round); - state = LegacyHash::hash(state, *report_context.extra_hash); - state = LegacyHash::hash(state, observation_timestamp); - state = LegacyHash::hash(state, observers); - state = LegacyHash::hash(state, observations.len()); - state = LegacyHash::hash(state, observations.span()); - state = LegacyHash::hash(state, juels_per_fee_coin); - state = LegacyHash::hash(state, gas_price); - let len: usize = 5 + 1 + observations.len() + 2; - state = LegacyHash::hash(state, len); - state - } + oracle.payment_juels += payment; + self._transmitters.write(caller, oracle); + } - #[generate_trait] - impl TransmissionHelperImpl of TransmissionHelperTrait { fn verify_signatures( self: @ContractState, msg: felt252, @@ -841,51 +840,6 @@ mod Aggregator { } } - // --- RequestNewRound - - // --- Set LINK Token - - #[derive(Drop, starknet::Event)] - struct LinkTokenSet { - old_link_token: ContractAddress, - new_link_token: ContractAddress - } - - #[external(v0)] - fn set_link_token( - ref self: ContractState, link_token: ContractAddress, recipient: ContractAddress - ) { - self.ownable.assert_only_owner(); - - let old_token = self._link_token.read(); - - if link_token == old_token { - return (); - } - - let contract_address = starknet::info::get_contract_address(); - - // call balanceOf as a sanity check to confirm we're talking to a token - let token = IERC20Dispatcher { contract_address: link_token }; - token.balance_of(account: contract_address); - - self.pay_oracles(); - - // transfer remaining balance of old token to recipient - let old_token_dispatcher = IERC20Dispatcher { contract_address: old_token }; - let amount = old_token_dispatcher.balance_of(account: contract_address); - old_token_dispatcher.transfer(recipient, amount); - - self._link_token.write(link_token); - - self - .emit( - Event::LinkTokenSet( - LinkTokenSet { old_link_token: old_token, new_link_token: link_token } - ) - ); - } - // --- Billing Config #[derive(Copy, Drop, Serde, starknet::Store)] @@ -917,8 +871,48 @@ mod Aggregator { link_token: ContractAddress, } - #[external(v0)] + #[derive(Drop, starknet::Event)] + struct LinkTokenSet { + old_link_token: ContractAddress, + new_link_token: ContractAddress + } + + #[abi(embed_v0)] impl BillingImpl of super::Billing { + fn set_link_token( + ref self: ContractState, link_token: ContractAddress, recipient: ContractAddress + ) { + self.ownable.assert_only_owner(); + + let old_token = self._link_token.read(); + + if link_token == old_token { + return (); + } + + let contract_address = starknet::info::get_contract_address(); + + // call balanceOf as a sanity check to confirm we're talking to a token + let token = IERC20Dispatcher { contract_address: link_token }; + token.balance_of(account: contract_address); + + self.pay_oracles(); + + // transfer remaining balance of old token to recipient + let old_token_dispatcher = IERC20Dispatcher { contract_address: old_token }; + let amount = old_token_dispatcher.balance_of(account: contract_address); + old_token_dispatcher.transfer(recipient, amount); + + self._link_token.write(link_token); + + self + .emit( + Event::LinkTokenSet( + LinkTokenSet { old_link_token: old_token, new_link_token: link_token } + ) + ); + } + fn set_billing_access_controller( ref self: ContractState, access_controller: ContractAddress ) { @@ -1102,14 +1096,11 @@ mod Aggregator { let mut index = self._oracles_len.read(); let latest_round_id = self._latest_aggregator_round_id.read(); let link_token = self._link_token.read(); - loop { - if index == 0_usize { - break (); - } + while index > 0_usize { let transmitter = self._transmitters_list.read(index); self.pay_oracle(transmitter, latest_round_id, link_token); index -= 1; - } + }; } fn total_link_due(self: @ContractState) -> u128 { @@ -1177,7 +1168,7 @@ mod Aggregator { current: ContractAddress, } - #[external(v0)] + #[abi(embed_v0)] impl PayeeManagementImpl of super::PayeeManagement { fn set_payees(ref self: ContractState, mut payees: Array) { self.ownable.assert_only_owner(); diff --git a/contracts/src/ocr2/aggregator_proxy.cairo b/contracts/src/ocr2/aggregator_proxy.cairo index 4204dddfb..32950ac8e 100644 --- a/contracts/src/ocr2/aggregator_proxy.cairo +++ b/contracts/src/ocr2/aggregator_proxy.cairo @@ -46,9 +46,11 @@ mod AggregatorProxy { use starknet::storage_address_from_base_and_offset; use starknet::class_hash::ClassHash; + use openzeppelin::access::ownable::ownable::OwnableComponent; + use chainlink::ocr2::aggregator::IAggregator; use chainlink::ocr2::aggregator::Round; - use chainlink::libraries::ownable::{OwnableComponent, IOwnable}; + // use chainlink::libraries::ownable::{OwnableComponent, IOwnable}; use chainlink::libraries::access_control::{AccessControlComponent, IAccessController}; use chainlink::libraries::access_control::AccessControlComponent::InternalTrait as AccessControlInternalTrait; use chainlink::utils::split_felt; @@ -70,7 +72,7 @@ mod AggregatorProxy { component!(path: AccessControlComponent, storage: access_control, event: AccessControlEvent); #[abi(embed_v0)] - impl OwnableImpl = OwnableComponent::OwnableImpl; + impl OwnableImpl = OwnableComponent::OwnableTwoStepImpl; impl OwnableInternalImpl = OwnableComponent::InternalImpl; #[abi(embed_v0)] @@ -105,13 +107,14 @@ mod AggregatorProxy { #[event] fn AggregatorConfirmed(previous: ContractAddress, latest: ContractAddress) {} - #[external(v0)] + #[abi(embed_v0)] impl AggregatorProxyImpl of IAggregatorProxy { fn latest_round_data(self: @ContractState) -> Round { self._require_read_access(); let phase = self._current_phase.read(); let aggregator = IAggregatorDispatcher { contract_address: phase.aggregator }; let round = aggregator.latest_round_data(); + Round { round_id: (phase.id.into() * SHIFT) + round.round_id, answer: round.answer, @@ -170,7 +173,7 @@ mod AggregatorProxy { // -- Upgradeable -- - #[external(v0)] + #[abi(embed_v0)] impl UpgradeableImpl of IUpgradeable { fn upgrade(ref self: ContractState, new_impl: ClassHash) { self.ownable.assert_only_owner(); @@ -180,7 +183,7 @@ mod AggregatorProxy { // - #[external(v0)] + #[abi(embed_v0)] impl AggregatorProxyInternal of super::IAggregatorProxyInternal { fn propose_aggregator(ref self: ContractState, address: ContractAddress) { self.ownable.assert_only_owner(); diff --git a/contracts/src/ocr2/mocks/mock_aggregator.cairo b/contracts/src/ocr2/mocks/mock_aggregator.cairo index 038a3ba18..69a80a489 100644 --- a/contracts/src/ocr2/mocks/mock_aggregator.cairo +++ b/contracts/src/ocr2/mocks/mock_aggregator.cairo @@ -35,7 +35,7 @@ mod MockAggregator { self._decimals.write(decimals); } - #[external(v0)] + #[abi(embed_v0)] impl MockImpl of super::IMockAggregator { fn set_latest_round_data( ref self: ContractState, @@ -91,7 +91,7 @@ mod MockAggregator { } } - #[external(v0)] + #[abi(embed_v0)] impl Aggregator of IAggregator { fn round_data(self: @ContractState, round_id: u128) -> Round { panic_with_felt252('unimplemented') diff --git a/contracts/src/tests/test_access_controller.cairo b/contracts/src/tests/test_access_controller.cairo index 4b96b2f30..17d570978 100644 --- a/contracts/src/tests/test_access_controller.cairo +++ b/contracts/src/tests/test_access_controller.cairo @@ -30,17 +30,15 @@ fn setup() -> ContractAddress { } #[test] -#[available_gas(2000000)] #[should_panic(expected: ('Caller is not the owner',))] fn test_upgrade_not_owner() { - let sender = setup(); + let _ = setup(); let mut state = STATE(); UpgradeableImpl::upgrade(ref state, class_hash_const::<2>()); } #[test] -#[available_gas(2000000)] fn test_access_control() { let owner = setup(); // Deploy access controller diff --git a/contracts/src/tests/test_aggregator.cairo b/contracts/src/tests/test_aggregator.cairo index 6268c9071..e2c3d81c3 100644 --- a/contracts/src/tests/test_aggregator.cairo +++ b/contracts/src/tests/test_aggregator.cairo @@ -26,7 +26,6 @@ use chainlink::tests::test_ownable::should_implement_ownable; use chainlink::tests::test_access_controller::should_implement_access_control; #[test] -#[available_gas(10000000)] fn test_pow_2_0() { assert(pow(2, 0) == 0x1, 'expected 0x1'); assert(pow(2, 1) == 0x2, 'expected 0x2'); @@ -107,7 +106,6 @@ fn setup() -> ( } #[test] -#[available_gas(3000000)] fn test_ownable() { let (account, _, _, _) = setup(); // Deploy aggregator @@ -129,7 +127,6 @@ fn test_ownable() { } #[test] -#[available_gas(3000000)] fn test_access_control() { let (account, _, _, _) = setup(); // Deploy aggregator @@ -152,10 +149,9 @@ fn test_access_control() { #[test] -#[available_gas(2000000)] #[should_panic(expected: ('Caller is not the owner',))] fn test_upgrade_non_owner() { - let sender = setup(); + let _ = setup(); let mut state = STATE(); UpgradeableImpl::upgrade(ref state, class_hash_const::<123>()); @@ -164,7 +160,6 @@ fn test_upgrade_non_owner() { // --- Billing tests --- #[test] -#[available_gas(2000000)] #[should_panic(expected: ('Caller is not the owner',))] fn test_set_billing_access_controller_not_owner() { let (owner, acc2, billingAccessController, _) = setup(); @@ -179,7 +174,6 @@ fn test_set_billing_access_controller_not_owner() { } #[test] -#[available_gas(2000000)] #[should_panic(expected: ('caller does not have access',))] fn test_set_billing_config_no_access() { let (owner, acc2, billingAccessController, _) = setup(); @@ -207,7 +201,6 @@ fn test_set_billing_config_no_access() { } #[test] -#[available_gas(2000000)] fn test_set_billing_config_as_owner() { let (owner, _, billingAccessController, _) = setup(); let mut state = STATE(); @@ -240,7 +233,6 @@ fn test_set_billing_config_as_owner() { } #[test] -#[available_gas(2000000)] fn test_set_billing_config_as_acc_with_access() { let (owner, acc2, billingAccessController, _) = setup(); let mut state = STATE(); @@ -280,7 +272,6 @@ fn test_set_billing_config_as_acc_with_access() { // --- Payee Management Tests --- #[test] -#[available_gas(2000000)] #[should_panic(expected: ('Caller is not the owner',))] fn test_set_payees_caller_not_owner() { let (owner, acc2, _, _) = setup(); @@ -297,7 +288,6 @@ fn test_set_payees_caller_not_owner() { } #[test] -#[available_gas(2000000)] fn test_set_single_payee() { let (owner, acc2, _, _) = setup(); let mut state = STATE(); @@ -312,7 +302,6 @@ fn test_set_single_payee() { } #[test] -#[available_gas(2000000)] fn test_set_multiple_payees() { let (owner, acc2, _, _) = setup(); let mut state = STATE(); @@ -330,7 +319,6 @@ fn test_set_multiple_payees() { } #[test] -#[available_gas(2000000)] #[should_panic(expected: ('only current payee can update',))] fn test_transfer_payeeship_caller_not_payee() { let (owner, acc2, _, _) = setup(); @@ -348,7 +336,6 @@ fn test_transfer_payeeship_caller_not_payee() { } #[test] -#[available_gas(2000000)] #[should_panic(expected: ('cannot transfer to self',))] fn test_transfer_payeeship_to_self() { let (owner, acc2, _, _) = setup(); @@ -367,7 +354,6 @@ fn test_transfer_payeeship_to_self() { } #[test] -#[available_gas(2000000)] #[should_panic(expected: ('only proposed payee can accept',))] fn test_accept_payeeship_caller_not_proposed_payee() { let (owner, acc2, _, _) = setup(); @@ -387,7 +373,6 @@ fn test_accept_payeeship_caller_not_proposed_payee() { } #[test] -#[available_gas(2000000)] fn test_transfer_and_accept_payeeship() { let (owner, acc2, _, _) = setup(); let mut state = STATE(); @@ -413,7 +398,6 @@ fn test_transfer_and_accept_payeeship() { // verification logic removed in the future. #[test] -#[available_gas(2000000)] fn test_owed_payment_no_rounds() { let (owner, acc2, _, _) = setup(); let mut state = STATE(); @@ -432,7 +416,6 @@ fn test_owed_payment_no_rounds() { } #[test] -#[available_gas(2000000)] fn test_link_available_for_payment_no_rounds_or_funds() { let (owner, acc2, _, linkToken) = setup(); let mut state = STATE(); diff --git a/contracts/src/tests/test_aggregator_proxy.cairo b/contracts/src/tests/test_aggregator_proxy.cairo index 29fcbb3a2..e138aa722 100644 --- a/contracts/src/tests/test_aggregator_proxy.cairo +++ b/contracts/src/tests/test_aggregator_proxy.cairo @@ -50,8 +50,12 @@ fn setup() -> ( let mockAggregator1 = IMockAggregatorDispatcher { contract_address: mockAggregatorAddr1 }; // Deploy mock aggregator 2 + // note: deployment address is deterministic based on deploy_syscall parameters + // so we need to change the decimals parameter to avoid an address conflict with mock aggregator 1 + let mut calldata2 = ArrayTrait::new(); + calldata2.append(10); // decimals = 10 let (mockAggregatorAddr2, _) = deploy_syscall( - MockAggregator::TEST_CLASS_HASH.try_into().unwrap(), 0, calldata.span(), false + MockAggregator::TEST_CLASS_HASH.try_into().unwrap(), 0, calldata2.span(), false ) .unwrap(); let mockAggregator2 = IMockAggregatorDispatcher { contract_address: mockAggregatorAddr2 }; @@ -61,7 +65,6 @@ fn setup() -> ( } #[test] -#[available_gas(2000000)] fn test_ownable() { let (account, mockAggregatorAddr, _, _, _) = setup(); // Deploy aggregator proxy @@ -76,7 +79,6 @@ fn test_ownable() { } #[test] -#[available_gas(3000000)] fn test_access_control() { let (account, mockAggregatorAddr, _, _, _) = setup(); // Deploy aggregator proxy @@ -91,7 +93,6 @@ fn test_access_control() { } #[test] -#[available_gas(2000000)] #[should_panic(expected: ('Caller is not the owner',))] fn test_upgrade_non_owner() { let (_, _, _, _, _) = setup(); @@ -119,7 +120,6 @@ fn test_query_latest_round_data() { } #[test] -#[available_gas(2000000)] #[should_panic(expected: ('user does not have read access',))] fn test_query_latest_round_data_without_access() { let (owner, mockAggregatorAddr, mockAggregator, _, _) = setup(); @@ -136,7 +136,6 @@ fn test_query_latest_round_data_without_access() { } #[test] -#[available_gas(3000000)] fn test_propose_new_aggregator() { let (owner, mockAggregatorAddr1, mockAggregator1, mockAggregatorAddr2, mockAggregator2) = setup(); @@ -155,6 +154,8 @@ fn test_propose_new_aggregator() { let round = AggregatorProxyImpl::latest_round_data(@state); assert(round.answer == 10, 'answer should be 10'); + // mockAggregator2.set_latest_round_data(12, 2, 10, 11); + // proposed_round_data should return new aggregator round data let proposed_round = AggregatorProxyInternal::proposed_latest_round_data(@state); assert(proposed_round.answer == 12, 'answer should be 12'); @@ -165,7 +166,6 @@ fn test_propose_new_aggregator() { } #[test] -#[available_gas(3000000)] fn test_confirm_new_aggregator() { let (owner, mockAggregatorAddr1, mockAggregator1, mockAggregatorAddr2, mockAggregator2) = setup(); diff --git a/contracts/src/tests/test_erc677.cairo b/contracts/src/tests/test_erc677.cairo index 15932e7b5..99b9c8fbf 100644 --- a/contracts/src/tests/test_erc677.cairo +++ b/contracts/src/tests/test_erc677.cairo @@ -66,7 +66,6 @@ fn transfer_and_call(receiver: ContractAddress) { } #[test] -#[available_gas(2000000)] #[should_panic(expected: ('ERC20: transfer to 0',))] fn test_to_zero_address() { setup(); @@ -74,7 +73,6 @@ fn test_to_zero_address() { } #[test] -#[available_gas(2000000)] fn test_valid_transfer_and_call() { let sender = setup(); let (receiver_address, receiver) = setup_valid_receiver(); @@ -85,7 +83,6 @@ fn test_valid_transfer_and_call() { } #[test] -#[available_gas(2000000)] #[should_panic(expected: ('ENTRYPOINT_NOT_FOUND',))] fn test_invalid_receiver_supports_interface_true() { setup(); @@ -97,17 +94,15 @@ fn test_invalid_receiver_supports_interface_true() { } #[test] -#[available_gas(2000000)] fn test_invalid_receiver_supports_interface_false() { setup(); - let (receiver_address, receiver) = setup_invalid_receiver(); + let (receiver_address, _) = setup_invalid_receiver(); transfer_and_call(receiver_address); } #[test] -#[available_gas(2000000)] #[should_panic(expected: ('CONTRACT_NOT_DEPLOYED',))] fn test_nonexistent_receiver() { setup(); diff --git a/contracts/src/tests/test_link_token.cairo b/contracts/src/tests/test_link_token.cairo index 719c1eb66..b41f08bdb 100644 --- a/contracts/src/tests/test_link_token.cairo +++ b/contracts/src/tests/test_link_token.cairo @@ -32,7 +32,6 @@ fn setup() -> ContractAddress { } #[test] -#[available_gas(2000000)] fn test_ownable() { let account = setup(); // Deploy LINK token @@ -48,7 +47,6 @@ fn test_ownable() { } #[test] -#[available_gas(2000000)] #[should_panic(expected: ('minter is 0',))] fn test_constructor_zero_address() { let sender = setup(); @@ -58,7 +56,6 @@ fn test_constructor_zero_address() { } #[test] -#[available_gas(2000000)] fn test_constructor() { let sender = setup(); let mut state = STATE(); @@ -70,7 +67,6 @@ fn test_constructor() { } #[test] -#[available_gas(2000000)] fn test_permissioned_mint_from_minter() { let sender = setup(); let mut state = STATE(); @@ -89,7 +85,6 @@ fn test_permissioned_mint_from_minter() { } #[test] -#[available_gas(2000000)] #[should_panic(expected: ('only minter',))] fn test_permissioned_mint_from_nonminter() { let sender = setup(); @@ -103,7 +98,6 @@ fn test_permissioned_mint_from_nonminter() { } #[test] -#[available_gas(2000000)] #[should_panic(expected: ('u256_sub Overflow',))] fn test_permissioned_burn_from_minter() { let zero = 0; @@ -132,7 +126,6 @@ fn test_permissioned_burn_from_minter() { #[test] -#[available_gas(2000000)] #[should_panic(expected: ('only minter',))] fn test_permissioned_burn_from_nonminter() { let sender = setup(); @@ -146,7 +139,6 @@ fn test_permissioned_burn_from_nonminter() { } #[test] -#[available_gas(2000000)] #[should_panic(expected: ('Caller is not the owner',))] fn test_upgrade_non_owner() { let sender = setup(); diff --git a/contracts/src/tests/test_mock_aggregator.cairo b/contracts/src/tests/test_mock_aggregator.cairo index 70ee6bad3..ede11f155 100644 --- a/contracts/src/tests/test_mock_aggregator.cairo +++ b/contracts/src/tests/test_mock_aggregator.cairo @@ -16,7 +16,6 @@ fn setup() -> ContractAddress { } #[test] -#[available_gas(2000000)] fn test_deploy() { setup(); @@ -28,7 +27,7 @@ fn test_deploy() { let latest_round = MockAggregator::Aggregator::latest_round_data(@state); - let zeroed_round = Round { + let _ = Round { round_id: 0, answer: 0_u128, block_num: 0_u64, started_at: 0_u64, updated_at: 0_u64 }; @@ -41,7 +40,6 @@ fn test_deploy() { } #[test] -#[available_gas(2000000)] fn test_set_latest_round() { setup(); diff --git a/contracts/src/tests/test_multisig.cairo b/contracts/src/tests/test_multisig.cairo index 40c87a8e1..becfe4d7d 100644 --- a/contracts/src/tests/test_multisig.cairo +++ b/contracts/src/tests/test_multisig.cairo @@ -24,9 +24,13 @@ mod MultisigTest { #[storage] struct Storage {} - #[external(v0)] - fn increment(ref self: ContractState, val1: felt252, val2: felt252) -> Array { - array![val1 + 1, val2 + 1] + #[abi(per_item)] + #[generate_trait] + impl HelperImpl of HelperTrait { + #[external(v0)] + fn increment(ref self: ContractState, val1: felt252, val2: felt252) -> Array { + array![val1 + 1, val2 + 1] + } } } @@ -40,21 +44,18 @@ fn sample_calldata() -> Array:: { } #[test] -#[available_gas(2000000)] fn test_assert_unique_values_empty() { let a = ArrayTrait::::new(); assert_unique_values(@a); } #[test] -#[available_gas(2000000)] fn test_assert_unique_values_no_duplicates() { let a = array![1, 2, 3]; assert_unique_values(@a); } #[test] -#[available_gas(2000000)] #[should_panic] fn test_assert_unique_values_with_duplicate() { let a = array![1, 2, 3, 3]; @@ -62,7 +63,6 @@ fn test_assert_unique_values_with_duplicate() { } #[test] -#[available_gas(2000000)] fn test_is_signer_true() { let mut state = STATE(); let signer = contract_address_const::<1>(); @@ -73,7 +73,6 @@ fn test_is_signer_true() { } #[test] -#[available_gas(2000000)] fn test_is_signer_false() { let mut state = STATE(); let not_signer = contract_address_const::<2>(); @@ -84,7 +83,6 @@ fn test_is_signer_false() { } #[test] -#[available_gas(2000000)] fn test_signer_len() { let mut state = STATE(); let mut signers = ArrayTrait::new(); @@ -95,7 +93,6 @@ fn test_signer_len() { } #[test] -#[available_gas(2000000)] fn test_get_signers() { let mut state = STATE(); let signer1 = contract_address_const::<1>(); @@ -110,7 +107,6 @@ fn test_get_signers() { } #[test] -#[available_gas(2000000)] fn test_get_threshold() { let mut state = STATE(); let mut signers = ArrayTrait::new(); @@ -121,7 +117,6 @@ fn test_get_threshold() { } #[test] -#[available_gas(2000000)] fn test_submit_transaction() { let mut state = STATE(); let signer = contract_address_const::<1>(); @@ -135,7 +130,7 @@ fn test_submit_transaction() { ref state, :to, :function_selector, calldata: sample_calldata() ); - let (transaction, calldata) = MultisigImpl::get_transaction(@state, 0); + let (transaction, _) = MultisigImpl::get_transaction(@state, 0); assert(transaction.to == to, 'should match target address'); assert(transaction.function_selector == function_selector, 'should match function selector'); assert(transaction.calldata_len == sample_calldata().len(), 'should match calldata length'); @@ -145,7 +140,6 @@ fn test_submit_transaction() { } #[test] -#[available_gas(2000000)] #[should_panic] fn test_submit_transaction_not_signer() { let mut state = STATE(); @@ -163,7 +157,6 @@ fn test_submit_transaction_not_signer() { } #[test] -#[available_gas(2000000)] fn test_confirm_transaction() { let mut state = STATE(); let signer1 = contract_address_const::<1>(); @@ -189,7 +182,6 @@ fn test_confirm_transaction() { } #[test] -#[available_gas(2000000)] #[should_panic] fn test_confirm_transaction_not_signer() { let mut state = STATE(); @@ -210,7 +202,6 @@ fn test_confirm_transaction_not_signer() { } #[test] -#[available_gas(4000000)] fn test_revoke_confirmation() { let mut state = STATE(); let signer1 = contract_address_const::<1>(); @@ -239,7 +230,6 @@ fn test_revoke_confirmation() { } #[test] -#[available_gas(4000000)] #[should_panic] fn test_revoke_confirmation_not_signer() { let mut state = STATE(); @@ -261,7 +251,6 @@ fn test_revoke_confirmation_not_signer() { } #[test] -#[available_gas(2000000)] #[should_panic] fn test_execute_confirmation_below_threshold() { let mut state = STATE(); @@ -281,7 +270,6 @@ fn test_execute_confirmation_below_threshold() { } #[test] -#[available_gas(2000000)] #[should_panic(expected: ('only multisig allowed',))] fn test_upgrade_not_multisig() { let mut state = STATE(); @@ -292,7 +280,6 @@ fn test_upgrade_not_multisig() { } #[test] -#[available_gas(80000000)] fn test_execute() { let mut state = STATE(); let signer1 = contract_address_const::<1>(); @@ -324,7 +311,6 @@ fn test_execute() { } #[test] -#[available_gas(8000000)] #[should_panic(expected: ('invalid signer',))] fn test_execute_not_signer() { let mut state = STATE(); @@ -348,7 +334,6 @@ fn test_execute_not_signer() { } #[test] -#[available_gas(8000000)] #[should_panic(expected: ('transaction invalid',))] fn test_execute_after_set_signers() { let mut state = STATE(); @@ -378,7 +363,6 @@ fn test_execute_after_set_signers() { } #[test] -#[available_gas(8000000)] #[should_panic(expected: ('transaction invalid',))] fn test_execute_after_set_signers_and_threshold() { let mut state = STATE(); @@ -408,7 +392,6 @@ fn test_execute_after_set_signers_and_threshold() { } #[test] -#[available_gas(8000000)] #[should_panic(expected: ('transaction invalid',))] fn test_execute_after_set_threshold() { let mut state = STATE(); @@ -455,7 +438,6 @@ fn test_set_threshold() { let multisig = IMultisigDispatcher { contract_address: multisig_address }; assert(multisig.get_threshold() == init_threshold, 'invalid init threshold'); - set_caller_address(multisig_address); set_contract_address(multisig_address); multisig.set_threshold(new_threshold); assert(multisig.get_threshold() == new_threshold, 'threshold was not updated'); @@ -485,30 +467,24 @@ fn test_recursive_set_threshold() { // Checks that the threshold was correctly initialized on deployment assert(multisig.get_threshold() == init_threshold, 'invalid init threshold'); - // Recursive call occurs here - this code proposes a transaction to the // multisig contract that calls the set_threshold function on the multisig // contract. let mut set_threshold_calldata = ArrayTrait::new(); Serde::serialize(@new_threshold, ref set_threshold_calldata); - set_caller_address(multisig_address); - set_contract_address(multisig_address); + set_contract_address(s1); multisig .submit_transaction(multisig_address, selector!("set_threshold"), set_threshold_calldata); - // Signer 1 confirms the transaction - set_caller_address(s1); set_contract_address(s1); multisig.confirm_transaction(0); // Signer 2 confirms the transaction - set_caller_address(s2); set_contract_address(s2); multisig.confirm_transaction(0); // Once we have enough confirmations, we execute the transaction - set_caller_address(multisig_address); - set_contract_address(multisig_address); + set_contract_address(s1); multisig.execute_transaction(0); // Now we check that the threshold was actually updated @@ -540,7 +516,6 @@ fn test_set_signers() { assert(*returned_signers.at(1) == s2, 'should match signer 2'); assert(multisig.get_threshold() == 2, 'wrong init threshold'); - set_caller_address(multisig_address); set_contract_address(multisig_address); multisig.set_signers(new_signers); @@ -584,23 +559,19 @@ fn test_recursive_set_signers() { // contract. let mut set_signers_calldata = ArrayTrait::new(); Serde::serialize(@new_signers, ref set_signers_calldata); - set_caller_address(multisig_address); - set_contract_address(multisig_address); + set_contract_address(s1); multisig.submit_transaction(multisig_address, selector!("set_signers"), set_signers_calldata); // Signer 1 confirms the transaction - set_caller_address(s1); set_contract_address(s1); multisig.confirm_transaction(0); // Signer 2 confirms the transaction - set_caller_address(s2); set_contract_address(s2); multisig.confirm_transaction(0); // Once we have enough confirmations, we execute the transaction - set_caller_address(multisig_address); - set_contract_address(multisig_address); + set_contract_address(s1); multisig.execute_transaction(0); // Now we check that the signers were actually updated @@ -638,7 +609,6 @@ fn test_set_signers_and_threshold() { assert(*returned_signers.at(2) == s3, 'should match signer 3'); assert(multisig.get_threshold() == init_threshold, 'wrong init threshold'); - set_caller_address(multisig_address); set_contract_address(multisig_address); multisig.set_signers_and_threshold(new_signers, new_threshold); @@ -687,8 +657,7 @@ fn test_recursive_set_signers_and_threshold() { let mut set_signers_and_threshold_calldata = ArrayTrait::new(); Serde::serialize(@new_signers, ref set_signers_and_threshold_calldata); Serde::serialize(@new_threshold, ref set_signers_and_threshold_calldata); - set_caller_address(multisig_address); - set_contract_address(multisig_address); + set_contract_address(s1); multisig .submit_transaction( multisig_address, @@ -697,23 +666,19 @@ fn test_recursive_set_signers_and_threshold() { ); // Signer 1 confirms the transaction - set_caller_address(s1); set_contract_address(s1); multisig.confirm_transaction(0); // Signer 2 confirms the transaction - set_caller_address(s2); set_contract_address(s2); multisig.confirm_transaction(0); // Signer 3 confirms the transaction - set_caller_address(s3); set_contract_address(s3); multisig.confirm_transaction(0); // Once we have enough confirmations, we execute the transaction - set_caller_address(multisig_address); - set_contract_address(multisig_address); + set_contract_address(s1); multisig.execute_transaction(0); // Now we check that the signers were actually updated diff --git a/contracts/src/tests/test_ownable.cairo b/contracts/src/tests/test_ownable.cairo index 54d543bc6..fdf010f1f 100644 --- a/contracts/src/tests/test_ownable.cairo +++ b/contracts/src/tests/test_ownable.cairo @@ -3,8 +3,10 @@ use starknet::ContractAddress; use starknet::testing::set_caller_address; use starknet::testing::set_contract_address; use zeroable::Zeroable; -use chainlink::libraries::ownable::{ - OwnableComponent, IOwnable, IOwnableDispatcher, IOwnableDispatcherTrait + +use openzeppelin::access::ownable::ownable::OwnableComponent; +use openzeppelin::access::ownable::interface::{ + IOwnableTwoStep, IOwnableTwoStepDispatcher, IOwnableTwoStepDispatcherTrait }; use OwnableComponent::InternalTrait; @@ -16,7 +18,7 @@ mod OwnableMock { component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); #[abi(embed_v0)] - impl OwnableImpl = OwnableComponent::OwnableImpl; + impl OwnableImpl = OwnableComponent::OwnableTwoStepImpl; impl InternalImpl = OwnableComponent::InternalImpl; #[storage] @@ -52,7 +54,6 @@ fn setup() -> (ContractAddress, ContractAddress) { } #[test] -#[available_gas(2000000)] fn test_assert_only_owner() { let (owner, _) = setup(); @@ -62,7 +63,6 @@ fn test_assert_only_owner() { } #[test] -#[available_gas(2000000)] #[should_panic] fn test_assert_only_owner_panics_if_not_owner() { let (owner, other_user) = setup(); @@ -74,7 +74,6 @@ fn test_assert_only_owner_panics_if_not_owner() { } #[test] -#[available_gas(2000000)] fn test_owner() { let (owner, _) = setup(); let mut state = STATE(); @@ -85,7 +84,6 @@ fn test_owner() { } #[test] -#[available_gas(2000000)] fn test_transfer_ownership() { let (owner, other_user) = setup(); let mut state = STATE(); @@ -93,22 +91,10 @@ fn test_transfer_ownership() { state.initializer(owner); state.transfer_ownership(other_user); - assert(other_user == state.proposed_owner(), 'should equal proposed owner'); -} - -#[test] -#[available_gas(2000000)] -#[should_panic] -fn test_transfer_ownership_panics_if_zero_address() { - let (owner, other_user) = setup(); - let mut state = STATE(); - - state.initializer(owner); - state.transfer_ownership(Zeroable::zero()); + assert(other_user == state.pending_owner(), 'should equal proposed owner'); } #[test] -#[available_gas(2000000)] #[should_panic] fn test_transfer_ownership_panics_if_not_owner() { let (owner, other_user) = setup(); @@ -120,7 +106,6 @@ fn test_transfer_ownership_panics_if_not_owner() { } #[test] -#[available_gas(2000000)] fn test_accept_ownership() { let (owner, other_user) = setup(); let mut state = STATE(); @@ -134,9 +119,8 @@ fn test_accept_ownership() { } #[test] -#[available_gas(2000000)] #[should_panic] -fn test_accept_ownership_panics_if_not_proposed_owner() { +fn test_accept_ownership_panics_if_not_pending_owner() { let (owner, other_user) = setup(); let mut state = STATE(); @@ -148,7 +132,6 @@ fn test_accept_ownership_panics_if_not_proposed_owner() { } #[test] -#[available_gas(2000000)] fn test_renounce_ownership() { let (owner, _) = setup(); let mut state = STATE(); @@ -161,7 +144,6 @@ fn test_renounce_ownership() { } #[test] -#[available_gas(2000000)] #[should_panic] fn test_renounce_ownership_panics_if_not_owner() { let (owner, other_user) = setup(); @@ -176,7 +158,7 @@ fn test_renounce_ownership_panics_if_not_owner() { // fn should_implement_ownable(contract_addr: ContractAddress, owner: ContractAddress) { - let contract = IOwnableDispatcher { contract_address: contract_addr }; + let contract = IOwnableTwoStepDispatcher { contract_address: contract_addr }; let acc2: ContractAddress = contract_address_const::<2222>(); // check owner is set correctly @@ -184,16 +166,15 @@ fn should_implement_ownable(contract_addr: ContractAddress, owner: ContractAddre // transfer ownership - check owner unchanged and proposed owner set correctly set_contract_address(owner); // required to call contract as owner - let caller = starknet::get_caller_address(); contract.transfer_ownership(acc2); assert(owner == contract.owner(), 'owner should remain unchanged'); - assert(acc2 == contract.proposed_owner(), 'acc2 should be proposed owner'); + assert(acc2 == contract.pending_owner(), 'acc2 should be proposed owner'); // accept ownership - check owner changed and proposed owner set to zero set_contract_address(acc2); // required to call function as acc2 contract.accept_ownership(); assert(contract.owner() == acc2, 'failed to change ownership'); - assert(contract.proposed_owner().is_zero(), 'proposed owner should be zero'); + assert(contract.pending_owner().is_zero(), 'proposed owner should be zero'); // renounce ownership contract.renounce_ownership(); diff --git a/contracts/src/tests/test_sequencer_uptime_feed.cairo b/contracts/src/tests/test_sequencer_uptime_feed.cairo index 558f40b2b..194f06f9f 100644 --- a/contracts/src/tests/test_sequencer_uptime_feed.cairo +++ b/contracts/src/tests/test_sequencer_uptime_feed.cairo @@ -51,21 +51,18 @@ fn setup() -> (ContractAddress, ContractAddress, ISequencerUptimeFeedDispatcher) } #[test] -#[available_gas(2000000)] fn test_ownable() { let (account, sequencerFeedAddr, _) = setup(); should_implement_ownable(sequencerFeedAddr, account); } #[test] -#[available_gas(3000000)] fn test_access_control() { let (account, sequencerFeedAddr, _) = setup(); should_implement_access_control(sequencerFeedAddr, account); } #[test] -#[available_gas(2000000)] #[should_panic()] fn test_set_l1_sender_not_owner() { let (_, _, sequencerUptimeFeed) = setup(); @@ -73,7 +70,6 @@ fn test_set_l1_sender_not_owner() { } #[test] -#[available_gas(2000000)] fn test_set_l1_sender() { let (owner, _, sequencerUptimeFeed) = setup(); set_contract_address(owner); @@ -82,7 +78,6 @@ fn test_set_l1_sender() { } #[test] -#[available_gas(2000000)] #[should_panic(expected: ('user does not have read access',))] fn test_latest_round_data_no_access() { let (owner, sequencerFeedAddr, _) = setup(); @@ -92,7 +87,6 @@ fn test_latest_round_data_no_access() { } #[test] -#[available_gas(2000000)] #[should_panic(expected: ('user does not have read access',))] fn test_aggregator_proxy_response() { let (owner, sequencerFeedAddr, _) = setup(); diff --git a/contracts/src/tests/test_upgradeable.cairo b/contracts/src/tests/test_upgradeable.cairo index c7cd16f8c..4d4213573 100644 --- a/contracts/src/tests/test_upgradeable.cairo +++ b/contracts/src/tests/test_upgradeable.cairo @@ -23,9 +23,8 @@ fn setup() -> ContractAddress { } #[test] -#[available_gas(2000000)] fn test_upgrade_and_call() { - let sender = setup(); + let _ = setup(); let calldata = array![]; let (contractAddr, _) = deploy_syscall( @@ -44,10 +43,9 @@ fn test_upgrade_and_call() { #[test] -#[available_gas(2000000)] #[should_panic(expected: ('Class hash cannot be zero',))] fn test_upgrade_zero_hash() { - let sender = setup(); + let _ = setup(); Upgradeable::upgrade(class_hash_const::<0>()); } diff --git a/contracts/src/token/link_token.cairo b/contracts/src/token/link_token.cairo index c3e139c89..60c8c0251 100644 --- a/contracts/src/token/link_token.cairo +++ b/contracts/src/token/link_token.cairo @@ -9,26 +9,27 @@ trait IMintableToken { #[starknet::contract] mod LinkToken { - use super::IMintableToken; + use starknet::ContractAddress; + use starknet::class_hash::ClassHash; use zeroable::Zeroable; + use openzeppelin::token::erc20::ERC20Component; + use openzeppelin::access::ownable::ownable::OwnableComponent; + + use super::IMintableToken; use openzeppelin::token::erc20::interface::{IERC20, IERC20Dispatcher, IERC20DispatcherTrait}; use chainlink::libraries::token::erc677::ERC677Component; - use chainlink::libraries::ownable::{OwnableComponent, IOwnable}; + // use chainlink::libraries::ownable::{OwnableComponent, IOwnable}; use chainlink::libraries::type_and_version::ITypeAndVersion; use chainlink::libraries::upgradeable::{Upgradeable, IUpgradeable}; - use openzeppelin::token::erc20::ERC20Component; - use starknet::ContractAddress; - use starknet::class_hash::ClassHash; - component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); component!(path: ERC20Component, storage: erc20, event: ERC20Event); component!(path: ERC677Component, storage: erc677, event: ERC677Event); #[abi(embed_v0)] - impl OwnableImpl = OwnableComponent::OwnableImpl; + impl OwnableImpl = OwnableComponent::OwnableTwoStepImpl; impl InternalImpl = OwnableComponent::InternalImpl; #[abi(embed_v0)] @@ -68,7 +69,7 @@ mod LinkToken { // // IMintableToken (StarkGate) // - #[external(v0)] + #[abi(embed_v0)] impl MintableToken of IMintableToken { fn permissioned_mint(ref self: ContractState, account: ContractAddress, amount: u256) { only_minter(@self); @@ -102,7 +103,7 @@ mod LinkToken { } } - #[external(v0)] + #[abi(embed_v0)] impl UpgradeableImpl of IUpgradeable { fn upgrade(ref self: ContractState, new_impl: ClassHash) { self.ownable.assert_only_owner(); diff --git a/contracts/src/token/mock/invalid_erc667_receiver.cairo b/contracts/src/token/mock/invalid_erc667_receiver.cairo index 6b2ef658b..9332dc66f 100644 --- a/contracts/src/token/mock/invalid_erc667_receiver.cairo +++ b/contracts/src/token/mock/invalid_erc667_receiver.cairo @@ -8,15 +8,18 @@ mod InvalidReceiver { #[constructor] fn constructor(ref self: ContractState) {} - // toggle whether or not receiver says it supports the interface id - #[external(v0)] - fn set_supports(ref self: ContractState, support: bool) { - self._supports.write(support); - } - + #[abi(per_item)] + #[generate_trait] + impl HelperImpl of HelperTrait { + // toggle whether or not receiver says it supports the interface id + #[external(v0)] + fn set_supports(ref self: ContractState, support: bool) { + self._supports.write(support); + } - #[external(v0)] - fn supports_interface(self: @ContractState, interface_id: u32) -> bool { - self._supports.read() + #[external(v0)] + fn supports_interface(self: @ContractState, interface_id: u32) -> bool { + self._supports.read() + } } } diff --git a/contracts/src/token/mock/valid_erc667_receiver.cairo b/contracts/src/token/mock/valid_erc667_receiver.cairo index 33e72c129..cd08f7da6 100644 --- a/contracts/src/token/mock/valid_erc667_receiver.cairo +++ b/contracts/src/token/mock/valid_erc667_receiver.cairo @@ -18,7 +18,8 @@ mod ValidReceiver { #[constructor] fn constructor(ref self: ContractState) {} - #[external(v0)] + + #[abi(embed_v0)] impl ERC677Receiver of IERC677Receiver { fn on_token_transfer( ref self: ContractState, sender: ContractAddress, value: u256, data: Array @@ -31,7 +32,7 @@ mod ValidReceiver { } } - #[external(v0)] + #[abi(embed_v0)] impl ValidReceiver of super::MockValidReceiver { fn verify(self: @ContractState) -> ContractAddress { self._sender.read()