Skip to content

Commit

Permalink
refactor: pass domain length to pricing to avoid computing it twice
Browse files Browse the repository at this point in the history
  • Loading branch information
Th0rgal committed Aug 24, 2023
1 parent 5909e19 commit 452b8a6
Show file tree
Hide file tree
Showing 8 changed files with 143 additions and 99 deletions.
1 change: 1 addition & 0 deletions src/interface/naming.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ trait INaming<TContractState> {
days: u16,
resolver: ContractAddress,
sponsor: ContractAddress,
discount_id: felt252,
metadata: felt252,
);

Expand Down
4 changes: 2 additions & 2 deletions src/interface/pricing.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ use starknet::ContractAddress;
#[starknet::interface]
trait IPricing<TContractState> {
fn compute_buy_price(
self: @TContractState, domain: felt252, days: u16
self: @TContractState, domain_len: usize, days: u16
) -> (ContractAddress, u256);

fn compute_renew_price(
self: @TContractState, domain: felt252, days: u16
self: @TContractState, domain_len: usize, days: u16
) -> (ContractAddress, u256);
}
54 changes: 51 additions & 3 deletions src/naming/main.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,17 @@ mod Naming {
parent_key: u32, // key of parent domain
}

#[derive(Copy, Drop, Serde, starknet::Store)]
struct Discount {
domain_len_range: (usize, usize),
days_range: (u16, u16),
timestamp_range: (u64, u64),
amount: felt252, // this is an amount in percentages
}

#[storage]
struct Storage {
discounts: LegacyMap<felt252, Discount>,
starknetid_contract: ContractAddress,
_pricing_contract: ContractAddress,
_referral_contract: ContractAddress,
Expand Down Expand Up @@ -172,10 +181,11 @@ mod Naming {
days: u16,
resolver: ContractAddress,
sponsor: ContractAddress,
discount_id: felt252,
metadata: felt252,
) {
let (hashed_domain, now, expiry) = self.assert_purchase_is_possible(id, domain, days);
self.pay_buy_domain(now, days, domain, sponsor);
self.pay_buy_domain(now, days, domain, sponsor, discount_id);
self.emit(Event::SaleMetadata(SaleMetadata { domain, metadata }));
self.mint_domain(expiry, resolver, hashed_domain, id, domain);
}
Expand Down Expand Up @@ -385,12 +395,32 @@ mod Naming {
}

fn pay_buy_domain(
self: @ContractState, now: u64, days: u16, domain: felt252, sponsor: ContractAddress
self: @ContractState,
now: u64,
days: u16,
domain: felt252,
sponsor: ContractAddress,
discount_id: felt252
) -> () {
// we need a u256 to be able to perform safe divisions
let domain_len = self.get_chars_len(domain.into());

// find domain cost
let (erc20, price) = IPricingDispatcher {
contract_address: self._pricing_contract.read()
}.compute_buy_price(domain, days);
}.compute_buy_price(domain_len, days);

// check the discount
if (discount_id != 0) { // let discount = self.discounts.read(discount_id);
// assert(
// discount.days_range.0 <= days && days <= discount.days_range.1,
// 'days out of discount range'
// );
// assert(
// discount.timestamp_range.0 <= now && now <= discount.timestamp_range.1,
// 'time out of discount range'
// );
}

// pay the price
IERC20Dispatcher {
Expand Down Expand Up @@ -440,5 +470,23 @@ mod Naming {
);
}
}

fn get_chars_len(self: @ContractState, domain: u256) -> usize {
if domain == (u256 { low: 0, high: 0 }) {
return 0;
}
// 38 = simple_alphabet_size
let (p, q, _) = u256_safe_divmod(domain, u256_as_non_zero(u256 { low: 38, high: 0 }));
if q == (u256 { low: 37, high: 0 }) {
// 3 = complex_alphabet_size
let (shifted_p, _, _) = u256_safe_divmod(
p, u256_as_non_zero(u256 { low: 2, high: 0 })
);
let next = self.get_chars_len(shifted_p);
return 1 + next;
}
let next = self.get_chars_len(p);
1 + next
}
}
}
46 changes: 17 additions & 29 deletions src/pricing.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -22,54 +22,42 @@ mod Pricing {
#[external(v0)]
impl PricingImpl of IPricing<ContractState> {
fn compute_buy_price(
self: @ContractState, domain: felt252, days: u16
self: @ContractState, domain_len: usize, days: u16
) -> (ContractAddress, u256) {
(self.erc20.read(), u256 { low: self.get_price_per_day(domain) * days.into(), high: 0 })
(
self.erc20.read(), u256 {
low: self.get_price_per_day(domain_len) * days.into(), high: 0
}
)
}

fn compute_renew_price(
self: @ContractState, domain: felt252, days: u16
self: @ContractState, domain_len: usize, days: u16
) -> (ContractAddress, u256) {
(self.erc20.read(), u256 { low: self.get_price_per_day(domain) * days.into(), high: 0 })
(
self.erc20.read(), u256 {
low: self.get_price_per_day(domain_len) * days.into(), high: 0
}
)
}
}

#[generate_trait]
impl InternalImpl of InternalTrait {
fn get_amount_of_chars(self: @ContractState, domain: u256) -> u128 {
if domain == (u256 { low: 0, high: 0 }) {
return 0;
}
// 38 = simple_alphabet_size
let (p, q, _) = u256_safe_divmod(domain, u256_as_non_zero(u256 { low: 38, high: 0 }));
if q == (u256 { low: 37, high: 0 }) {
// 3 = complex_alphabet_size
let (shifted_p, _, _) = u256_safe_divmod(
p, u256_as_non_zero(u256 { low: 2, high: 0 })
);
let next = self.get_amount_of_chars(shifted_p);
return 1 + next;
}
let next = self.get_amount_of_chars(p);
1 + next
}

fn get_price_per_day(self: @ContractState, domain: felt252) -> u128 {
let number_of_character = self.get_amount_of_chars(domain.into());

if number_of_character == 1 {
fn get_price_per_day(self: @ContractState, domain_len: usize) -> u128 {
if domain_len == 1 {
return 1068493150684932;
}

if number_of_character == 2 {
if domain_len == 2 {
return 657534246575343;
}

if number_of_character == 3 {
if domain_len == 3 {
return 200000000000000;
}

if number_of_character == 4 {
if domain_len == 4 {
return 73972602739726;
}

Expand Down
24 changes: 13 additions & 11 deletions src/tests/naming/test_abuses.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,16 @@ fn test_not_enough_eth() {
identity.mint(id);

// we check how much a domain costs
let (_, price) = pricing.compute_buy_price(th0rgal, 365);
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 and empty metadata (and also no money)
// we buy with no resolver, no sponsor, no discount and empty metadata (and also no money)
naming
.buy(id, th0rgal, 365, ContractAddressZeroable::zero(), ContractAddressZeroable::zero(), 0);
.buy(
id, th0rgal, 365, ContractAddressZeroable::zero(), ContractAddressZeroable::zero(), 0, 0
);
}


Expand All @@ -67,21 +69,21 @@ fn test_buying_domain_twice() {
identity.mint(id2);

// we check how much a domain costs
let (_, price) = pricing.compute_buy_price(th0rgal, 365);
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 and empty metadata
// we buy with no resolver, no sponsor, no discount and empty metadata
naming
.buy(
id1, th0rgal, 365, ContractAddressZeroable::zero(), ContractAddressZeroable::zero(), 0
id1, th0rgal, 365, ContractAddressZeroable::zero(), ContractAddressZeroable::zero(), 0, 0
);

// buying again
naming
.buy(
id2, th0rgal, 365, ContractAddressZeroable::zero(), ContractAddressZeroable::zero(), 0
id2, th0rgal, 365, ContractAddressZeroable::zero(), ContractAddressZeroable::zero(), 0, 0
);
}

Expand All @@ -103,17 +105,17 @@ fn test_buying_twice_on_same_id() {
identity.mint(id);

// we check how much a domain costs
let (_, price) = pricing.compute_buy_price(th0rgal, 365);
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 and empty metadata
// we buy with no resolver, no sponsor, no discount and empty metadata
naming
.buy(id, th0rgal, 365, ContractAddressZeroable::zero(), ContractAddressZeroable::zero(), 0);
.buy(id, th0rgal, 365, ContractAddressZeroable::zero(), ContractAddressZeroable::zero(), 0, 0);
naming
.buy(
id, altdomain, 365, ContractAddressZeroable::zero(), ContractAddressZeroable::zero(), 0
id, altdomain, 365, ContractAddressZeroable::zero(), ContractAddressZeroable::zero(), 0, 0
);
}

Expand Down
53 changes: 47 additions & 6 deletions src/tests/naming/test_features.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,21 @@ fn test_subdomains() {
identity.mint(id1);

// we check how much a domain costs
let (_, price) = pricing.compute_buy_price(th0rgal, 365);
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 and empty metadata
// we buy with no resolver, no sponsor, no discount and empty metadata
naming
.buy(
id1, th0rgal, 365, ContractAddressZeroable::zero(), ContractAddressZeroable::zero(), 0
id1,
th0rgal,
365,
ContractAddressZeroable::zero(),
ContractAddressZeroable::zero(),
0,
0
);

let subdomain = array![hello, th0rgal].span();
Expand Down Expand Up @@ -75,18 +81,53 @@ fn test_claim_balance() {
identity.mint(id);

// we check how much a domain costs
let (_, price) = pricing.compute_buy_price(th0rgal, 365);
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 and empty metadata
// we buy with no resolver, no sponsor, no discount and empty metadata
naming
.buy(id, th0rgal, 365, ContractAddressZeroable::zero(), ContractAddressZeroable::zero(), 0);
.buy(
id, th0rgal, 365, ContractAddressZeroable::zero(), ContractAddressZeroable::zero(), 0, 0
);

let contract_bal = eth.balance_of(naming.contract_address);
let admin_balance = eth.balance_of(caller);
assert(contract_bal == price, 'naming has wrong balance');
naming.claim_balance(eth.contract_address);
assert(admin_balance + price == eth.balance_of(caller), 'balance didn\'t increase');
}


#[cfg(test)]
#[test]
#[available_gas(200000000000)]
fn test_get_chars_len() {
let mut unsafe_state = Naming::unsafe_new_contract_state();

// Should return 0 (empty string)
assert(Naming::InternalImpl::get_chars_len(@unsafe_state, 0) == 0, 'Should return 0');

// Should return 2 (be)
assert(Naming::InternalImpl::get_chars_len(@unsafe_state, 153) == 2, 'Should return 0');

// Should return 4 ("toto")
assert(Naming::InternalImpl::get_chars_len(@unsafe_state, 796195) == 4, 'Should return 4');

// Should return 5 ("aloha")
assert(Naming::InternalImpl::get_chars_len(@unsafe_state, 77554770) == 5, 'Should return 5');

// Should return 9 ("chocolate")
assert(
Naming::InternalImpl::get_chars_len(@unsafe_state, 19565965532212) == 9, 'Should return 9'
);

// Should return 30 ("这来abcdefghijklmopqrstuvwyq1234")
assert(
Naming::InternalImpl::get_chars_len(
@unsafe_state, 801855144733576077820330221438165587969903898313
) == 30,
'Should return 30'
);
}
8 changes: 5 additions & 3 deletions src/tests/naming/test_usecases.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,16 @@ fn test_basic_usage() {
identity.mint(id);

// we check how much a domain costs
let (_, price) = pricing.compute_buy_price(th0rgal, 365);
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 and empty metadata
// we buy with no resolver, no sponsor, no discount and empty metadata
naming
.buy(id, th0rgal, 365, ContractAddressZeroable::zero(), ContractAddressZeroable::zero(), 0);
.buy(
id, th0rgal, 365, ContractAddressZeroable::zero(), ContractAddressZeroable::zero(), 0, 0
);

// let's try the resolving
let domain = array![th0rgal].span();
Expand Down
Loading

0 comments on commit 452b8a6

Please sign in to comment.