@mysten/suins
Version:
384 lines (382 loc) • 15.1 kB
JavaScript
import { ALLOWED_METADATA, MAX_U64 } from "./constants.mjs";
import { isNestedSubName, isSubName, zeroCoin } from "./helpers.mjs";
import { calculatePrice, calculatePriceAfterDiscount, handleBasePayment, handlePayment } from "./contracts/suins_payments/payments.mjs";
import { initRegistration, initRenewal, register, renew } from "./contracts/suins/payment.mjs";
import { burnExpired, burnExpiredSubname, setReverseLookup } from "./contracts/suins/controller.mjs";
import { applyCoupon } from "./contracts/suins_coupons/coupon_house.mjs";
import { applyPercentageDiscount } from "./contracts/suins_discounts/discounts.mjs";
import { freeClaim } from "./contracts/suins_discounts/free_claims.mjs";
import { SUI_CLOCK_OBJECT_ID, isValidSuiNSName, normalizeSuiNSName } from "@mysten/sui/utils";
import { bcs } from "@mysten/sui/bcs";
//#region src/suins-transaction.ts
var SuinsTransaction = class {
constructor(client, transaction) {
this.suinsClient = client;
this.transaction = transaction;
}
/**
* Registers a domain for a number of years.
*/
register(params) {
if (params.couponCode && params.discountInfo) throw new Error("Cannot apply both coupon and discount NFT");
const paymentIntent = this.initRegistration(params.domain);
if (params.couponCode) this.applyCoupon(paymentIntent, params.couponCode);
if (params.discountInfo) this.applyDiscount(paymentIntent, params.discountInfo);
const priceAfterDiscount = this.calculatePriceAfterDiscount(paymentIntent, params.coinConfig.type);
const receipt = this.generateReceipt({
paymentIntent,
priceAfterDiscount,
coinConfig: params.coinConfig,
coin: params.coin,
maxAmount: params.maxAmount,
priceInfoObjectId: params.priceInfoObjectId
});
const nft = this.finalizeRegister(receipt);
if (params.years > 1) this.renew({
nft,
years: params.years - 1,
coinConfig: params.coinConfig,
coin: params.coin,
couponCode: params.couponCode,
discountInfo: params.discountInfo,
maxAmount: params.maxAmount,
priceInfoObjectId: params.priceInfoObjectId
});
return nft;
}
/**
* Renews an NFT for a number of years.
*/
renew(params) {
if (params.couponCode && params.discountInfo) throw new Error("Cannot apply both coupon and discount NFT");
const paymentIntent = this.initRenewal(params.nft, params.years);
if (params.couponCode) this.applyCoupon(paymentIntent, params.couponCode);
if (params.discountInfo) this.applyDiscount(paymentIntent, params.discountInfo);
const priceAfterDiscount = this.calculatePriceAfterDiscount(paymentIntent, params.coinConfig.type);
const receipt = this.generateReceipt({
paymentIntent,
priceAfterDiscount,
coinConfig: params.coinConfig,
coin: params.coin,
maxAmount: params.maxAmount,
priceInfoObjectId: params.priceInfoObjectId
});
this.finalizeRenew(receipt, params.nft);
}
initRegistration(domain) {
const config = this.suinsClient.config;
return this.transaction.add(initRegistration({
package: config.packageId,
arguments: {
suins: config.suins,
domain
}
}));
}
initRenewal(nft, years) {
const config = this.suinsClient.config;
return this.transaction.add(initRenewal({
package: config.packageId,
arguments: {
suins: config.suins,
nft: this.transaction.object(nft),
years
}
}));
}
calculatePrice(baseAmount, paymentType, priceInfoObjectId) {
const config = this.suinsClient.config;
return this.transaction.add(calculatePrice({
package: config.payments.packageId,
arguments: {
suins: config.suins,
baseAmount,
priceInfoObject: priceInfoObjectId
},
typeArguments: [paymentType]
}));
}
handleBasePayment(paymentIntent, paymentArg, paymentType) {
const config = this.suinsClient.config;
return this.transaction.add(handleBasePayment({
package: config.payments.packageId,
arguments: {
suins: config.suins,
bbbVault: config.bbb.vault,
intent: paymentIntent,
payment: paymentArg
},
typeArguments: [paymentType]
}));
}
handlePayment(paymentIntent, paymentArg, paymentType, priceInfoObjectId, maxAmount = MAX_U64) {
const config = this.suinsClient.config;
return this.transaction.add(handlePayment({
package: config.payments.packageId,
arguments: {
suins: config.suins,
bbbVault: config.bbb.vault,
intent: paymentIntent,
payment: paymentArg,
priceInfoObject: priceInfoObjectId,
userPriceGuard: maxAmount
},
typeArguments: [paymentType]
}));
}
finalizeRegister(receipt) {
const config = this.suinsClient.config;
return this.transaction.add(register({
package: config.packageId,
arguments: {
receipt,
suins: config.suins
}
}));
}
finalizeRenew(receipt, nft) {
const config = this.suinsClient.config;
return this.transaction.add(renew({
package: config.packageId,
arguments: {
receipt,
suins: config.suins,
nft: this.transaction.object(nft)
}
}));
}
calculatePriceAfterDiscount(paymentIntent, paymentType) {
const config = this.suinsClient.config;
return this.transaction.add(calculatePriceAfterDiscount({
package: config.payments.packageId,
arguments: {
suins: config.suins,
intent: paymentIntent
},
typeArguments: [paymentType]
}));
}
generateReceipt(params) {
if (params.coinConfig.feed === "") {
const payment = params.coin ? this.transaction.splitCoins(this.transaction.object(params.coin), [params.priceAfterDiscount]) : zeroCoin(this.transaction, params.coinConfig.type);
return this.handleBasePayment(params.paymentIntent, payment, params.coinConfig.type);
} else {
const priceInfoObjectId = params.priceInfoObjectId;
if (!priceInfoObjectId) throw new Error("Price info object ID is required for non-base asset purchases");
const price = this.calculatePrice(params.priceAfterDiscount, params.coinConfig.type, priceInfoObjectId);
if (!params.coin) throw new Error("coin input is required");
const payment = this.transaction.splitCoins(this.transaction.object(params.coin), [price]);
return this.handlePayment(params.paymentIntent, payment, params.coinConfig.type, priceInfoObjectId, params.maxAmount);
}
}
/**
* Applies a coupon to the payment intent.
*/
applyCoupon(intent, couponCode) {
const config = this.suinsClient.config;
return this.transaction.add(applyCoupon({
package: config.coupons.packageId,
arguments: {
suins: config.suins,
intent,
couponCode
}
}));
}
/**
* Applies a discount to the payment intent.
*/
applyDiscount(intent, discountInfo) {
const config = this.suinsClient.config;
if (discountInfo.isFreeClaim) this.transaction.add(freeClaim({
package: config.discountsPackage.packageId,
arguments: {
self: config.discountsPackage.discountHouseId,
suins: config.suins,
intent,
object: this.transaction.object(discountInfo.discountNft)
},
typeArguments: [discountInfo.type]
}));
else this.transaction.add(applyPercentageDiscount({
package: config.discountsPackage.packageId,
arguments: {
self: config.discountsPackage.discountHouseId,
intent,
suins: config.suins,
_: this.transaction.object(discountInfo.discountNft)
},
typeArguments: [discountInfo.type]
}));
}
/**
* Creates a subdomain.
*/
createSubName({ parentNft, name, expirationTimestampMs, allowChildCreation, allowTimeExtension }) {
if (!isValidSuiNSName(name)) throw new Error("Invalid SuiNS name");
const isParentSubdomain = isNestedSubName(name);
if (!this.suinsClient.config.suins) throw new Error("SuiNS Object ID not found");
if (!this.suinsClient.config.subNamesPackageId) throw new Error("Subnames package ID not found");
if (isParentSubdomain && !this.suinsClient.config.tempSubdomainsProxyPackageId) throw new Error("Subnames proxy package ID not found");
return this.transaction.moveCall({
target: isParentSubdomain ? `${this.suinsClient.config.tempSubdomainsProxyPackageId}::subdomain_proxy::new` : `${this.suinsClient.config.subNamesPackageId}::subdomains::new`,
arguments: [
this.transaction.object(this.suinsClient.config.suins),
this.transaction.object(parentNft),
this.transaction.object(SUI_CLOCK_OBJECT_ID),
this.transaction.pure.string(normalizeSuiNSName(name, "dot")),
this.transaction.pure.u64(expirationTimestampMs),
this.transaction.pure.bool(!!allowChildCreation),
this.transaction.pure.bool(!!allowTimeExtension)
]
});
}
/**
* Builds the PTB to create a leaf subdomain.
* Parent can be a `SuinsRegistration` or a `SubDomainRegistration` object.
* Can be passed in as an ID or a TransactionArgument.
*/
createLeafSubName({ parentNft, name, targetAddress }) {
if (!isValidSuiNSName(name)) throw new Error("Invalid SuiNS name");
const isParentSubdomain = isNestedSubName(name);
if (!this.suinsClient.config.suins) throw new Error("SuiNS Object ID not found");
if (!this.suinsClient.config.subNamesPackageId) throw new Error("Subnames package ID not found");
if (isParentSubdomain && !this.suinsClient.config.tempSubdomainsProxyPackageId) throw new Error("Subnames proxy package ID not found");
this.transaction.moveCall({
target: isParentSubdomain ? `${this.suinsClient.config.tempSubdomainsProxyPackageId}::subdomain_proxy::new_leaf` : `${this.suinsClient.config.subNamesPackageId}::subdomains::new_leaf`,
arguments: [
this.transaction.object(this.suinsClient.config.suins),
this.transaction.object(parentNft),
this.transaction.object(SUI_CLOCK_OBJECT_ID),
this.transaction.pure.string(normalizeSuiNSName(name, "dot")),
this.transaction.pure.address(targetAddress)
]
});
}
/**
* Removes a leaf subname.
*/
removeLeafSubName({ parentNft, name }) {
if (!isValidSuiNSName(name)) throw new Error("Invalid SuiNS name");
const isParentSubdomain = isNestedSubName(name);
if (!isSubName(name)) throw new Error("This can only be invoked for subnames");
if (!this.suinsClient.config.suins) throw new Error("SuiNS Object ID not found");
if (!this.suinsClient.config.subNamesPackageId) throw new Error("Subnames package ID not found");
if (isParentSubdomain && !this.suinsClient.config.tempSubdomainsProxyPackageId) throw new Error("Subnames proxy package ID not found");
this.transaction.moveCall({
target: isParentSubdomain ? `${this.suinsClient.config.tempSubdomainsProxyPackageId}::subdomain_proxy::remove_leaf` : `${this.suinsClient.config.subNamesPackageId}::subdomains::remove_leaf`,
arguments: [
this.transaction.object(this.suinsClient.config.suins),
this.transaction.object(parentNft),
this.transaction.object(SUI_CLOCK_OBJECT_ID),
this.transaction.pure.string(normalizeSuiNSName(name, "dot"))
]
});
}
/**
* Sets the target address of an NFT.
*/
setTargetAddress({ nft, address, isSubname }) {
if (isSubname && !this.suinsClient.config.tempSubdomainsProxyPackageId) throw new Error("Subnames proxy package ID not found");
this.transaction.moveCall({
target: isSubname ? `${this.suinsClient.config.tempSubdomainsProxyPackageId}::subdomain_proxy::set_target_address` : `${this.suinsClient.config.packageId}::controller::set_target_address`,
arguments: [
this.transaction.object(this.suinsClient.config.suins),
this.transaction.object(nft),
this.transaction.pure(bcs.option(bcs.Address).serialize(address).toBytes()),
this.transaction.object(SUI_CLOCK_OBJECT_ID)
]
});
}
/**
* Sets a default name for the user.
*/
setDefault(name) {
if (!isValidSuiNSName(name)) throw new Error("Invalid SuiNS name");
if (!this.suinsClient.config.suins) throw new Error("SuiNS Object ID not found");
this.transaction.add(setReverseLookup({
package: this.suinsClient.config.packageId,
arguments: {
suins: this.suinsClient.config.suins,
domainName: normalizeSuiNSName(name, "dot")
}
}));
}
/**
* Edits the setup of a subname.
*/
editSetup({ parentNft, name, allowChildCreation, allowTimeExtension }) {
if (!isValidSuiNSName(name)) throw new Error("Invalid SuiNS name");
const isParentSubdomain = isNestedSubName(name);
if (!this.suinsClient.config.suins) throw new Error("SuiNS Object ID not found");
if (!isParentSubdomain && !this.suinsClient.config.subNamesPackageId) throw new Error("Subnames package ID not found");
if (isParentSubdomain && !this.suinsClient.config.tempSubdomainsProxyPackageId) throw new Error("Subnames proxy package ID not found");
this.transaction.moveCall({
target: isParentSubdomain ? `${this.suinsClient.config.tempSubdomainsProxyPackageId}::subdomain_proxy::edit_setup` : `${this.suinsClient.config.subNamesPackageId}::subdomains::edit_setup`,
arguments: [
this.transaction.object(this.suinsClient.config.suins),
this.transaction.object(parentNft),
this.transaction.object(SUI_CLOCK_OBJECT_ID),
this.transaction.pure.string(normalizeSuiNSName(name, "dot")),
this.transaction.pure.bool(!!allowChildCreation),
this.transaction.pure.bool(!!allowTimeExtension)
]
});
}
/**
* Extends the expiration of a subname.
*/
extendExpiration({ nft, expirationTimestampMs }) {
if (!this.suinsClient.config.suins) throw new Error("SuiNS Object ID not found");
if (!this.suinsClient.config.subNamesPackageId) throw new Error("Subnames package ID not found");
this.transaction.moveCall({
target: `${this.suinsClient.config.subNamesPackageId}::subdomains::extend_expiration`,
arguments: [
this.transaction.object(this.suinsClient.config.suins),
this.transaction.object(nft),
this.transaction.pure.u64(expirationTimestampMs)
]
});
}
/**
* Sets the user data of an NFT.
*/
setUserData({ nft, value, key, isSubname }) {
if (!this.suinsClient.config.suins) throw new Error("SuiNS Object ID not found");
if (isSubname && !this.suinsClient.config.tempSubdomainsProxyPackageId) throw new Error("Subnames proxy package ID not found");
if (!Object.values(ALLOWED_METADATA).some((x) => x === key)) throw new Error("Invalid key");
this.transaction.moveCall({
target: isSubname ? `${this.suinsClient.config.tempSubdomainsProxyPackageId}::subdomain_proxy::set_user_data` : `${this.suinsClient.config.packageId}::controller::set_user_data`,
arguments: [
this.transaction.object(this.suinsClient.config.suins),
this.transaction.object(nft),
this.transaction.pure.string(key),
this.transaction.pure.string(value),
this.transaction.object(SUI_CLOCK_OBJECT_ID)
]
});
}
/**
* Burns an expired NFT to collect storage rebates.
*/
burnExpired({ nft, isSubname }) {
if (!this.suinsClient.config.suins) throw new Error("SuiNS Object ID not found");
if (isSubname) this.transaction.add(burnExpiredSubname({
package: this.suinsClient.config.packageId,
arguments: {
suins: this.suinsClient.config.suins,
nft: this.transaction.object(nft)
}
}));
else this.transaction.add(burnExpired({
package: this.suinsClient.config.packageId,
arguments: {
suins: this.suinsClient.config.suins,
nft: this.transaction.object(nft)
}
}));
}
};
//#endregion
export { SuinsTransaction };
//# sourceMappingURL=suins-transaction.mjs.map