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

2024 Move Migration Syntax #74

Merged
merged 38 commits into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
607388f
reference update
leecchh Mar 28, 2024
fda792c
test changes
leecchh Mar 28, 2024
f633d8e
test function
leecchh Mar 28, 2024
f4bfa27
update
leecchh Mar 28, 2024
8b97910
coupons.move finished
leecchh Mar 28, 2024
46610e2
coupons migrated
leecchh Mar 28, 2024
5ba98a6
remove comments
leecchh Mar 28, 2024
4554736
update small syntax
leecchh Mar 28, 2024
3b7d82d
debug and update test
leecchh Mar 29, 2024
9213d95
coupon tests
leecchh Mar 29, 2024
28877dc
setup
leecchh Mar 29, 2024
c48b28c
denylist
leecchh Mar 29, 2024
0808d81
discounts
leecchh Mar 29, 2024
957f9da
managed_names
leecchh Mar 29, 2024
2ea7f2a
registration
leecchh Mar 29, 2024
eb74bff
renewal
leecchh Mar 29, 2024
653edbc
subdomains
leecchh Mar 29, 2024
eae1f9f
subdomain proxy
leecchh Mar 29, 2024
9e62b89
utils
leecchh Mar 29, 2024
5d30df4
part 1 suins
leecchh Apr 1, 2024
f3114d2
move part 2
leecchh Apr 1, 2024
f3486ac
migration tests update
leecchh Apr 1, 2024
96d6d60
remaining tests
leecchh Apr 1, 2024
b0a2def
dependency updates
leecchh Apr 2, 2024
273842e
update coupons
leecchh Apr 10, 2024
052eb9f
more syntax conversion
leecchh Apr 10, 2024
31f2779
borrow syntax additional
leecchh Apr 16, 2024
ec2fbad
final borrow changes
leecchh Apr 16, 2024
390eb23
cleanup use
leecchh Apr 16, 2024
e65e360
more cleanup
leecchh Apr 16, 2024
6fc7490
minor
leecchh Apr 16, 2024
348401c
nesting added
leecchh Apr 16, 2024
cd9fba0
formatting
leecchh Apr 16, 2024
00a5e1f
update
leecchh Apr 16, 2024
ebe129c
coupon update
leecchh Apr 16, 2024
349ffe2
rules update
leecchh Apr 16, 2024
e324db7
update
leecchh Apr 16, 2024
5b90844
discounts
leecchh Apr 16, 2024
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
65 changes: 26 additions & 39 deletions packages/coupons/sources/coupons.move
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,12 @@
/// Each coupon is validated towards a list of rules. View `rules` module for explanation.
/// The app is authorized on `SuiNS` to be able to claim names and add earnings to the registry.
module coupons::coupons {
use std::string::{Self, String};
use std::string::String;

use sui::table::{Self, Table};
use sui::tx_context::{TxContext, sender};
use sui::object::{Self, UID};
use sui::transfer;
use sui::dynamic_field::{Self as df};
use sui::clock::Clock;
use sui::sui::SUI;
use sui::coin::{Self, Coin};
use sui::{table::{Self, Table}, dynamic_field::{Self as df}, clock::Clock, sui::SUI, coin::Coin};

use coupons::rules::{Self, CouponRules};
use coupons::constants;
use coupons::{rules::{Self, CouponRules}, constants};
use suins::{domain, suins::{Self, AdminCap, SuiNS}, suins_registration::SuinsRegistration, config::Self, registry::Registry};

/// Coupon already exists
const ECouponAlreadyExists: u64 = 0;
Expand All @@ -41,12 +34,6 @@ module coupons::coupons {
/// Our versioning of the coupons package.
const VERSION: u8 = 1;

// use suins::config;
use suins::domain;
use suins::suins::{Self, AdminCap, SuiNS}; // re-use AdminCap for creating new coupons.
use suins::suins_registration::SuinsRegistration;
use suins::config::{Self, Config};
use suins::registry::{Self, Registry};

// Authorization for the Coupons on SuiNS, to be able to register names on the app.
public struct CouponsApp has drop {}
Expand Down Expand Up @@ -99,65 +86,65 @@ module coupons::coupons {
clock: &Clock,
ctx: &mut TxContext
): SuinsRegistration {
assert_version_is_valid(self);
self.assert_version_is_valid();
// Validate that specified coupon is valid.
assert!(table::contains(&mut self.data.coupons, coupon_code), ECouponNotExists);
assert!(self.data.coupons.contains(coupon_code), ECouponNotExists);

// Verify coupon house is authorized to buy names.
suins::assert_app_is_authorized<CouponsApp>(suins);
suins.assert_app_is_authorized<CouponsApp>();

// Validate registration years are in [0,5] range.
assert!(no_years > 0 && no_years <= 5, EInvalidYearsArgument);

let config = suins::get_config<Config>(suins);
let config = suins.get_config();
let domain = domain::new(domain_name);
let label = domain::sld(&domain);
let label = domain.sld();

let domain_length = (string::length(label) as u8);
let domain_length = (label.length() as u8);

// Borrow coupon from the table.
let coupon = table::borrow_mut(&mut self.data.coupons, coupon_code);
let coupon = &mut self.data.coupons[coupon_code];

// We need to do a total of 5 checks, based on `CouponRules`
// Our checks work with `AND`, all of the conditions must pass for a coupon to be used.
// 1. Validate domain size.
rules::assert_coupon_valid_for_domain_size(&coupon.rules, domain_length);
coupon.rules.assert_coupon_valid_for_domain_size(domain_length);
// 2. Decrease available claims. Will ABORT if the coupon doesn't have enough available claims.
rules::decrease_available_claims(&mut coupon.rules);
coupon.rules.decrease_available_claims();
// 3. Validate the coupon is valid for the specified user.
rules::assert_coupon_valid_for_address(&coupon.rules, sender(ctx));
coupon.rules.assert_coupon_valid_for_address(ctx.sender());
// 4. Validate the coupon hasn't expired (Based on clock)
rules::assert_coupon_is_not_expired(&coupon.rules, clock);
coupon.rules.assert_coupon_is_not_expired(clock);
// 5. Validate years are valid for the coupon.
rules::assert_coupon_valid_for_domain_years(&coupon.rules, no_years);
coupon.rules.assert_coupon_valid_for_domain_years(no_years);

// Validate name can be registered (is main domain (no subdomain) and length is valid)
config::assert_valid_user_registerable_domain(&domain);

let original_price = config::calculate_price(config, domain_length, no_years);
let sale_price = internal_calculate_sale_price(original_price, coupon);

assert!(coin::value(&payment) == sale_price, EIncorrectAmount);
suins::app_add_balance(CouponsApp {}, suins, coin::into_balance(payment));
assert!(payment.value() == sale_price, EIncorrectAmount);
suins::app_add_balance(CouponsApp {}, suins, payment.into_balance());

// Clean up our registry by removing the coupon if no more available claims!
if(!rules::has_available_claims(&coupon.rules)){
if(!coupon.rules.has_available_claims()){
// remove the coupon, since it's no longer usable.
internal_remove_coupon(&mut self.data, coupon_code);
self.data.internal_remove_coupon(coupon_code);
};

let registry = suins::app_registry_mut<CouponsApp, Registry>(CouponsApp {}, suins);
registry::add_record(registry, domain, no_years, clock, ctx)
registry.add_record(domain, no_years, clock, ctx)
}

// A convenient helper to calculate the price in a PTB.
// Important: This function doesn't check the validity of the coupon (Whether the user can indeed use it)
// Nor does it calculate the original price. This is part of the Frontend anyways.
public fun calculate_sale_price(self: &mut CouponHouse, price: u64, coupon_code: String): u64 {
// Validate that specified coupon is valid.
assert!(table::contains(&mut self.data.coupons, coupon_code), ECouponNotExists);
assert!(self.data.coupons.contains(coupon_code), ECouponNotExists);
// Borrow coupon from the table.
let coupon = table::borrow_mut(&mut self.data.coupons, coupon_code);
let coupon = &mut self.data.coupons[coupon_code];
internal_calculate_sale_price(price, coupon)
}

Expand Down Expand Up @@ -262,8 +249,8 @@ module coupons::coupons {
code: String,
coupon: Coupon
) {
assert!(!table::contains(&mut self.coupons, code), ECouponAlreadyExists);
table::add(&mut self.coupons, code, coupon);
assert!(!self.coupons.contains(code), ECouponAlreadyExists);
self.coupons.add(code, coupon);
}

/// An internal function to create a coupon object.
Expand All @@ -282,7 +269,7 @@ module coupons::coupons {

// A function to remove a coupon from the system.
fun internal_remove_coupon(self: &mut Data, code: String) {
table::remove(&mut self.coupons, code);
self.coupons.remove(code);
}

// test only functions.
Expand Down
6 changes: 2 additions & 4 deletions packages/coupons/sources/range.move
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
/// A module to introduce `range` checks for the rules.
module coupons::range {

use std::vector;

/// Invalid [from, to] setup in the range!
/// `to` parameter has to be >= `from`
const EInvalidRange: u64 = 0;
Expand All @@ -31,12 +29,12 @@ module coupons::range {

/// Get floor limit for the range.
public fun from(range: &Range): u8 {
*vector::borrow(&range.vec, 0)
range.vec[0]
}

/// Get upper limit for the range.
public fun to(range: &Range): u8 {
*vector::borrow(&range.vec, 1)
range.vec[1]
}

}
51 changes: 23 additions & 28 deletions packages/coupons/sources/rules.move
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,9 @@
// A module with a couple of helpers for validation of coupons
// validation of names etc.
module coupons::rules {
use sui::clock::Clock;

use std::vector;
use std::option::{Self, Option};

use sui::clock::{Self, Clock};

use coupons::constants;
use coupons::range::{Self, Range};
use coupons::{constants, range::Range};

use suins::constants::{Self as suins_constants};
// Errors
Expand Down Expand Up @@ -64,7 +59,7 @@ module coupons::rules {
): CouponRules {
assert!(is_valid_years_range(&years), EInvalidYears);
assert!(is_valid_length_range(&length), EInvalidLengthRule);
assert!(option::is_none(&available_claims) || (*option::borrow(&available_claims) > 0), EInvalidAvailableClaims);
assert!(available_claims.is_none() || (*available_claims.borrow() > 0), EInvalidAvailableClaims);
CouponRules {
length, available_claims, user, expiration, years
}
Expand All @@ -87,19 +82,19 @@ module coupons::rules {
/// We shouldn't get here ever, as we're checking this on the coupon creation, but
/// keeping it as a sanity check (e.g. created a coupon with 0 available claims).
public fun decrease_available_claims(rules: &mut CouponRules) {
if(option::is_some(&rules.available_claims)){
if(rules.available_claims.is_some()){
assert!(has_available_claims(rules), ENoMoreAvailableClaims);
// Decrease available claims by 1.
let available_claims = *option::borrow(&rules.available_claims);
option::swap(&mut rules.available_claims, available_claims - 1);
let available_claims = *rules.available_claims.borrow();
rules.available_claims.swap(available_claims - 1);
}
}

// Checks whether a coupon has available claims.
// Returns true if the rule is not set OR it has used all the available claims.
public fun has_available_claims(rules: &CouponRules): bool {
if(option::is_none(&rules.available_claims)) return true;
*option::borrow(&rules.available_claims) > 0
if(rules.available_claims.is_none()) return true;
*rules.available_claims.borrow() > 0
}

// Assertion helper for the validity of years.
Expand All @@ -113,13 +108,13 @@ module coupons::rules {
// 1. Exact years (e.g. 2 years, by passing [2,2])
// 2. Range of years (e.g. [1,3])
public fun is_coupon_valid_for_domain_years(rules: &CouponRules, target: u8): bool {
if(option::is_none(&rules.years)) return true;
if(rules.years.is_none()) return true;

range::is_in_range(option::borrow(&rules.years), target)
rules.years.borrow().is_in_range(target)
}

public fun assert_is_valid_discount_type(`type`: u8) {
assert!(vector::contains(&constants::discount_rule_types(), &`type`), EInvalidType);
assert!(constants::discount_rule_types().contains(&`type`), EInvalidType);
}

// verify that we are creating the coupons correctly (based on amount & type).
Expand All @@ -139,9 +134,9 @@ module coupons::rules {
/// We check the length of the name based on the domain length rule
public fun is_coupon_valid_for_domain_size(rules: &CouponRules, length: u8): bool {
// If the vec is not set, we pass this rule test.
if(option::is_none(&rules.length)) return true;
if(rules.length.is_none()) return true;

range::is_in_range(option::borrow(&rules.length), length)
rules.length.borrow().is_in_range(length)
}


Expand All @@ -152,8 +147,8 @@ module coupons::rules {
}
/// Check that the domain is valid for the specified address
public fun is_coupon_valid_for_address(rules: &CouponRules, user: address): bool {
if(option::is_none(&rules.user)) return true;
*option::borrow(&rules.user) == user
if(rules.user.is_none()) return true;
rules.user.borrow() == user
}

/// Simple assertion for the coupon expiration.
Expand All @@ -164,23 +159,23 @@ module coupons::rules {

/// Check whether a coupon has expired
public fun is_coupon_expired(rules: &CouponRules, clock: &Clock): bool {
if(option::is_none(&rules.expiration)){
if(rules.expiration.is_none()){
return false
};

clock::timestamp_ms(clock) > *option::borrow(&rules.expiration)
clock.timestamp_ms() > *rules.expiration.borrow()
}


fun is_valid_years_range(range: &Option<Range>): bool {
if(option::is_none(range)) return true;
let range = option::borrow(range);
range::from(range) >= 1 && range::to(range) <= 5
if(range.is_none()) return true;
let range = range.borrow();
range.from() >= 1 && range.to() <= 5
}

fun is_valid_length_range(range: &Option<Range>): bool {
if(option::is_none(range)) return true;
let range = option::borrow(range);
range::from(range) >= suins_constants::min_domain_length() && range::to(range) <= suins_constants::max_domain_length()
if(range.is_none()) return true;
let range = range.borrow();
range.from() >= suins_constants::min_domain_length() && range.to() <= suins_constants::max_domain_length()
}
}
60 changes: 34 additions & 26 deletions packages/coupons/tests/authorization_tests.move
Original file line number Diff line number Diff line change
Expand Up @@ -5,61 +5,69 @@
#[test_only]
module coupons::app_authorization_tests {

// use std::string::{utf8, String};
use sui::test_scenario::{Self};
use sui::test_scenario::{return_shared, return_to_sender, end};

use coupons::coupons::{Self, CouponHouse};
use suins::suins::{AdminCap};
use coupons::setup::{Self, UnauthorizedTestApp, TestApp, admin, user};
use coupons::{
coupons::{app_data_mut, deauthorize_app},
setup::{
Self,
UnauthorizedTestApp,
TestApp,
admin,
user,
test_init,
unauthorized_test_app
}
};

#[test]
fun admin_get_app_success() {
let mut scenario_val = setup::test_init();
let mut scenario_val = test_init();
let scenario = &mut scenario_val;
// auth style as authorized app
{
test_scenario::next_tx(scenario, user());
let mut coupon_house = test_scenario::take_shared<CouponHouse>(scenario);
coupons::app_data_mut<TestApp>(setup::test_app(), &mut coupon_house);
test_scenario::return_shared(coupon_house);
scenario.next_tx(user());
let mut coupon_house = scenario.take_shared();
app_data_mut<TestApp>(setup::test_app(), &mut coupon_house);
return_shared(coupon_house);
};

test_scenario::end(scenario_val);
end(scenario_val);
}

#[test]
fun authorized_app_get_app_success(){
let mut scenario_val = setup::test_init();
let mut scenario_val = test_init();
let scenario = &mut scenario_val;
{
test_scenario::next_tx(scenario, admin());
scenario.next_tx(admin());

let mut coupon_house = test_scenario::take_shared<CouponHouse>(scenario);
let admin_cap = test_scenario::take_from_sender<AdminCap>(scenario);
let mut coupon_house = scenario.take_shared();
let admin_cap = scenario.take_from_sender();

// test app deauthorization.
coupons::deauthorize_app<TestApp>(&admin_cap, &mut coupon_house);
deauthorize_app<TestApp>(&admin_cap, &mut coupon_house);

// test that the app is indeed non authorized
assert!(!coupons::is_app_authorized<TestApp>(&coupon_house), 0);
assert!(!coupon_house.is_app_authorized<TestApp>(), 0);

test_scenario::return_to_sender(scenario, admin_cap);
test_scenario::return_shared(coupon_house);
return_to_sender(scenario, admin_cap);
return_shared(coupon_house);
};
test_scenario::end(scenario_val);
end(scenario_val);
}

#[test, expected_failure(abort_code=::coupons::coupons::EAppNotAuthorized)]
fun unauthorized_app_failure() {
let mut scenario_val = setup::test_init();
let mut scenario_val = test_init();
let scenario = &mut scenario_val;
{
test_scenario::next_tx(scenario, user());
let mut coupon_house = test_scenario::take_shared<CouponHouse>(scenario);
coupons::app_data_mut<UnauthorizedTestApp>(setup::unauthorized_test_app(), &mut coupon_house);
test_scenario::return_shared(coupon_house);
scenario.next_tx(user());
let mut coupon_house = scenario.take_shared();
app_data_mut<UnauthorizedTestApp>(unauthorized_test_app(), &mut coupon_house);
return_shared(coupon_house);
};
test_scenario::end(scenario_val);
end(scenario_val);
}

}
Loading
Loading