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

Feat/ar discount renew #36

Merged
merged 4 commits into from
May 6, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
1 change: 1 addition & 0 deletions src/interface.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ mod naming;
mod resolver;
mod pricing;
mod referral;
mod auto_renewal;
13 changes: 13 additions & 0 deletions src/interface/auto_renewal.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use starknet::ContractAddress;

#[starknet::interface]
trait IAutoRenewal<TContractState> {
fn get_renewing_allowance(
self: @TContractState, domain: felt252, renewer: starknet::ContractAddress,
) -> u256;

// naming, erc20, tax
fn get_contracts(
self: @TContractState
) -> (starknet::ContractAddress, starknet::ContractAddress, starknet::ContractAddress);
}
9 changes: 8 additions & 1 deletion src/interface/naming.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ trait INaming<TContractState> {
self: @TContractState, domain: Span<felt252>, hint: Span<felt252>
) -> ContractAddress;

fn address_to_domain(self: @TContractState, address: ContractAddress, hint: Span<felt252>) -> Span<felt252>;
fn address_to_domain(
self: @TContractState, address: ContractAddress, hint: Span<felt252>
) -> Span<felt252>;

// external
fn buy(
Expand Down Expand Up @@ -70,6 +72,8 @@ trait INaming<TContractState> {
sig: (felt252, felt252),
);

fn ar_discount_renew(ref self: TContractState, domain: felt252, ar_contract: ContractAddress,);

fn auto_renew_altcoin(
ref self: TContractState,
domain: felt252,
Expand Down Expand Up @@ -117,4 +121,7 @@ trait INaming<TContractState> {
fn whitelist_renewal_contract(ref self: TContractState, contract: ContractAddress);

fn blacklist_renewal_contract(ref self: TContractState, contract: ContractAddress);

fn toggle_ar_discount_renew(ref self: TContractState);

}
57 changes: 57 additions & 0 deletions src/naming/main.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ mod Naming {
resolver::{IResolver, IResolverDispatcher, IResolverDispatcherTrait},
pricing::{IPricing, IPricingDispatcher, IPricingDispatcherTrait},
referral::{IReferral, IReferralDispatcher, IReferralDispatcherTrait},
auto_renewal::{IAutoRenewal, IAutoRenewalDispatcher, IAutoRenewalDispatcherTrait}
}
};
use clone::Clone;
Expand Down Expand Up @@ -136,6 +137,9 @@ mod Naming {
_address_to_domain: LegacyMap<(ContractAddress, usize), felt252>,
_server_pub_key: felt252,
_whitelisted_renewal_contracts: LegacyMap<ContractAddress, bool>,
// a for alpha, as we will probably do this campaign again in the future
_ar_discount_blacklist_a: LegacyMap<felt252, bool>,
_ar_discount_renew_enabled: bool,
#[substorage(v0)]
storage_read: storage_read_component::Storage,
}
Expand Down Expand Up @@ -463,6 +467,53 @@ mod Naming {
self.emit(Event::DomainRenewal(DomainRenewal { domain, new_expiry }));
}

fn ar_discount_renew(
ref self: ContractState, domain: felt252, ar_contract: ContractAddress,
) {
// First we check the discount is enabled
assert(self._ar_discount_renew_enabled.read(), 'Discount disabled');

// We check that domain didn't already claim the discount
assert(!self._ar_discount_blacklist_a.read(domain), 'You can\'t claim this twice');

// We check it's a valid AR contract, then we check that AR is enabled,
// we don't validate the pricing because it could change
assert(self._whitelisted_renewal_contracts.read(ar_contract), 'AR not whitelisted');
let auto_renewal_dispatcher = IAutoRenewalDispatcher { contract_address: ar_contract };
let caller = get_caller_address();
let ar_allowance = auto_renewal_dispatcher.get_renewing_allowance(domain, caller);
assert(ar_allowance != 0, 'Invalid AR allowance');
let (_, erc20, _) = auto_renewal_dispatcher.get_contracts();
let erc20_allowance = IERC20CamelDispatcher { contract_address: erc20 }
.allowance(caller, ar_contract);
assert(erc20_allowance != 0, 'Invalid ERC20 allowance');

// We then blacklist that domain for this discount
self._ar_discount_blacklist_a.write(domain, true);

// We can finally renew the domain with no SaleMetadata event since it's free
let now = get_block_timestamp();
let hashed_domain = self.hash_domain(array![domain].span());
let domain_data = self._domain_data.read(hashed_domain);
// we extended its expiry by 90 days (~3 months)
let new_expiry = if domain_data.expiry <= now {
now + 86400 * 90
} else {
domain_data.expiry + 86400 * 90
};

let data = DomainData {
owner: domain_data.owner,
resolver: domain_data.resolver,
address: domain_data.address,
expiry: new_expiry,
key: domain_data.key,
parent_key: 0,
};
self._domain_data.write(hashed_domain, data);
self.emit(Event::DomainRenewal(DomainRenewal { domain, new_expiry }));
}

fn auto_renew_altcoin(
ref self: ContractState,
domain: felt252,
Expand Down Expand Up @@ -721,6 +772,12 @@ mod Naming {
assert(get_caller_address() == self._admin_address.read(), 'you are not admin');
self._whitelisted_renewal_contracts.write(contract, false);
}


fn toggle_ar_discount_renew(ref self: ContractState) {
assert(get_caller_address() == self._admin_address.read(), 'you are not admin');
self._ar_discount_renew_enabled.write(!self._ar_discount_renew_enabled.read());
}
}
}

1 change: 1 addition & 0 deletions src/tests/naming.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ mod test_custom_resolver;
mod test_usecases;
mod test_features;
mod test_altcoin;
mod test_ar_discount;
194 changes: 194 additions & 0 deletions src/tests/naming/test_ar_discount.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
use array::ArrayTrait;
use array::SpanTrait;
use option::OptionTrait;
use zeroable::Zeroable;
use traits::Into;
use starknet::testing;
use starknet::ContractAddress;
use starknet::contract_address::ContractAddressZeroable;
use starknet::contract_address_const;
use starknet::testing::set_contract_address;
use super::super::utils;
use openzeppelin::token::erc20::{
interface::{IERC20Camel, IERC20CamelDispatcher, IERC20CamelDispatcherTrait}
};
use identity::{
identity::main::Identity, interface::identity::{IIdentityDispatcher, IIdentityDispatcherTrait}
};
use naming::interface::naming::{INamingDispatcher, INamingDispatcherTrait};
use naming::interface::pricing::{IPricingDispatcher, IPricingDispatcherTrait};
use naming::interface::auto_renewal::{
IAutoRenewal, IAutoRenewalDispatcher, IAutoRenewalDispatcherTrait
};
use naming::naming::main::Naming;
use naming::pricing::Pricing;
use super::common::deploy;
use naming::naming::main::Naming::Discount;


#[starknet::contract]
mod DummyAutoRenewal {
use core::array::ArrayTrait;
use starknet::ContractAddress;
use starknet::{contract_address_const, get_caller_address, get_contract_address};

#[storage]
struct Storage {
erc20: starknet::ContractAddress,
}

#[constructor]
fn constructor(ref self: ContractState, erc20: starknet::ContractAddress) {
self.erc20.write(erc20);
}

#[abi(embed_v0)]
impl DummyImpl of naming::interface::auto_renewal::IAutoRenewal<ContractState> {
fn get_renewing_allowance(
self: @ContractState, domain: felt252, renewer: starknet::ContractAddress,
) -> u256 {
1
}

// naming, erc20, tax
fn get_contracts(
self: @ContractState
) -> (starknet::ContractAddress, starknet::ContractAddress, starknet::ContractAddress) {
(contract_address_const::<0x0>(), self.erc20.read(), contract_address_const::<0x0>())
}
}
}

#[test]
#[available_gas(2000000000)]
fn test_ar_discount() {
// setup
let (eth, pricing, identity, naming) = deploy();
let caller = contract_address_const::<0x123>();
set_contract_address(caller);
let id: u128 = 1;
let th0rgal: felt252 = 33133781693;

//we mint an id
identity.mint(id);

// we check how much a domain costs
let (_, price) = pricing.compute_buy_price(7, 365);

// we allow the naming to take our money
eth.approve(naming.contract_address, price);

// we buy with no resolver, no sponsor, no discount and empty metadata
naming
.buy(
id, th0rgal, 365, ContractAddressZeroable::zero(), ContractAddressZeroable::zero(), 0, 0
);

let auto_renewal = utils::deploy(
DummyAutoRenewal::TEST_CLASS_HASH, array![eth.contract_address.into()]
);

let current_expiry = naming.domain_to_expiry(array![th0rgal].span());

// we set the renewal contract and enable the discount
naming.whitelist_renewal_contract(auto_renewal);
naming.toggle_ar_discount_renew();
let (_, yearly_renewal_price) = pricing.compute_renew_price(7, 365);
eth.approve(auto_renewal, yearly_renewal_price);
let _allowance = eth.allowance(caller, auto_renewal);
naming.ar_discount_renew(th0rgal, auto_renewal);

// we don't set the auto renewal allowance in this test because we
// use a dummy contract which always return 1, theoretically we should
// set it to infinity (2**256-1)
let new_expiry = naming.domain_to_expiry(array![th0rgal].span());
assert(new_expiry - current_expiry == 86400 * 90, 'Invalid expiry');
}


#[test]
#[available_gas(2000000000)]
#[should_panic(expected: ('Discount disabled', 'ENTRYPOINT_FAILED'))]
fn test_ar_discount_not_enabled() {
// setup
let (eth, pricing, identity, naming) = deploy();
let caller = contract_address_const::<0x123>();
set_contract_address(caller);
let id: u128 = 1;
let th0rgal: felt252 = 33133781693;

//we mint an id
identity.mint(id);

// we check how much a domain costs
let (_, price) = pricing.compute_buy_price(7, 365);

// we allow the naming to take our money
eth.approve(naming.contract_address, price);

// we buy with no resolver, no sponsor, no discount and empty metadata
naming
.buy(
id, th0rgal, 365, ContractAddressZeroable::zero(), ContractAddressZeroable::zero(), 0, 0
);

let auto_renewal = utils::deploy(
DummyAutoRenewal::TEST_CLASS_HASH, array![eth.contract_address.into()]
);

// we set the renewal contract and enable the discount
Copy link
Collaborator

Choose a reason for hiding this comment

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

and we don't enable the discounts no ?

naming.whitelist_renewal_contract(auto_renewal);
//naming.toggle_ar_discount_renew();
let (_, yearly_renewal_price) = pricing.compute_renew_price(7, 365);
eth.approve(auto_renewal, yearly_renewal_price);
let _allowance = eth.allowance(caller, auto_renewal);
naming.ar_discount_renew(th0rgal, auto_renewal);
}


#[test]
#[available_gas(2000000000)]
#[should_panic(expected: ('Invalid ERC20 allowance', 'ENTRYPOINT_FAILED'))]
fn test_ar_discount_wrong_ar_allowance() {
// setup
let (eth, pricing, identity, naming) = deploy();
let caller = contract_address_const::<0x123>();
set_contract_address(caller);
let id: u128 = 1;
let th0rgal: felt252 = 33133781693;

//we mint an id
identity.mint(id);

// we check how much a domain costs
let (_, price) = pricing.compute_buy_price(7, 365);

// we allow the naming to take our money
eth.approve(naming.contract_address, price);

// we buy with no resolver, no sponsor, no discount and empty metadata
naming
.buy(
id, th0rgal, 365, ContractAddressZeroable::zero(), ContractAddressZeroable::zero(), 0, 0
);

let auto_renewal = utils::deploy(
DummyAutoRenewal::TEST_CLASS_HASH, array![eth.contract_address.into()]
);

let current_expiry = naming.domain_to_expiry(array![th0rgal].span());

// we set the renewal contract and enable the discount
naming.whitelist_renewal_contract(auto_renewal);
naming.toggle_ar_discount_renew();
let (_, _yearly_renewal_price) = pricing.compute_renew_price(7, 365);
//eth.approve(auto_renewal, yearly_renewal_price);
let _allowance = eth.allowance(caller, auto_renewal);
naming.ar_discount_renew(th0rgal, auto_renewal);

// we don't set the auto renewal allowance in this test because we
// use a dummy contract which always return 1, theoretically we should
// set it to infinity (2**256-1)
let new_expiry = naming.domain_to_expiry(array![th0rgal].span());
assert(new_expiry - current_expiry == 86400 * 90, 'Invalid expiry');
}
Loading