Sui Move 实现一个简单的票务系统 —— 做自己的掌权者,任何人都可以创立独属于自己管理的服务机构,并通过票务订单的形式发售各式各样的服务内容......
做自己的掌权者,任何人都可以创立独属于自己管理的服务机构,并通过票务订单的形式发售各式各样的服务内容,与此同时,系统还提供套票管理,管理人员可以根据需求创建套餐,修改其中包含的服务或者套餐优惠价格,购买者后续则通过票务订单来享受权益服务,更细化的功能如下:
整个票务系统的发布者标识由 $\mathit {One}$-$\mathit {Time}$-$\mathit {Witness}$ 来生成,这需要一个与模块同名但全大写的结构:
public struct ADMIN has drop {}
这个一个共享的对象,其中存储了所有服务机构的相关信息,任何操作都需要通过它作为入口进行:
public struct OrganizationList has key {
id: UID,
organizations: Table<ID, ServiceOrganization>,
organizations_ids: vector<ID>,
publisher_income: Balance<SUI>,
}
用来标识一所服务机构的所有者,如果说,根据机构的 $\mathit {ID}$ 就可以借助上面的对象找到对应的服务机构,那么这个对象的拥有者才是真正有权限修改这所机构中提供的服务内容的人,它只需要做标识用,不需要存储其它任何东西:
public struct ServiceOrganizationCap has key {
id: UID,
}
具体的服务机构对象,它被存储在OrganizationList
当中,其中最关键的东西是如何维护单项服务以及服务套餐:
public struct ServiceOrganization has store {
organization_name: String,
services: Table<ID, Service>,
services_ids: vector<ID>,
package_services: Table<ID, PackageServices>,
package_services_ids: vector<ID>,
income: Balance<SUI>,
}
单项服务,只需要服务名称和其价格:
public struct Service has store, drop {
service_name: String,
price: u64,
}
服务套餐,除了价格之外,还需要套餐内包含的所有服务的 $\mathit {ID}$:
public struct PackageServices has store {
services: vector<ID>,
price: u64,
}
顾客购买了单项服务的凭证,记录了他/她是在哪一所服务机构购买的哪一项服务,付款金额也暂存在其中:
public struct ServiceCertification has key {
id: UID,
organization_id: ID,
service_id: ID,
purchase_price: Balance<SUI>,
}
顾客购买了服务套餐的凭证,记录了他/她是在哪一所服务机构购买的哪一种套餐,付款金额也暂存在其中:
public struct PackageServicesCertification has key {
id: UID,
organization_id: ID,
package_services_id: ID,
services: vector<ID>,
purchase_price: Balance<SUI>,
}
为了便于交互,设计了一系列事件对象结构,在特定情形下触发,以告知关键更改(多为涉及金额方面的变更):
public struct OrganizationListEvent has copy, drop {
organization_name: String,
organization_id: ID,
}
public struct ServiceEvent has copy, drop {
service_name: String,
service_id: ID,
}
public struct PackageServicesEvent has copy, drop {
services: vector<String>,
package_services_id: ID,
}
public struct RefundEvent has copy, drop {
note: String,
value: u64,
}
public struct EnjoyEvent has copy, drop {
note: String,
}
提现分为票务系统发布者提现和服务机构管理者提现,前者需要Publisher
,后者需要拥有对应的ServiceOrganizationCap
同时账户内当前收益不少于 $\text {100}$,且服务机构在提现的同时,将支付给系统发布者一笔 $\text 1\%$ 的抽成。
entry fun publisher_withdraw(_: &Publisher, organization_list: &mut OrganizationList, ctx: &mut TxContext) {
// check balance
assert!(organization_list.publisher_income.value() > 0, ENotIncome);
// get all balance
let all_balance = organization_list.publisher_income.withdraw_all();
// transfer the coin to the publisher
transfer::public_transfer(coin::from_balance(all_balance, ctx), ctx.sender());
}
// in order to make the withdrawal function more versatile
#[allow(lint(self_transfer))]
fun organization_withdraw_(service_organization: &mut ServiceOrganization, publisher_income: &mut Balance<SUI>, ctx: &mut TxContext) {
// check balance
assert!(service_organization.income.value() >= 100, ENotEnoughIncome);
// get all amount
let amount = service_organization.income.value();
// split 1%
let publisher_earned = service_organization.income.split(amount / 100);
// join to the publisher_balance
publisher_income.join(publisher_earned);
// get the remaining balance
let all_balance = service_organization.income.withdraw_all();
// transfer the coin to the organization owner
transfer::public_transfer(coin::from_balance(all_balance, ctx), ctx.sender());
}
entry fun organization_withdraw(service_organization_cap: &ServiceOrganizationCap, organization_list: &mut OrganizationList, ctx: &mut TxContext) {
// the fact that `service_organization_cap` exists means that `organization_list` must own what it corresponds to
// so we don’t need to judge here
// get id
let organization_id = object::id(service_organization_cap);
// get and return service organization
let service_organization = &mut organization_list.organizations[organization_id];
// withdraw
organization_withdraw_(service_organization, &mut organization_list.publisher_income, ctx);
}
创建ServiceOrganization
及其对应的ServiceOrganizationCap
,前者存储入OrganizationList
,后者则移交给这笔交易的发送者,也就是这所服务机构的管理者:
entry fun create_service_organization(organization_list: &mut OrganizationList, organization_name: String, ctx: &mut TxContext) {
// create service organization cap
let service_organization_cap = ServiceOrganizationCap {id: object::new(ctx)};
// create service organization
let service_organization = ServiceOrganization {
organization_name,
services: table::new<ID, Service>(ctx),
services_ids: vector<ID>[],
package_services: table::new<ID, PackageServices>(ctx),
package_services_ids: vector<ID>[],
income: balance::zero(),
};
// get service_organization_cap id as service_organization id
let service_organization_id = object::id(&service_organization_cap);
// store service_organization to organization_list
organization_list.organizations.add(service_organization_id, service_organization);
organization_list.organizations_ids.push_back(service_organization_id);
// transfer service_organization_cap to the organization owner
transfer::transfer(service_organization_cap, ctx.sender());
}
注销服务机构,需要有service_organization_cap
权限,解构这个权限对象,同时将对应的机构从OrganizationList
当中移除,别忘记将单独取出来的ServiceOrganization
也进行解构,尤其是其中的Balance<SUI>
要妥善处理:
entry fun destroy_service_organization(service_organization_cap: ServiceOrganizationCap, organization_list: &mut OrganizationList, ctx: &mut TxContext) {
// get id
let ServiceOrganizationCap {id} = service_organization_cap;
// get service organization
let mut service_organization = organization_list.organizations.remove(id.to_inner());
// get index
let (_, index) = organization_list.organizations_ids.index_of(&id.to_inner());
// remove index
organization_list.organizations_ids.remove(index);
// delete Service Organization Cap id
object::delete(id);
// withdraw
if (service_organization.income.value() < 100) {
// get all balance
let all_balance = service_organization.income.withdraw_all();
// transfer it to the owner
transfer::public_transfer(coin::from_balance(all_balance, ctx), ctx.sender());
} else {
// use the withdrawal function
organization_withdraw_(&mut service_organization, &mut organization_list.publisher_income, ctx);
};
// deconstruct ServiceOrganization
let ServiceOrganization {
organization_name: _,
mut services,
mut services_ids,
mut package_services,
mut package_services_ids,
income,
} = service_organization;
// destroy services
while (services_ids.length() > 0) {
// get service id
let id = services_ids.pop_back();
// remove it
services.remove(id); // return Service has ability drop
};
services.destroy_empty();
services_ids.destroy_empty();
// destroy package_services
while (package_services_ids.length() > 0) {
// get package id
let id = package_services_ids.pop_back();
// get services
let PackageServices {
mut services,
price: _,
} = package_services.remove(id);
// clear and destroy services(vector)
while (services.length() > 0) {
services.pop_back();
};
services.destroy_empty();
};
package_services.destroy_empty();
package_services_ids.destroy_empty();
// destroy income
income.destroy_zero();
}
创建Service
并将其存储对应的ServiceOrganization
当中:
entry fun create_service(service_organization_cap: &ServiceOrganizationCap, organization_list: &mut OrganizationList, service_name: String, price: u64, ctx: &mut TxContext) {
// get ServiceOrganization
let service_organization = get_service_organization(service_organization_cap, organization_list);
// generate new id
let id = object::id_from_address(ctx.fresh_object_address());
// create Service
let service = Service {
service_name,
price,
};
// store service
service_organization.services.add(id, service);
service_organization.services_ids.push_back(id);
}
移除(下架)一项服务,除了将其从ServiceOrganization
当中删除之外,还需要更新含有这项服务的套餐,当删掉它之后,若是套餐内的服务少于两个,则不构成套餐,那么直接删除这个套餐,否则就只是将这项服务移出套餐,同时按照平均价格更新套餐售价:
fun update_package_services(service_organization_cap: &ServiceOrganizationCap, organization_list: &mut OrganizationList, service_id: ID) {
// get ServiceOrganization
let service_organization = get_service_organization(service_organization_cap, organization_list);
// get package services ids
let package_services_ids = &service_organization.package_services_ids;
// get package services
let package_services = &mut service_organization.package_services;
let mut i = 0;
let mut waiting_destroy_ids = vector<ID>[];
while (i < package_services_ids.length()) {
// get package id
let package_id = package_services_ids[i];
// get package_services
let package_services = &mut package_services[package_id];
// get services
let services = &mut package_services.services;
// don't contains then continue
if (!services.contains(&service_id)) {
i = i + 1;
continue
};
// get index
let (_, index) = services.index_of(&service_id);
// remove it
services.remove(index);
// still have enough service(>= 2) then continue
if (services.length() >= 2) {
// decrease price
let decrease_price = package_services.price / (services.length() + 1);
package_services.price = package_services.price - decrease_price;
i = i + 1;
continue
};
// add to waiting destroy list
waiting_destroy_ids.push_back(package_id);
i = i + 1;
};
// destroy package services
while (waiting_destroy_ids.length() > 0) {
let package_id = waiting_destroy_ids.pop_back();
destroy_package_services(service_organization_cap, organization_list, package_id);
};
}
entry fun destroy_service(service_organization_cap: &ServiceOrganizationCap, organization_list: &mut OrganizationList, service_id: ID) {
// get ServiceOrganization
let service_organization = get_service_organization(service_organization_cap, organization_list);
// check service_id
assert!(service_organization.services_ids.contains(&service_id), ENotCorrectService);
// get index of `services_ids`
let (_, index) = service_organization.services_ids.index_of(&service_id);
// remove it
service_organization.services_ids.remove(index);
// services remove
service_organization.services.remove(service_id); // return Service has ability drop
// update package services
update_package_services(service_organization_cap, organization_list, service_id);
}
创建PackageServices
并将其存入ServiceOrganization
,其中,将套餐内含有的服务 $\mathit {ID}$ 列表存储这一行为,可以单独写成函数,为后续方便调用做准备:
entry fun modify_package_services(service_organization_cap: &ServiceOrganizationCap, organization_list: &mut OrganizationList, package_id: ID, services: vector<ID>) {
// get ServiceOrganization
let service_organization = get_service_organization(service_organization_cap, organization_list);
// check package id
assert!(service_organization.package_services_ids.contains(&package_id), ENotCorrectPackage);
// check services
let mut i = 0;
while (i < services.length()) {
let service_id = services[i];
assert!(service_organization.services_ids.contains(&service_id), ENotAllCorrectService);
i = i + 1;
};
// modify services
let package_services = &mut service_organization.package_services[package_id];
let store_services = &mut package_services.services;
*store_services = services;
}
entry fun create_package_services(service_organization_cap: &ServiceOrganizationCap, organization_list: &mut OrganizationList, services: vector<ID>, ctx: &mut TxContext) {
// get ServiceOrganization
let service_organization = get_service_organization(service_organization_cap, organization_list);
// get default price
let default_price = get_default_price(service_organization, &services);
create_package_services_and_price(service_organization_cap, organization_list, services, default_price, ctx);
}
entry fun create_package_services_and_price(service_organization_cap: &ServiceOrganizationCap, organization_list: &mut OrganizationList, services: vector<ID>, price: u64, ctx: &mut TxContext) {
// get ServiceOrganization
let service_organization = get_service_organization(service_organization_cap, organization_list);
// generate new id
let id = object::id_from_address(ctx.fresh_object_address());
// init package serivces
service_organization.package_services_ids.push_back(id);
service_organization.package_services.add(id, PackageServices {
services: vector<ID>[],
price,
});
// store services
modify_package_services(service_organization_cap, organization_list, id, services);
}
类似的,可以轻松写出修改价格等函数,这里就不再一一列举。
除了将其从ServiceOrganization
移除之外,别忘了解构得到的PackageServices
:
entry fun destroy_package_services(service_organization_cap: &ServiceOrganizationCap, organization_list: &mut OrganizationList, package_id: ID) {
// get ServiceOrganization
let service_organization = get_service_organization(service_organization_cap, organization_list);
// check package id
assert!(service_organization.package_services_ids.contains(&package_id), ENotCorrectPackage);
// get index of `package_services_ids`
let (_, index) = service_organization.package_services_ids.index_of(&package_id);
// remove it
service_organization.package_services_ids.remove(index);
// package_services remove
let PackageServices {
mut services,
price: _,
} = service_organization.package_services.remove(package_id);
// clear vector
while (services.length() > 0) {
services.pop_back();
};
// destroy vector
services.destroy_empty();
}
查询当前有几所服务机构及其信息:
entry fun query_organization_list(organization_list: &OrganizationList) {
// get organization ids
let organization_ids = organization_list.borrow_organizations_ids();
// get organizations
let organizations = organization_list.borrow_organizations();
let mut i = 0;
while (i < organization_ids.length()) {
// get organization_id
let organization_id = organization_ids[i];
// get organization
let organization = &organizations[organization_id];
// get organization_name
let organization_name = organization.get_organization_name();
// emit event
event::emit(OrganizationListEvent {
organization_name,
organization_id,
});
i = i + 1;
};
}
查询某一所服务机构的详细信息,包括其提供的所有单项服务以及服务套餐:
entry fun query_organization_services(organization_id: ID, organization_list: &OrganizationList) {
// get organizations
let organizations = organization_list.borrow_organizations();
// get organization
let organization = &organizations[organization_id];
// get services
let services = organization.borrow_services();
// get services_ids
let services_ids = organization.borrow_services_ids();
let mut i = 0;
while (i < services_ids.length()) {
// get service is
let service_id = services_ids[i];
// get service
let service = &services[service_id];
// get service name
let service_name = service.get_service_name();
// emit event
event::emit(ServiceEvent {
service_name,
service_id,
});
i = i + 1;
};
// get package services
let package_services = organization.borrow_package_services();
// get package services ids
let package_services_ids = organization.borrow_package_services_ids();
i = 0;
while (i < package_services_ids.length()) {
// get package_id
let package_id = package_services_ids[i];
// get package_services
let package_services = &package_services[package_id];
// get services(ids)
let services_ids = package_services.borrow_ids_from_package_services();
// init services names
let mut services_names = vector<String>[];
while (services_names.length() < services.length()) {
// get index
let idx = services_names.length();
// get service id
let service_id = services_ids[idx];
// get service
let service = &services[service_id];
// get service name
let service_name = service.get_service_name();
// push back
services_names.push_back(service_name);
};
// emit event
event::emit(PackageServicesEvent {
services: services_names,
package_services_id: package_id,
});
i = i + 1;
};
}
购买单项服务,创建凭证ServiceCertification
并将其所有权转移给顾客:
entry fun buy_service(organization_list: &OrganizationList, organization_id: ID, service_id: ID, mut sui: Coin<SUI>, ctx: &mut TxContext) {
// get organizations
let organizations = organization_list.borrow_organizations();
// get organizations ids
let organizations_ids = organization_list.borrow_organizations_ids();
// check organization_id
assert!(organizations_ids.contains(&organization_id), ENotCorrectOrganizationID);
// get organization
let organization = &organizations[organization_id];
// get services
let services = organization.borrow_services();
// get services ids
let services_ids = organization.borrow_services_ids();
// check service_id
assert!(services_ids.contains(&service_id), ENotCorrectServiceID);
// get service
let service = &services[service_id];
// get price
let price = service.get_service_price();
// check coin amount
assert!(sui.value() >= price, ENotEnoughCoin);
// split coin
let pay_coin = sui.split(price, ctx);
// deal with the remaining coin
if (sui.value() > 0) {
transfer::public_transfer(sui, ctx.sender());
} else {
sui.destroy_zero();
};
// create ServiceCertification
let service_certification = ServiceCertification {
id: object::new(ctx),
organization_id,
service_id,
purchase_price: pay_coin.into_balance(),
};
// tranfer it
transfer::transfer(service_certification, ctx.sender());
}
对购买的单项服务进行退款,如果机构已经下架或者整个服务机构已经注销,则直接退款,否则将收取 $\text {10}\%$ 的手续费。
entry fun refund_service(service_certification: ServiceCertification, organization_list: &mut OrganizationList, ctx: &mut TxContext) {
// deconstruct ServiceCertification
let ServiceCertification {
id,
organization_id,
service_id,
mut purchase_price,
} = service_certification;
// delete id
object::delete(id);
// if the service organization has canceled, a direct refund will be issued
// if the service has canceled, a direct refund will be issued
if (check_organization_canceled(&organization_id, organization_list) || check_service_canceled(organization_id, &service_id, organization_list)) {
event::emit(RefundEvent {
note: string::utf8(b"service organization or service has canceled!"),
value: purchase_price.value(),
});
transfer::public_transfer(coin::from_balance(purchase_price, ctx), ctx.sender());
return
};
// for refunds due to your own reasons, a 10% fee will be charged
let amount = purchase_price.value();
// split
let fee = purchase_price.split(amount / 10);
organization_list.join_organization_balance(organization_id, fee);
event::emit(RefundEvent {
note: string::utf8(b"for refunds due to your own reasons, a 10% fee will be charged!"),
value: purchase_price.value(),
});
// refunds
transfer::public_transfer(coin::from_balance(purchase_price, ctx), ctx.sender());
}
享受服务,解构ServiceCertification
的同时,钱款转移到机构:
entry fun enjoy_service(service_certification: ServiceCertification, organization_list: &mut OrganizationList, ctx: &mut TxContext) {
// deconstruct ServiceCertification
let ServiceCertification {
id,
organization_id,
service_id,
purchase_price,
} = service_certification;
// delete id
object::delete(id);
// if the service organization has canceled, a direct refund will be issued
// if the service has canceled, a direct refund will be issued
if (check_organization_canceled(&organization_id, organization_list) || check_service_canceled(organization_id, &service_id, organization_list)) {
event::emit(RefundEvent {
note: string::utf8(b"service organization or service has canceled!"),
value: purchase_price.value(),
});
transfer::public_transfer(coin::from_balance(purchase_price, ctx), ctx.sender());
return
};
// pay coin
organization_list.join_organization_balance(organization_id, purchase_price);
event::emit(EnjoyEvent {
note: string::utf8(b"good morning, and in case i don't see you, good afternoon, good evening, and good night!"),
});
}
类似的,对于套餐服务,也可以编写出购买、退款和享受服务的功能函数,其中需要额外考虑的就是当用户拿着凭证享受服务的时候,其中部分服务已经下架了该如何处理(部分退款并将其 $\mathit {ID}$ 从凭证中移除),为了做到这一点,就多了一个函数来处理:
entry fun buy_package_services(organization_list: &OrganizationList, organization_id: ID, package_services_id: ID, mut sui: Coin<SUI>, ctx: &mut TxContext) {
// get organizations
let organizations = organization_list.borrow_organizations();
// get organizations ids
let organizations_ids = organization_list.borrow_organizations_ids();
// check organization_id
assert!(organizations_ids.contains(&organization_id), ENotCorrectOrganizationID);
// get organization
let organization = &organizations[organization_id];
// get package services
let package_services = organization.borrow_package_services();
// get package services ids
let package_services_ids = organization.borrow_package_services_ids();
// check package_services_id
assert!(package_services_ids.contains(&package_services_id), ENotCorrectPackageServicesID);
// get package_services
let package_services_contents = &package_services[package_services_id];
// get price
let price = package_services_contents.get_package_services_price();
// check coin amount
assert!(sui.value() >= price, ENotEnoughCoin);
// split coin
let pay_coin = sui.split(price, ctx);
// deal with the remaining coin
if (sui.value() > 0) {
transfer::public_transfer(sui, ctx.sender());
} else {
sui.destroy_zero();
};
// create PackageServiceCertification
let package_services_certification = PackageServicesCertification {
id: object::new(ctx),
organization_id,
package_services_id,
services: *package_services_contents.borrow_ids_from_package_services(),
purchase_price: pay_coin.into_balance(),
};
// transfer it
transfer::transfer(package_services_certification, ctx.sender());
}
entry fun refund_package_services(package_services_certification: PackageServicesCertification, organization_list: &mut OrganizationList, ctx: &mut TxContext) {
// deconstruct PackageServicesCertification
let PackageServicesCertification {
id,
organization_id,
package_services_id,
mut services,
mut purchase_price,
} = package_services_certification;
// delete id
object::delete(id);
// if the service organization has canceled, a direct refund will be issued
// if the package_services has canceled, a direct refund will be issued
if (check_organization_canceled(&organization_id, organization_list) || check_package_services_canceled(organization_id, &package_services_id, organization_list)) {
event::emit(RefundEvent {
note: string::utf8(b"service organization or package services has canceled!"),
value: purchase_price.value(),
});
transfer::public_transfer(coin::from_balance(purchase_price, ctx), ctx.sender());
while (services.length() > 0) {
services.pop_back();
};
services.destroy_empty();
return
};
// if the corresponding service has been cancelled, the corresponding amount will be refunded directly
refund_canceled_package_services(organization_id, organization_list, &mut services, &mut purchase_price, ctx);
// for refunds due to your own reasons, a 10% fee will be charged
let amount = purchase_price.value();
// if all canceled then return
if (amount == 0) {
services.destroy_empty();
purchase_price.destroy_zero();
return
};
// split
let fee = purchase_price.split(amount / 10);
organization_list.join_organization_balance(organization_id, fee);
event::emit(RefundEvent {
note: string::utf8(b"for refunds due to your own reasons, a 10% fee will be charged!"),
value: purchase_price.value(),
});
// refunds
transfer::public_transfer(coin::from_balance(purchase_price, ctx), ctx.sender());
// clear and destroy services
while (services.length() > 0) {
services.pop_back();
};
services.destroy_empty();
}
entry fun enjoy_package_services(mut package_services_certification: PackageServicesCertification, enjoy_services: vector<ID>, organization_list: &mut OrganizationList, ctx: &mut TxContext) {
// get details
let organization_id = package_services_certification.organization_id;
let package_services_id = package_services_certification.package_services_id;
let services = &mut package_services_certification.services;
let purchase_price = &mut package_services_certification.purchase_price;
// if the service organization has canceled, a direct refund will be issued
// if the package_services has canceled, a direct refund will be issued
if (check_organization_canceled(&organization_id, organization_list) || check_package_services_canceled(organization_id, &package_services_id, organization_list)) {
event::emit(RefundEvent {
note: string::utf8(b"service organization or package services has canceled!"),
value: purchase_price.value(),
});
let amount = purchase_price.value();
transfer::public_transfer(coin::take(purchase_price, amount, ctx), ctx.sender());
// deconstruct
let PackageServicesCertification {
id,
organization_id: _,
package_services_id: _,
mut services,
purchase_price,
} = package_services_certification;
// delete id
object::delete(id);
// destroy
purchase_price.destroy_zero();
while (services.length() > 0) {
services.pop_back();
};
services.destroy_empty();
return
};
// if the corresponding service has been cancelled, the corresponding amount will be refunded directly
refund_canceled_package_services(organization_id, organization_list, services, purchase_price, ctx);
// if all canceled then return
if (purchase_price.value() == 0) {
// deconstruct
let PackageServicesCertification {
id,
organization_id: _,
package_services_id: _,
services,
purchase_price,
} = package_services_certification;
// delete id
object::delete(id);
// destroy
services.destroy_empty();
purchase_price.destroy_zero();
return
};
// check enjoy_services
let mut i = 0;
while (i < enjoy_services.length()) {
let service_id = enjoy_services[i];
assert!(services.contains(&service_id), ENotCorrectServicesList);
// remove this service
let (_, index) = services.index_of(&service_id);
services.remove(index);
i = i + 1;
};
// get value
let amount = purchase_price.value();
// payout ratio = enjoy_services.length() / (enjoy_services.length() + services.length())
// so we need to pay
let need_to_pay = if (services.length() != 0) amount / (enjoy_services.length() + services.length()) * enjoy_services.length() else amount;
// pay coin
organization_list.join_organization_balance(organization_id, purchase_price.split(need_to_pay));
event::emit(EnjoyEvent {
note: string::utf8(b"good morning, and in case i don't see you, good afternoon, good evening, and good night!"),
});
if (purchase_price.value() == 0) {
// deconstruct
let PackageServicesCertification {
id,
organization_id: _,
package_services_id: _,
services,
purchase_price,
} = package_services_certification;
// delete id
object::delete(id);
// destroy
services.destroy_empty();
purchase_price.destroy_zero();
} else {
// transfer to owner
transfer::transfer(package_services_certification, ctx.sender());
};
}
entry fun update_package_services(mut package_services_certification: PackageServicesCertification, organization_list: &OrganizationList, ctx: &mut TxContext) {
// get details
let organization_id = package_services_certification.organization_id;
let package_services_id = package_services_certification.package_services_id;
let services = &mut package_services_certification.services;
let purchase_price = &mut package_services_certification.purchase_price;
// if the service organization has canceled, a direct refund will be issued
// if the package_services has canceled, a direct refund will be issued
if (check_organization_canceled(&organization_id, organization_list) || check_package_services_canceled(organization_id, &package_services_id, organization_list)) {
event::emit(RefundEvent {
note: string::utf8(b"service organization or package services has canceled!"),
value: purchase_price.value(),
});
let amount = purchase_price.value();
transfer::public_transfer(coin::take(purchase_price, amount, ctx), ctx.sender());
// deconstruct
let PackageServicesCertification {
id,
organization_id: _,
package_services_id: _,
mut services,
purchase_price,
} = package_services_certification;
// delete id
object::delete(id);
// destroy
purchase_price.destroy_zero();
while (services.length() > 0) {
services.pop_back();
};
services.destroy_empty();
return
};
// if the corresponding service has been cancelled, the corresponding amount will be refunded directly
refund_canceled_package_services(organization_id, organization_list, services, purchase_price, ctx);
// if all canceled then return
if (purchase_price.value() == 0) {
// deconstruct
let PackageServicesCertification {
id,
organization_id: _,
package_services_id: _,
services,
purchase_price,
} = package_services_certification;
// delete id
object::delete(id);
// destroy
services.destroy_empty();
purchase_price.destroy_zero();
return
};
// still have services then transfer it to owner
transfer::transfer(package_services_certification, ctx.sender());
}
完整代码可以点击查看,上文省略了很多辅助函数,都可以在里面详细查看到。
注意: 本篇测试内容部署在测试网($\mathit {testnet}$),由于功能众多,仅仅挑选了其中一部分进行。
sui client publish --gas-budget 100000000
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Object Changes │
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Created Objects: │
│ ┌── │
│ │ ObjectID: 0x1f4044ef7050a8305fcf33ce7aed3b91d34a9333ae7efc2af7205b3c286d5f30 │
│ │ Sender: 0x9e4092b6a894e6b168aa1c6c009f5c1c1fcb83fb95e5aa39144e1d2be4ee0d67 │
│ │ Owner: Shared │
│ │ ObjectType: 0x216202fdaae06b761222286300e18eb6ef24c2fe5cf7336b7f2da9ab97beea41::admin::OrganizationList │
│ │ Version: 28131907 │
│ │ Digest: ACTMHcXEBhVKkSPyWHG2utssZc2VtPenPx11iSkXQ1PP │
│ └── │
│ ┌── │
│ │ ObjectID: 0xa4eea40898c7a07fb6c19ff8f91eb7d1cd7972239ccf09dd980d3918710139d6 │
│ │ Sender: 0x9e4092b6a894e6b168aa1c6c009f5c1c1fcb83fb95e5aa39144e1d2be4ee0d67 │
│ │ Owner: Account Address ( 0x9e4092b6a894e6b168aa1c6c009f5c1c1fcb83fb95e5aa39144e1d2be4ee0d67 ) │
│ │ ObjectType: 0x2::package::UpgradeCap │
│ │ Version: 28131907 │
│ │ Digest: DfWZyiGJASyHRictNPZaXxBuscP95cBMzj8aU2HR9znb │
│ └── │
│ ┌── │
│ │ ObjectID: 0xcede98cf7512b7bb36d605eaa6b2b40c4d1fe0b5d95508a4a4bb1c9b6ea1e6ed │
│ │ Sender: 0x9e4092b6a894e6b168aa1c6c009f5c1c1fcb83fb95e5aa39144e1d2be4ee0d67 │
│ │ Owner: Account Address ( 0x9e4092b6a894e6b168aa1c6c009f5c1c1fcb83fb95e5aa39144e1d2be4ee0d67 ) │
│ │ ObjectType: 0x2::package::Publisher │
│ │ Version: 28131907 │
│ │ Digest: 7ZTY3uhTAy66zuuQhzGuTuhd85WMbQbLgYCHER3enV7W │
│ └── │
│ Mutated Objects: │
│ ┌── │
│ │ ObjectID: 0x01676de212960b0689245914312ac6be3b4d5cffa0cae91ef527441b894f746a │
│ │ Sender: 0x9e4092b6a894e6b168aa1c6c009f5c1c1fcb83fb95e5aa39144e1d2be4ee0d67 │
│ │ Owner: Account Address ( 0x9e4092b6a894e6b168aa1c6c009f5c1c1fcb83fb95e5aa39144e1d2be4ee0d67 ) │
│ │ ObjectType: 0x2::coin::Coin<0x2::sui::SUI> │
│ │ Version: 28131907 │
│ │ Digest: EyVfUXRunZ1tUZJu9xPMPMfjVXECQdjDqrNaihwLKCVo │
│ └── │
│ Published Objects: │
│ ┌── │
│ │ PackageID: 0x216202fdaae06b761222286300e18eb6ef24c2fe5cf7336b7f2da9ab97beea41 │
│ │ Version: 1 │
│ │ Digest: 3pGSsGYWDBVZBvDzRMb9pgCqzQYixdFxPJPcDGUKTrzn │
│ │ Modules: admin, customer │
│ └── │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
export PACKAGE=0x216202fdaae06b761222286300e18eb6ef24c2fe5cf7336b7f2da9ab97beea41
export PUBLISHER=0xcede98cf7512b7bb36d605eaa6b2b40c4d1fe0b5d95508a4a4bb1c9b6ea1e6ed
export ORGANIZATIONLIST=0x1f4044ef7050a8305fcf33ce7aed3b91d34a9333ae7efc2af7205b3c286d5f30
sui client call --package $PACKAGE --module admin --function create_service_organization --args $ORGANIZATIONLIST organization_test1 --gas-budget 100000000
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Object Changes │
├────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Created Objects: │
│ ┌── │
│ │ ObjectID: 0x787784d9199ba7b4cc737968f757e51618161a20011a3587d47fc6a41a3d8d2f │
│ │ Sender: 0x9e4092b6a894e6b168aa1c6c009f5c1c1fcb83fb95e5aa39144e1d2be4ee0d67 │
│ │ Owner: Object ID: ( 0xfd82ff66cfa34dd54a73800f6b84353687c196cddbb018e7e405262b8d198518 ) │
│ │ ObjectType: 0x2::dynamic_field::Field<0x2::object::ID, 0x216202fdaae06b761222286300e18eb6ef24c2fe5cf7336b7f2da9ab97beea41::admin::ServiceOrganization> │
│ │ Version: 28131908 │
│ │ Digest: mPs2f9vzsR4h6avMUmPuxmZjNvfqexXGiRjp5syAPmb │
│ └── │
│ ┌── │
│ │ ObjectID: 0xea3dcaae81667ff00e5a7ac31326f1e5d5a8e5758ce4ef88f7230a4537bf2485 │
│ │ Sender: 0x9e4092b6a894e6b168aa1c6c009f5c1c1fcb83fb95e5aa39144e1d2be4ee0d67 │
│ │ Owner: Account Address ( 0x9e4092b6a894e6b168aa1c6c009f5c1c1fcb83fb95e5aa39144e1d2be4ee0d67 ) │
│ │ ObjectType: 0x216202fdaae06b761222286300e18eb6ef24c2fe5cf7336b7f2da9ab97beea41::admin::ServiceOrganizationCap │
│ │ Version: 28131908 │
│ │ Digest: PLRK1ZpfJ1SGcudcDcBdqDdTFBoPHLj2SAU3rgk8PZM │
│ └── │
│ Mutated Objects: │
│ ┌── │
│ │ ObjectID: 0x01676de212960b0689245914312ac6be3b4d5cffa0cae91ef527441b894f746a │
│ │ Sender: 0x9e4092b6a894e6b168aa1c6c009f5c1c1fcb83fb95e5aa39144e1d2be4ee0d67 │
│ │ Owner: Account Address ( 0x9e4092b6a894e6b168aa1c6c009f5c1c1fcb83fb95e5aa39144e1d2be4ee0d67 ) │
│ │ ObjectType: 0x2::coin::Coin<0x2::sui::SUI> │
│ │ Version: 28131908 │
│ │ Digest: 5guVeUMee7L7qzXx919o1eKiBQK5hCcyyAnHJvBcfNdQ │
│ └── │
│ ┌── │
│ │ ObjectID: 0x1f4044ef7050a8305fcf33ce7aed3b91d34a9333ae7efc2af7205b3c286d5f30 │
│ │ Sender: 0x9e4092b6a894e6b168aa1c6c009f5c1c1fcb83fb95e5aa39144e1d2be4ee0d67 │
│ │ Owner: Shared │
│ │ ObjectType: 0x216202fdaae06b761222286300e18eb6ef24c2fe5cf7336b7f2da9ab97beea41::admin::OrganizationList │
│ │ Version: 28131908 │
│ │ Digest: B2ghJsWe8mez3cfMTuwoqJFV2p5rFxB4DZcXixxZKDR7 │
│ └── │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
export SERVICEORGANIZATIONCAP=0xea3dcaae81667ff00e5a7ac31326f1e5d5a8e5758ce4ef88f7230a4537bf2485
sui client call --package $PACKAGE --module admin --function create_service --args $SERVICEORGANIZATIONCAP $ORGANIZATIONLIST service_test_1 50 --gas-budget 100000000
sui client call --package $PACKAGE --module admin --function create_service --args $SERVICEORGANIZATIONCAP $ORGANIZATIONLIST service_test_2 50 --gas-budget 100000000
sui client switch --address peaceful-hiddenite
sui client call --package $PACKAGE --module customer --function query_organization_list --args $ORGANIZATIONLIST --gas-budget 100000000
# important output
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Transaction Block Events │
├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ┌── │
│ │ EventID: 9ewfm1HnvaaC2R4nnK7Zctcbb42XUMgzNLPRDFQm7BNj:0 │
│ │ PackageID: 0x216202fdaae06b761222286300e18eb6ef24c2fe5cf7336b7f2da9ab97beea41 │
│ │ Transaction Module: customer │
│ │ Sender: 0xf6029b82e355f627b0e3d8941d63e139c4b73b495a2017ef48aaf17cc377457b │
│ │ EventType: 0x216202fdaae06b761222286300e18eb6ef24c2fe5cf7336b7f2da9ab97beea41::customer::OrganizationListEvent │
│ │ ParsedJSON: │
│ │ ┌───────────────────┬────────────────────────────────────────────────────────────────────┐ │
│ │ │ organization_id │ 0xea3dcaae81667ff00e5a7ac31326f1e5d5a8e5758ce4ef88f7230a4537bf2485 │ │
│ │ ├───────────────────┼────────────────────────────────────────────────────────────────────┤ │
│ │ │ organization_name │ organization_test1 │ │
│ │ └───────────────────┴────────────────────────────────────────────────────────────────────┘ │
│ └── │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
# record ID
export ORGANIZATIONID=0xea3dcaae81667ff00e5a7ac31326f1e5d5a8e5758ce4ef88f7230a4537bf2485
sui client call --package $PACKAGE --module customer --function query_organization_services --args $ORGANIZATIONID $ORGANIZATIONLIST --gas-budget 100000000
# important output
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Transaction Block Events │
├──────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ┌── │
│ │ EventID: 97DF15Z7CKpc68AWv5jWLcUvTSAqkeTczHiMdseYgksA:0 │
│ │ PackageID: 0x216202fdaae06b761222286300e18eb6ef24c2fe5cf7336b7f2da9ab97beea41 │
│ │ Transaction Module: customer │
│ │ Sender: 0xf6029b82e355f627b0e3d8941d63e139c4b73b495a2017ef48aaf17cc377457b │
│ │ EventType: 0x216202fdaae06b761222286300e18eb6ef24c2fe5cf7336b7f2da9ab97beea41::customer::ServiceEvent │
│ │ ParsedJSON: │
│ │ ┌──────────────┬────────────────────────────────────────────────────────────────────┐ │
│ │ │ service_id │ 0xb4a15bb46d38f61fc89bd942e8816caab6c48cc688dd4b2e023262eebd1c8951 │ │
│ │ ├──────────────┼────────────────────────────────────────────────────────────────────┤ │
│ │ │ service_name │ service_test_1 │ │
│ │ └──────────────┴────────────────────────────────────────────────────────────────────┘ │
│ └── │
│ ┌── │
│ │ EventID: 97DF15Z7CKpc68AWv5jWLcUvTSAqkeTczHiMdseYgksA:1 │
│ │ PackageID: 0x216202fdaae06b761222286300e18eb6ef24c2fe5cf7336b7f2da9ab97beea41 │
│ │ Transaction Module: customer │
│ │ Sender: 0xf6029b82e355f627b0e3d8941d63e139c4b73b495a2017ef48aaf17cc377457b │
│ │ EventType: 0x216202fdaae06b761222286300e18eb6ef24c2fe5cf7336b7f2da9ab97beea41::customer::ServiceEvent │
│ │ ParsedJSON: │
│ │ ┌──────────────┬────────────────────────────────────────────────────────────────────┐ │
│ │ │ service_id │ 0x6bd5e56f2ef6f5d2ff27ea55526e725fb1a50e0ecf29e839c05b4e7b2853ac43 │ │
│ │ ├──────────────┼────────────────────────────────────────────────────────────────────┤ │
│ │ │ service_name │ service_test_2 │ │
│ │ └──────────────┴────────────────────────────────────────────────────────────────────┘ │
│ └── │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯
# record ID
export SERVICE1ID=0xb4a15bb46d38f61fc89bd942e8816caab6c48cc688dd4b2e023262eebd1c8951
export SERVICE2ID=0x6bd5e56f2ef6f5d2ff27ea55526e725fb1a50e0ecf29e839c05b4e7b2853ac43
注意: 后续内容将省略用户切换这一步,所有的非服务机构管理方面的链上调用均由普通用户进行交易。
sui client call --package $PACKAGE --module admin --function create_package_services --args $SERVICEORGANIZATIONCAP $ORGANIZATIONLIST "[$SERVICE1ID, $SERVICE2ID]" --gas-budget 100000000
sui client call --package $PACKAGE --module customer --function query_organization_services --args $ORGANIZATIONID $ORGANIZATIONLIST --gas-budget 100000000
# important output
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Transaction Block Events │
├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ┌── │
│ │ EventID: 5Fe2WvfW7f8LEjvHgExYu4qPRosjN7ci7oFRPwFvqpAe:0 │
│ │ PackageID: 0x216202fdaae06b761222286300e18eb6ef24c2fe5cf7336b7f2da9ab97beea41 │
│ │ Transaction Module: customer │
│ │ Sender: 0x9e4092b6a894e6b168aa1c6c009f5c1c1fcb83fb95e5aa39144e1d2be4ee0d67 │
│ │ EventType: 0x216202fdaae06b761222286300e18eb6ef24c2fe5cf7336b7f2da9ab97beea41::customer::ServiceEvent │
│ │ ParsedJSON: │
│ │ ┌──────────────┬────────────────────────────────────────────────────────────────────┐ │
│ │ │ service_id │ 0xb4a15bb46d38f61fc89bd942e8816caab6c48cc688dd4b2e023262eebd1c8951 │ │
│ │ ├──────────────┼────────────────────────────────────────────────────────────────────┤ │
│ │ │ service_name │ service_test_1 │ │
│ │ └──────────────┴────────────────────────────────────────────────────────────────────┘ │
│ └── │
│ ┌── │
│ │ EventID: 5Fe2WvfW7f8LEjvHgExYu4qPRosjN7ci7oFRPwFvqpAe:1 │
│ │ PackageID: 0x216202fdaae06b761222286300e18eb6ef24c2fe5cf7336b7f2da9ab97beea41 │
│ │ Transaction Module: customer │
│ │ Sender: 0x9e4092b6a894e6b168aa1c6c009f5c1c1fcb83fb95e5aa39144e1d2be4ee0d67 │
│ │ EventType: 0x216202fdaae06b761222286300e18eb6ef24c2fe5cf7336b7f2da9ab97beea41::customer::ServiceEvent │
│ │ ParsedJSON: │
│ │ ┌──────────────┬────────────────────────────────────────────────────────────────────┐ │
│ │ │ service_id │ 0x6bd5e56f2ef6f5d2ff27ea55526e725fb1a50e0ecf29e839c05b4e7b2853ac43 │ │
│ │ ├──────────────┼────────────────────────────────────────────────────────────────────┤ │
│ │ │ service_name │ service_test_2 │ │
│ │ └──────────────┴────────────────────────────────────────────────────────────────────┘ │
│ └── │
│ ┌── │
│ │ EventID: 5Fe2WvfW7f8LEjvHgExYu4qPRosjN7ci7oFRPwFvqpAe:2 │
│ │ PackageID: 0x216202fdaae06b761222286300e18eb6ef24c2fe5cf7336b7f2da9ab97beea41 │
│ │ Transaction Module: customer │
│ │ Sender: 0x9e4092b6a894e6b168aa1c6c009f5c1c1fcb83fb95e5aa39144e1d2be4ee0d67 │
│ │ EventType: 0x216202fdaae06b761222286300e18eb6ef24c2fe5cf7336b7f2da9ab97beea41::customer::PackageServicesEvent │
│ │ ParsedJSON: │
│ │ ┌─────────────────────┬────────────────────────────────────────────────────────────────────┐ │
│ │ │ package_services_id │ 0xd1f30ffcf54817a02da70251d4fd85949ecad34e772b0c4aee43304c705bc1c0 │ │
│ │ ├─────────────────────┼────────────────────────────────────────────────────────────────────┤ │
│ │ │ services │ service_test_1 │ │
│ │ │ ├────────────────────────────────────────────────────────────────────┤ │
│ │ │ │ service_test_2 │ │
│ │ └─────────────────────┴────────────────────────────────────────────────────────────────────┘ │
│ └── │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
# record ID
export PACKAGESERVICEID=0xd1f30ffcf54817a02da70251d4fd85949ecad34e772b0c4aee43304c705bc1c0
sui client gas
# output
╭────────────────────────────────────────────────────────────────────┬────────────────────┬──────────────────╮
│ gasCoinId │ mistBalance (MIST) │ suiBalance (SUI) │
├────────────────────────────────────────────────────────────────────┼────────────────────┼──────────────────┤
│ 0x516586df0c5e9c9567696840981f720d64335ac6e8ad409f4ba4843b8dc2274a │ 100 │ 0.00 │
│ 0x82ec01655d746b42dba5c5951841472e5d1715e74238a5ef8e39d0b0566dc3be │ 982798853 │ 0.98 │
│ 0x8ef6503cb330c4114bc7995a403adf4015190d8effc02504fd849377caa6499b │ 963358100 │ 0.96 │
│ 0xad3fa2545f5db01bd4d349871df5af4d6de913600e7e3e032d5229c006d35851 │ 100 │ 0.00 │
│ 0xcb24b30fe196f4f2ca6d5f8d87a273bf168f7f86f6b7ae3f1f20fc5cf447e557 │ 940920294 │ 0.94 │
│ 0xe4f2d7831241583d534271d8d777d7558290124779e98e03b059a2fe108d37b0 │ 989 │ 0.00 │
╰────────────────────────────────────────────────────────────────────┴────────────────────┴──────────────────╯
export COIN=0x82ec01655d746b42dba5c5951841472e5d1715e74238a5ef8e39d0b0566dc3be
sui client call --package $PACKAGE --module customer --function buy_package_services --args $ORGANIZATIONLIST $ORGANIZATIONID $PACKAGESERVICEID $COIN --gas-budget 100000000
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Object Changes │
├────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Created Objects: │
│ ┌── │
│ │ ObjectID: 0x4e7eebf65218cf0ed484fdba0182434cdd24ff3140af950c228442447790b42c │
│ │ Sender: 0xf6029b82e355f627b0e3d8941d63e139c4b73b495a2017ef48aaf17cc377457b │
│ │ Owner: Account Address ( 0xf6029b82e355f627b0e3d8941d63e139c4b73b495a2017ef48aaf17cc377457b ) │
│ │ ObjectType: 0x216202fdaae06b761222286300e18eb6ef24c2fe5cf7336b7f2da9ab97beea41::customer::PackageServicesCertification │
│ │ Version: 28131913 │
│ │ Digest: 7fUc3pVppMEcQPQ1QRVwcp6Y9rWp5jKw2qfB76E1vze9 │
│ └── │
│ Mutated Objects: │
│ ┌── │
│ │ ObjectID: 0x82ec01655d746b42dba5c5951841472e5d1715e74238a5ef8e39d0b0566dc3be │
│ │ Sender: 0xf6029b82e355f627b0e3d8941d63e139c4b73b495a2017ef48aaf17cc377457b │
│ │ Owner: Account Address ( 0xf6029b82e355f627b0e3d8941d63e139c4b73b495a2017ef48aaf17cc377457b ) │
│ │ ObjectType: 0x2::coin::Coin<0x2::sui::SUI> │
│ │ Version: 28131913 │
│ │ Digest: shAMmcj8Ca6SPC7TomPqxQviSdN4UQfwcZmKSJ2nNZ8 │
│ └── │
│ ┌── │
│ │ ObjectID: 0x8ef6503cb330c4114bc7995a403adf4015190d8effc02504fd849377caa6499b │
│ │ Sender: 0xf6029b82e355f627b0e3d8941d63e139c4b73b495a2017ef48aaf17cc377457b │
│ │ Owner: Account Address ( 0xf6029b82e355f627b0e3d8941d63e139c4b73b495a2017ef48aaf17cc377457b ) │
│ │ ObjectType: 0x2::coin::Coin<0x2::sui::SUI> │
│ │ Version: 28131913 │
│ │ Digest: ANDLCwxmDXNcurMemzk2fp22SWQfDdVVJkyYHQKhJj2k │
│ └── │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
export PACKAGESERVICESCERTIFICATION=0x4e7eebf65218cf0ed484fdba0182434cdd24ff3140af950c228442447790b42c
sui client call --package $PACKAGE --module admin --function destroy_service --args $SERVICEORGANIZATIONCAP $ORGANIZATIONLIST $SERVICE1ID --gas-budget 100000000
sui client call --package $PACKAGE --module customer --function query_organization_services --args $ORGANIZATIONID $ORGANIZATIONLIST --gas-budget 100000000
# important output
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Transaction Block Events │
├──────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ┌── │
│ │ EventID: A8d3a5CHgpjpmK8HyLH1e4xs3ww15ary8JSw6ts8BUBL:0 │
│ │ PackageID: 0x216202fdaae06b761222286300e18eb6ef24c2fe5cf7336b7f2da9ab97beea41 │
│ │ Transaction Module: customer │
│ │ Sender: 0x9e4092b6a894e6b168aa1c6c009f5c1c1fcb83fb95e5aa39144e1d2be4ee0d67 │
│ │ EventType: 0x216202fdaae06b761222286300e18eb6ef24c2fe5cf7336b7f2da9ab97beea41::customer::ServiceEvent │
│ │ ParsedJSON: │
│ │ ┌──────────────┬────────────────────────────────────────────────────────────────────┐ │
│ │ │ service_id │ 0x6bd5e56f2ef6f5d2ff27ea55526e725fb1a50e0ecf29e839c05b4e7b2853ac43 │ │
│ │ ├──────────────┼────────────────────────────────────────────────────────────────────┤ │
│ │ │ service_name │ service_test_2 │ │
│ │ └──────────────┴────────────────────────────────────────────────────────────────────┘ │
│ └── │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯
sui client call --package $PACKAGE --module customer --function update_package_services --args $PACKAGESERVICESCERTIFICATION $ORGANIZATIONLIST --gas-budget 100000000
# important output
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Transaction Block Events │
├─────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ┌── │
│ │ EventID: FT639fjhsoFqeoU79XH21tJYmKKiKTwMb97A3KtWNGzG:0 │
│ │ PackageID: 0x216202fdaae06b761222286300e18eb6ef24c2fe5cf7336b7f2da9ab97beea41 │
│ │ Transaction Module: customer │
│ │ Sender: 0xf6029b82e355f627b0e3d8941d63e139c4b73b495a2017ef48aaf17cc377457b │
│ │ EventType: 0x216202fdaae06b761222286300e18eb6ef24c2fe5cf7336b7f2da9ab97beea41::customer::RefundEvent │
│ │ ParsedJSON: │
│ │ ┌───────┬────────────────────────────────────────────────────────┐ │
│ │ │ note │ service organization or package services has canceled! │ │
│ │ ├───────┼────────────────────────────────────────────────────────┤ │
│ │ │ value │ 85 │ │
│ │ └───────┴────────────────────────────────────────────────────────┘ │
│ └── │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯
sui client gas
# output
╭────────────────────────────────────────────────────────────────────┬────────────────────┬──────────────────╮
│ gasCoinId │ mistBalance (MIST) │ suiBalance (SUI) │
├────────────────────────────────────────────────────────────────────┼────────────────────┼──────────────────┤
│ 0x516586df0c5e9c9567696840981f720d64335ac6e8ad409f4ba4843b8dc2274a │ 100 │ 0.00 │
│ 0x82ec01655d746b42dba5c5951841472e5d1715e74238a5ef8e39d0b0566dc3be │ 983283808 │ 0.98 │
│ 0x8ef6503cb330c4114bc7995a403adf4015190d8effc02504fd849377caa6499b │ 959830340 │ 0.95 │
│ 0x9bfd02b5901fcdcce225336c009d881b191fad46b82158dc15b84e1346973523 │ 85 │ 0.00 │
│ 0xad3fa2545f5db01bd4d349871df5af4d6de913600e7e3e032d5229c006d35851 │ 100 │ 0.00 │
│ 0xcb24b30fe196f4f2ca6d5f8d87a273bf168f7f86f6b7ae3f1f20fc5cf447e557 │ 940920294 │ 0.94 │
│ 0xe4f2d7831241583d534271d8d777d7558290124779e98e03b059a2fe108d37b0 │ 989 │ 0.00 │
╰────────────────────────────────────────────────────────────────────┴────────────────────┴──────────────────╯
sui client call --package $PACKAGE --module customer --function buy_service --args $ORGANIZATIONLIST $ORGANIZATIONID $SERVICE2ID $COIN --gas-budget 100000000
# important output
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Object Changes │
├────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Created Objects: │
│ ┌── │
│ │ ObjectID: 0x65599566e55f6bef29d7e67c8d04d10b2c7701459e368fa6ef2288d3e7a0ce66 │
│ │ Sender: 0xf6029b82e355f627b0e3d8941d63e139c4b73b495a2017ef48aaf17cc377457b │
│ │ Owner: Account Address ( 0xf6029b82e355f627b0e3d8941d63e139c4b73b495a2017ef48aaf17cc377457b ) │
│ │ ObjectType: 0x216202fdaae06b761222286300e18eb6ef24c2fe5cf7336b7f2da9ab97beea41::customer::ServiceCertification │
│ │ Version: 28131915 │
│ │ Digest: CCaaGnshMomZHcpegnb4GB2k5TuYSogKxZ4ZdQGAp3h8 │
│ └── │
│ Mutated Objects: │
│ ┌── │
│ │ ObjectID: 0x82ec01655d746b42dba5c5951841472e5d1715e74238a5ef8e39d0b0566dc3be │
│ │ Sender: 0xf6029b82e355f627b0e3d8941d63e139c4b73b495a2017ef48aaf17cc377457b │
│ │ Owner: Account Address ( 0xf6029b82e355f627b0e3d8941d63e139c4b73b495a2017ef48aaf17cc377457b ) │
│ │ ObjectType: 0x2::coin::Coin<0x2::sui::SUI> │
│ │ Version: 28131915 │
│ │ Digest: 4kHSso6yaPMmZ8VHFKvcbNJjdFLAtvCkJALStidq56ik │
│ └── │
│ ┌── │
│ │ ObjectID: 0x8ef6503cb330c4114bc7995a403adf4015190d8effc02504fd849377caa6499b │
│ │ Sender: 0xf6029b82e355f627b0e3d8941d63e139c4b73b495a2017ef48aaf17cc377457b │
│ │ Owner: Account Address ( 0xf6029b82e355f627b0e3d8941d63e139c4b73b495a2017ef48aaf17cc377457b ) │
│ │ ObjectType: 0x2::coin::Coin<0x2::sui::SUI> │
│ │ Version: 28131915 │
│ │ Digest: 4vsaUViSYCkazVVrQHQqbXNT3VPFhb13cDpJTw5gE5mc │
│ └── │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
# record ID
export SERVICECERTIFICATION=0x65599566e55f6bef29d7e67c8d04d10b2c7701459e368fa6ef2288d3e7a0ce66
sui client call --package $PACKAGE --module customer --function enjoy_service --args $SERVICECERTIFICATION $ORGANIZATIONLIST --gas-budget 100000000
# important output
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Transaction Block Events │
├──────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ┌── │
│ │ EventID: 61Rj3VYz3jC5KhPbe2jexoBfJd8Be7fGk6AMG9iffP5z:0 │
│ │ PackageID: 0x216202fdaae06b761222286300e18eb6ef24c2fe5cf7336b7f2da9ab97beea41 │
│ │ Transaction Module: customer │
│ │ Sender: 0xf6029b82e355f627b0e3d8941d63e139c4b73b495a2017ef48aaf17cc377457b │
│ │ EventType: 0x216202fdaae06b761222286300e18eb6ef24c2fe5cf7336b7f2da9ab97beea41::customer::EnjoyEvent │
│ │ ParsedJSON: │
│ │ ┌──────┬──────────────────────────────────────────────────────────────────────────────────────────┐ │
│ │ │ note │ good morning, and in case i don't see you, good afternoon, good evening, and good night! │ │
│ │ └──────┴──────────────────────────────────────────────────────────────────────────────────────────┘ │
│ └── │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯
sui client call --package $PACKAGE --module admin --function organization_withdraw --args $SERVICEORGANIZATIONCAP $ORGANIZATIONLIST --gas-budget 100000000
# error
Error executing transaction: Failure {
error: "MoveAbort(MoveLocation { module: ModuleId { address: 216202fdaae06b761222286300e18eb6ef24c2fe5cf7336b7f2da9ab97beea41, name: Identifier(\"admin\") }, function: 2, instruction: 14, function_name: Some(\"organization_withdraw_\") }, 1) in command 0",
}
因为收入少于 $\text {100}$,所以提现失败。<br>我们切换用户,再一次购买并享受一次价值 $\text {50}$ 的单项服务,之后再尝试发起这一笔交易。<br>现在,我们的收入是 $\text {100}$,服务机构将得到 $\text {99}$,而票务系统发布者将赚得 $\text {1}$,接下去我们将见证这一点。
sui client call --package $PACKAGE --module admin --function organization_withdraw --args $SERVICEORGANIZATIONCAP $ORGANIZATIONLIST --gas-budget 100000000
# query gas
sui client gas
╭────────────────────────────────────────────────────────────────────┬────────────────────┬──────────────────╮
│ gasCoinId │ mistBalance (MIST) │ suiBalance (SUI) │
├────────────────────────────────────────────────────────────────────┼────────────────────┼──────────────────┤
│ 0x01676de212960b0689245914312ac6be3b4d5cffa0cae91ef527441b894f746a │ 601182904 │ 0.60 │
│ 0x03335f68ff3616af7e000b113c56a5ad53e8e8209784ca0a5623f70997c8d948 │ 3182792690 │ 3.18 │
│ 0xd61fa7c67f44180f8987e9de59cae81e2b26215c6209e0a40bc782402344474d │ 99 │ 0.00 │
│ 0xf0d1ea4828fe7391b41b2b07cc8c4c5fd1831aee6b6a4e5195b236dea20fbde4 │ 666 │ 0.00 │
╰────────────────────────────────────────────────────────────────────┴────────────────────┴──────────────────╯
sui client call --package $PACKAGE --module admin --function publisher_withdraw --args $PUBLISHER $ORGANIZATIONLIST --gas-budget 100000000
# query gas
sui client gas
╭────────────────────────────────────────────────────────────────────┬────────────────────┬──────────────────╮
│ gasCoinId │ mistBalance (MIST) │ suiBalance (SUI) │
├────────────────────────────────────────────────────────────────────┼────────────────────┼──────────────────┤
│ 0x01676de212960b0689245914312ac6be3b4d5cffa0cae91ef527441b894f746a │ 599146796 │ 0.59 │
│ 0x03335f68ff3616af7e000b113c56a5ad53e8e8209784ca0a5623f70997c8d948 │ 3182792690 │ 3.18 │
│ 0x8eb492cda5672e5b87541b744962f927b40b3e67119dc5ba437ee8dfb4785595 │ 1 │ 0.00 │
│ 0xd61fa7c67f44180f8987e9de59cae81e2b26215c6209e0a40bc782402344474d │ 99 │ 0.00 │
│ 0xf0d1ea4828fe7391b41b2b07cc8c4c5fd1831aee6b6a4e5195b236dea20fbde4 │ 666 │ 0.00 │
╰────────────────────────────────────────────────────────────────────┴────────────────────┴──────────────────╯
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!