Skip to content

Commit

Permalink
NONEVM-629: Link Token Changes (#535)
Browse files Browse the repository at this point in the history
  • Loading branch information
augustbleeds authored Oct 24, 2024
1 parent 4be11f6 commit bfc10fe
Show file tree
Hide file tree
Showing 19 changed files with 440 additions and 45 deletions.
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;
File renamed without changes.
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;

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

0 comments on commit bfc10fe

Please sign in to comment.