Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

NONEVM-629: Link Token Changes #535

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion contracts/src/libraries/token.cairo
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
mod erc677;
mod v1;
mod v2;
1 change: 1 addition & 0 deletions contracts/src/libraries/token/v1.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mod erc677;
2 changes: 2 additions & 0 deletions contracts/src/libraries/token/v2.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
mod erc677;
mod erc677_receiver;
79 changes: 79 additions & 0 deletions contracts/src/libraries/token/v2/erc677.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use starknet::ContractAddress;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doing a diff between v2/erc667.cairo and v1/erc667.cairo will make the changes more apparent


const IERC677_ID: felt252 = 0x3c4538abc63e0cdf912cef3d2e1389d0b2c3f24ee0c06b21736229f52ece6c8;

#[starknet::interface]
trait IERC677<TContractState> {
fn transfer_and_call(
ref self: TContractState, to: ContractAddress, value: u256, data: Array<felt252>
) -> bool;
}

#[starknet::component]
mod ERC677Component {
use starknet::ContractAddress;
use openzeppelin::token::erc20::interface::IERC20;
use openzeppelin::introspection::interface::{ISRC5, ISRC5Dispatcher, ISRC5DispatcherTrait};
use array::ArrayTrait;
use array::SpanTrait;
use clone::Clone;
use array::ArrayTCloneImpl;
use chainlink::libraries::token::v2::erc677_receiver::{
IERC677ReceiverDispatcher, IERC677ReceiverDispatcherTrait, IERC677_RECEIVER_ID
};

#[storage]
struct Storage {}

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
TransferAndCall: TransferAndCall,
}

#[derive(Drop, starknet::Event)]
struct TransferAndCall {
#[key]
from: ContractAddress,
#[key]
to: ContractAddress,
value: u256,
data: Array<felt252>
}

#[embeddable_as(ERC677Impl)]
impl ERC677<
TContractState,
+HasComponent<TContractState>,
+IERC20<TContractState>,
+Drop<TContractState>,
> of super::IERC677<ComponentState<TContractState>> {
fn transfer_and_call(
ref self: ComponentState<TContractState>,
to: ContractAddress,
value: u256,
data: Array<felt252>
) -> bool {
let sender = starknet::info::get_caller_address();

let mut contract = self.get_contract_mut();
contract.transfer(to, value);
self
.emit(
Event::TransferAndCall(
TransferAndCall { from: sender, to: to, value: value, data: data.clone(), }
)
);

let receiver = ISRC5Dispatcher { contract_address: to };

let supports = receiver.supports_interface(IERC677_RECEIVER_ID);

if supports {
IERC677ReceiverDispatcher { contract_address: to }
.on_token_transfer(sender, value, data);
}
true
}
}
}
43 changes: 43 additions & 0 deletions contracts/src/libraries/token/v2/erc677_receiver.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use starknet::ContractAddress;

const IERC677_RECEIVER_ID: felt252 =
0x224f0246bc4ebdcc391196e93f522342f12393c1a456db2a57043638940254;

#[starknet::interface]
trait IERC677Receiver<TContractState> {
fn on_token_transfer(
ref self: TContractState, sender: ContractAddress, value: u256, data: Array<felt252>
);
}

#[starknet::component]
mod ERC677ReceiverComponent {
use openzeppelin::introspection::src5::SRC5Component::InternalTrait as SRC5InternalTrait;
use openzeppelin::introspection::src5::SRC5Component;
use starknet::ContractAddress;
use super::{IERC677Receiver, IERC677_RECEIVER_ID};

#[storage]
struct Storage {}

#[event]
#[derive(Drop, starknet::Event)]
enum Event {}

#[generate_trait]
impl InternalImpl<
TContractState,
+HasComponent<TContractState>,
// ensure that the contract implements the IERC677Receiver interface
+IERC677Receiver<TContractState>,
impl SRC5: SRC5Component::HasComponent<TContractState>,
+Drop<TContractState>
> of InternalTrait<TContractState> {
/// Initializes the contract by registering the IERC677Receiver interface ID.
/// This should be used inside the contract's constructor.
fn initializer(ref self: ComponentState<TContractState>) {
let mut src5_component = get_dep_component_mut!(ref self, SRC5);
src5_component.register_interface(IERC677_RECEIVER_ID);
}
}
}
8 changes: 8 additions & 0 deletions contracts/src/libraries/token/v2/test.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
trait IERC677Receiver {
fn on_token_transfer(sender: ContractAddress, value: u256, data: Array<felt252>);
}

trait IERC677 {
fn transfer_and_call(to: ContractAddress, value: u256, data: Array<felt252>) -> bool;
}

14 changes: 7 additions & 7 deletions contracts/src/tests/test_aggregator.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,12 @@ use chainlink::ocr2::aggregator::Aggregator::{
use chainlink::ocr2::aggregator::Aggregator::BillingConfig;
use chainlink::ocr2::aggregator::Aggregator::PayeeConfig;
use chainlink::access_control::access_controller::AccessController;
use chainlink::token::link_token::LinkToken;
use chainlink::tests::test_ownable::should_implement_ownable;
use chainlink::tests::test_access_controller::should_implement_access_control;
use chainlink::token::v2::link_token::LinkToken;
use chainlink::tests::{
test_ownable::should_implement_ownable, test_access_controller::should_implement_access_control,
test_link_token::link_deploy_args
};


use snforge_std::{
declare, ContractClassTrait, start_cheat_caller_address_global, stop_cheat_caller_address_global
Expand Down Expand Up @@ -97,10 +100,7 @@ fn setup() -> (
contract_address: billingAccessControllerAddr
};

// deploy link token contract
let calldata = array![acc1.into(), // minter = acc1;
acc1.into(), // owner = acc1;
];
let calldata = link_deploy_args(acc1, acc1);

let (linkTokenAddr, _) = declare("LinkToken").unwrap().deploy(@calldata).unwrap();

Expand Down
6 changes: 3 additions & 3 deletions contracts/src/tests/test_erc677.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ use core::result::ResultTrait;

use chainlink::token::mock::valid_erc667_receiver::ValidReceiver;
use chainlink::token::mock::invalid_erc667_receiver::InvalidReceiver;
use chainlink::libraries::token::erc677::ERC677Component;
use chainlink::libraries::token::erc677::ERC677Component::ERC677Impl;
use chainlink::libraries::token::v2::erc677::ERC677Component;
use chainlink::libraries::token::v2::erc677::ERC677Component::ERC677Impl;

use snforge_std::{
declare, ContractClassTrait, start_cheat_caller_address_global, stop_cheat_caller_address_global
Expand Down Expand Up @@ -58,7 +58,7 @@ fn setup_invalid_receiver() -> (ContractAddress, MockInvalidReceiverDispatcher)
}

type ComponentState =
ERC677Component::ComponentState<chainlink::token::link_token::LinkToken::ContractState>;
ERC677Component::ComponentState<chainlink::token::v2::link_token::LinkToken::ContractState>;

fn transfer_and_call(receiver: ContractAddress) {
let data = ArrayTrait::<felt252>::new();
Expand Down
100 changes: 79 additions & 21 deletions contracts/src/tests/test_link_token.cairo
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
use starknet::ContractAddress;
use starknet::testing::set_caller_address;
use starknet::contract_address_const;
use starknet::class_hash::class_hash_const;
use starknet::class_hash::Felt252TryIntoClassHash;
use starknet::syscalls::deploy_syscall;
use starknet::{
syscalls::deploy_syscall, ContractAddress, testing::set_caller_address, contract_address_const,
class_hash::{class_hash_const, Felt252TryIntoClassHash}
};

use array::ArrayTrait;
use traits::Into;
use traits::TryInto;
use traits::{Into, TryInto};
use zeroable::Zeroable;
use option::OptionTrait;
use core::result::ResultTrait;

use chainlink::token::link_token::LinkToken;
use chainlink::token::link_token::LinkToken::{MintableToken, UpgradeableImpl};
use chainlink::token::v2::link_token::{
LinkToken, LinkToken::{MintableToken, UpgradeableImpl, Minter}
};
use openzeppelin::token::erc20::ERC20Component::{ERC20Impl, ERC20MetadataImpl};
use chainlink::tests::test_ownable::should_implement_ownable;

Expand All @@ -36,13 +34,33 @@ fn setup() -> ContractAddress {
account
}

fn link_deploy_args(minter: ContractAddress, owner: ContractAddress) -> Array<felt252> {
let mut calldata = ArrayTrait::new();
let _name_ignore: felt252 = 0;
let _symbol_ignore: felt252 = 0;
let _decimals_ignore: u8 = 0;
let _initial_supply_ignore: u256 = 0;
let _initial_recipient_ignore: ContractAddress = Zeroable::zero();
let _upgrade_delay_ignore: u64 = 0;
Serde::serialize(@_name_ignore, ref calldata);
Serde::serialize(@_symbol_ignore, ref calldata);
Serde::serialize(@_decimals_ignore, ref calldata);
Serde::serialize(@_initial_supply_ignore, ref calldata);
Serde::serialize(@_initial_recipient_ignore, ref calldata);
Serde::serialize(@minter, ref calldata);
Serde::serialize(@owner, ref calldata);
Serde::serialize(@_upgrade_delay_ignore, ref calldata);

calldata
}

#[test]
fn test_ownable() {
let account = setup();
// Deploy LINK token
let mut calldata = ArrayTrait::new();
calldata.append(class_hash_const::<123>().into()); // minter
calldata.append(account.into()); // owner
let calldata = link_deploy_args(contract_address_const::<123>(), // minter
account // owner
);

let (linkAddr, _) = declare("LinkToken").unwrap().deploy(@calldata).unwrap();

Expand All @@ -55,16 +73,16 @@ fn test_constructor_zero_address() {
let sender = setup();
let mut state = STATE();

LinkToken::constructor(ref state, Zeroable::zero(), sender);
LinkToken::constructor(ref state, 0, 0, 0, 0, Zeroable::zero(), Zeroable::zero(), sender, 0);
}

#[test]
fn test_constructor() {
let sender = setup();
let mut state = STATE();
LinkToken::constructor(ref state, sender, sender);
LinkToken::constructor(ref state, 0, 0, 0, 0, Zeroable::zero(), sender, sender, 0);

assert(LinkToken::minter(@state) == sender, 'minter valid');
assert(Minter::minter(@state) == sender, 'minter valid');
assert(state.erc20.name() == "ChainLink Token", 'name valid');
assert(state.erc20.symbol() == "LINK", 'symbol valid');
}
Expand All @@ -73,7 +91,7 @@ fn test_constructor() {
fn test_permissioned_mint_from_minter() {
let sender = setup();
let mut state = STATE();
LinkToken::constructor(ref state, sender, sender);
LinkToken::constructor(ref state, 0, 0, 0, 0, Zeroable::zero(), sender, sender, 0);
let to = contract_address_const::<908>();

let zero: felt252 = 0;
Expand All @@ -93,7 +111,7 @@ fn test_permissioned_mint_from_nonminter() {
let sender = setup();
let mut state = STATE();
let minter = contract_address_const::<111>();
LinkToken::constructor(ref state, minter, sender);
LinkToken::constructor(ref state, 0, 0, 0, 0, Zeroable::zero(), minter, sender, 0);
let to = contract_address_const::<908>();

let amount: felt252 = 3000;
Expand All @@ -106,7 +124,7 @@ fn test_permissioned_burn_from_minter() {
let zero = 0;
let sender = setup();
let mut state = STATE();
LinkToken::constructor(ref state, sender, sender);
LinkToken::constructor(ref state, 0, 0, 0, 0, Zeroable::zero(), sender, sender, 0);
let to = contract_address_const::<908>();

let amount: felt252 = 3000;
Expand Down Expand Up @@ -134,7 +152,7 @@ fn test_permissioned_burn_from_nonminter() {
let sender = setup();
let mut state = STATE();
let minter = contract_address_const::<111>();
LinkToken::constructor(ref state, minter, sender);
LinkToken::constructor(ref state, 0, 0, 0, 0, Zeroable::zero(), minter, sender, 0);
let to = contract_address_const::<908>();

let amount: felt252 = 3000;
Expand All @@ -146,8 +164,48 @@ fn test_permissioned_burn_from_nonminter() {
fn test_upgrade_non_owner() {
let sender = setup();
let mut state = STATE();
LinkToken::constructor(ref state, sender, contract_address_const::<111>());
LinkToken::constructor(
ref state, 0, 0, 0, 0, Zeroable::zero(), sender, contract_address_const::<111>(), 0
);

UpgradeableImpl::upgrade(ref state, class_hash_const::<123>());
}

#[test]
#[should_panic(expected: ('Caller is not the owner',))]
fn test_set_minter_non_owner() {
let sender = setup();
let mut state = STATE();
LinkToken::constructor(
ref state, 0, 0, 0, 0, Zeroable::zero(), sender, contract_address_const::<111>(), 0
);

Minter::set_minter(ref state, contract_address_const::<123>())
}

#[test]
#[should_panic(expected: ('is minter already',))]
fn test_set_minter_already() {
let sender = setup();
let mut state = STATE();

let minter = contract_address_const::<111>();
LinkToken::constructor(ref state, 0, 0, 0, 0, Zeroable::zero(), minter, sender, 0);

Minter::set_minter(ref state, minter);
}

#[test]
fn test_set_minter_success() {
let sender = setup();
let mut state = STATE();

let minter = contract_address_const::<111>();
LinkToken::constructor(ref state, 0, 0, 0, 0, Zeroable::zero(), minter, sender, 0);

let new_minter = contract_address_const::<222>();
Minter::set_minter(ref state, new_minter);

assert(new_minter == Minter::minter(@state), 'new minter should be 222');
}

3 changes: 2 additions & 1 deletion contracts/src/token.cairo
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
mod link_token;
mod v1;
mod v2;
mod mock;
2 changes: 1 addition & 1 deletion contracts/src/token/mock/invalid_erc667_receiver.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ mod InvalidReceiver {
}

#[external(v0)]
fn supports_interface(self: @ContractState, interface_id: u32) -> bool {
fn supports_interface(self: @ContractState, interface_id: felt252) -> bool {
self._supports.read()
}
}
Expand Down
Loading
Loading