Skip to content

Commit

Permalink
pricing and cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
leecchh committed Oct 21, 2024
1 parent 425b705 commit 75f4cf4
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 17 deletions.
19 changes: 10 additions & 9 deletions packages/suins/sources/auction.move
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@ module suins::auction {
use std::{option::{none, some, is_some}, string::String};

use sui::{
balance::{Self, Balance},
coin::{Self, Coin},
clock::Clock,
event,
sui::SUI,
balance::{Self, Balance},
coin::{Self, Coin},
clock::Clock,
event,
sui::SUI,
linked_table::{Self, LinkedTable}
};

use suins::{
config::{Self, Config},
suins::{Self, AdminCap, SuiNS},
suins_registration::SuinsRegistration,
registry::Registry,
config::{Self, Config},
suins::{Self, AdminCap, SuiNS},
suins_registration::SuinsRegistration,
registry::Registry,
domain::{Self, Domain}
};

Expand Down Expand Up @@ -56,6 +56,7 @@ module suins::auction {
}

/// The Auction application.
#[allow(lint(coin_field))]
public struct Auction has store {
domain: Domain,
start_timestamp_ms: u64,
Expand Down
2 changes: 1 addition & 1 deletion packages/suins/sources/controller.move
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ module suins::controller {
let domain = nft.domain();

registry.assert_nft_is_authorized(nft, clock);
let key_bytes = *key.bytes();
let key_bytes = *key.as_bytes();
assert!(key_bytes == AVATAR || key_bytes == CONTENT_HASH, EUnsupportedKey);

if (data.contains(&key)) {
Expand Down
14 changes: 7 additions & 7 deletions packages/suins/sources/domain.move
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ module suins::domain {
number_of_levels(domain) > 2
}

/// Derive the parent of a subdomain.
/// e.g. `subdomain.example.sui` -> `example.sui`
/// Derive the parent of a subdomain.
/// e.g. `subdomain.example.sui` -> `example.sui`
public fun parent(domain: &Domain): Domain {
let mut labels = domain.labels;
// we pop the last element and construct the parent from the remaining labels.
Expand All @@ -104,10 +104,10 @@ module suins::domain {
labels
}
}

/// Checks if `parent` domain is a valid parent for `child`.
public fun is_parent_of(parent: &Domain, child: &Domain): bool {
number_of_levels(parent) < number_of_levels(child) &&
number_of_levels(parent) < number_of_levels(child) &&
&parent(child).labels == &parent.labels
}

Expand All @@ -126,7 +126,7 @@ module suins::domain {

fun is_valid_label(label: &String): bool {
let len = label.length();
let label_bytes = label.bytes();
let label_bytes = label.as_bytes();
let mut index = 0;

if (!(len >= MIN_LABEL_LENGTH && len <= MAX_LABEL_LENGTH)) {
Expand Down Expand Up @@ -156,7 +156,7 @@ module suins::domain {
let mut parts: vector<String> = vector[];
while (!s.is_empty()) {
let index_of_next_dot = s.index_of(&dot);
let part = s.sub_string(0, index_of_next_dot);
let part = s.substring(0, index_of_next_dot);
parts.push_back(part);

let len = s.length();
Expand All @@ -166,7 +166,7 @@ module suins::domain {
index_of_next_dot + 1
};

s = s.sub_string(start_of_next_part, len);
s = s.substring(start_of_next_part, len);
};

parts
Expand Down
62 changes: 62 additions & 0 deletions packages/suins/sources/pricing.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
module suins::pricing {
use sui::vec_map::{Self, VecMap};

// Tries to create a range with more than 2 values
const EInvalidLength: u64 = 1;
/// Tries to create a range with the first value greater than the second
const EInvalidRange: u64 = 2;
/// Tries to create a pricing config with different lengths for ranges and prices
const ELengthMissmatch: u64 = 3;
/// Tries to calculate the price for a given length
const EPriceNotSet: u64 = 4;

/// A range struct that holds the start and end of a range (inclusive).
public struct Range(u64, u64) has copy, store, drop;

/// A struct that holds the length range and the price of a service.
public struct PricingConfig<phantom T> has copy, store, drop {
pricing: VecMap<Range, u64>,
}

/// Calculates the price for a given length.
/// Aborts with EPriceNotSet if the price for the given length is not set.
public fun calculate_price<T>(config: &PricingConfig<T>, length: u64): u64 {
let keys = config.pricing.keys();
let mut idx = keys.find_index!(|range| range.0 <= length && range.1 >= length);

assert!(idx.is_some(), EPriceNotSet);
let range = keys[idx.extract()];

*config.pricing.get(&range)
}

/// Creates a new PricingConfig with the given ranges and prices.
/// - The ranges should be sorted in `ascending order` and should not overlap.
/// - The length of the ranges and prices should be the same.
///
/// All the ranges are inclusive (e.g. [3,5]: includes 3, 4, and 5).
public fun new<T>(ranges: vector<Range>, prices: vector<u64>): PricingConfig<T> {
assert!(ranges.length() == prices.length(), ELengthMissmatch);
// Validate that our ranges are passed in the correct order
// we expect them to be sorted in ascending order, and we expect them
// to not have any overlaps.
let mut i = 1;

while (i < ranges.length()) {
assert!(ranges[i - 1].1 < ranges[i].0, EInvalidRange);
i = i + 1;
};

// let sorted = ranges.
PricingConfig {
pricing: vec_map::from_keys_values(ranges, prices)
}
}

public fun new_range(range: vector<u64>): Range {
assert!(range.length() == 2, EInvalidLength);
assert!(range[0] <= range[1], EInvalidRange);

Range(range[0], range[1])
}
}
104 changes: 104 additions & 0 deletions packages/suins/tests/pricing_tests.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
module suins::pricing_tests {
use sui::{sui::SUI, coin::Coin};
use suins::pricing;

#[test]
fun test_e2e() {
let ranges = vector[
pricing::new_range(vector[1, 10]),
pricing::new_range(vector[11, 20]),
pricing::new_range(vector[21, 30]),
pricing::new_range(vector[31, 31])
];

let pricing_config = pricing::new<Coin<SUI>>(ranges, vector[10, 20, 30, 45]);

// test internal values
assert!(pricing_config.calculate_price(5) == 10);
assert!(pricing_config.calculate_price(15) == 20);
assert!(pricing_config.calculate_price(25) == 30);

// test upper bounds
assert!(pricing_config.calculate_price(10) == 10);
assert!(pricing_config.calculate_price(20) == 20);
assert!(pricing_config.calculate_price(30) == 30);

// test lower bounds
assert!(pricing_config.calculate_price(1) == 10);
assert!(pricing_config.calculate_price(11) == 20);
assert!(pricing_config.calculate_price(21) == 30);

// single length pricing
assert!(pricing_config.calculate_price(31) == 45);
}

#[test, expected_failure(abort_code = ::suins::pricing::EInvalidRange)]
fun test_range_overlap_1() {
let ranges = vector[
pricing::new_range(vector[1, 10]),
pricing::new_range(vector[9, 20])
];

pricing::new<Coin<SUI>>(ranges, vector[10, 20]);
}

#[test, expected_failure(abort_code = ::suins::pricing::EInvalidRange)]
fun test_range_overlap_2() {
let ranges = vector[
pricing::new_range(vector[1, 10]),
pricing::new_range(vector[10, 20])
];

pricing::new<Coin<SUI>>(ranges, vector[10, 20]);
}

#[test, expected_failure(abort_code = ::suins::pricing::EInvalidRange)]
fun test_range_overlap_3() {
let ranges = vector[
pricing::new_range(vector[1, 10]),
pricing::new_range(vector[21, 30]),
pricing::new_range(vector[11, 20])
];

pricing::new<Coin<SUI>>(ranges, vector[10, 20, 30]);
}

#[test, expected_failure(abort_code = ::suins::pricing::EInvalidRange)]
fun test_range_overlap_4() {
let ranges = vector[
pricing::new_range(vector[20, 30]),
pricing::new_range(vector[30, 40]),
pricing::new_range(vector[40, 50])
];

pricing::new<Coin<SUI>>(ranges, vector[10, 20, 30]);
}

#[test, expected_failure(abort_code = ::suins::pricing::ELengthMissmatch)]
fun test_length_missmatch() {
let ranges = vector[
pricing::new_range(vector[10, 20]),
];

pricing::new<Coin<SUI>>(ranges, vector[10, 20]);
}

#[test, expected_failure(abort_code = ::suins::pricing::EInvalidLength)]
fun test_range_construction_too_long() {
pricing::new_range(vector[10, 20, 30]);
}

#[test, expected_failure(abort_code = ::suins::pricing::EInvalidRange)]
fun test_invalid_range_construction() {
pricing::new_range(vector[20, 10]);
}

#[test, expected_failure(abort_code = ::suins::pricing::EPriceNotSet)]
fun test_price_not_set() {
let ranges = vector[pricing::new_range(vector[1, 10])];

let pricing = pricing::new<Coin<SUI>>(ranges, vector[10]);

pricing.calculate_price(20);
}
}

0 comments on commit 75f4cf4

Please sign in to comment.