@stacks/bns
Version:
Library for working with the Stacks Blockchain Naming System BNS.
312 lines • 12.3 kB
JavaScript
import { intToBigInt, utf8ToBytes } from '@stacks/common';
import { ClarityType, bufferCV, bufferCVFromString, cvToString, fetchCallReadOnlyFunction, getAddressFromPrivateKey, getCVTypeString, hash160, makeRandomPrivKey, makeUnsignedContractCall, noneCV, publicKeyToAddress, someCV, standardPrincipalCV, tupleCV, uintCV, } from '@stacks/transactions';
import { decodeFQN, getZonefileHash } from './utils';
export const BNS_CONTRACT_NAME = 'bns';
async function makeBnsContractCall(options) {
const txOptions = {
contractAddress: options.network.bootAddress,
contractName: BNS_CONTRACT_NAME,
functionName: options.functionName,
functionArgs: options.functionArgs,
publicKey: options.publicKey,
validateWithAbi: false,
network: options.network,
postConditions: options.postConditions,
};
return makeUnsignedContractCall(txOptions);
}
async function callReadOnlyBnsFunction(options) {
return fetchCallReadOnlyFunction({
...options,
contractAddress: options.network.bootAddress,
contractName: BNS_CONTRACT_NAME,
});
}
export async function canRegisterName({ fullyQualifiedName, network, }) {
const bnsFunctionName = 'can-name-be-registered';
const { subdomain, namespace, name } = decodeFQN(fullyQualifiedName);
if (subdomain) {
throw new Error('Cannot register a subdomain using registerName');
}
const randomPrivateKey = makeRandomPrivKey();
const randomAddress = getAddressFromPrivateKey(randomPrivateKey);
return callReadOnlyBnsFunction({
functionName: bnsFunctionName,
senderAddress: randomAddress,
functionArgs: [bufferCVFromString(namespace), bufferCVFromString(name)],
network,
}).then((responseCV) => {
if (responseCV.type === ClarityType.ResponseOk) {
return responseCV.value.type === ClarityType.BoolTrue;
}
else {
return false;
}
});
}
export async function getNamespacePrice({ namespace, network, }) {
const bnsFunctionName = 'get-namespace-price';
const randomPrivateKey = makeRandomPrivKey();
const randomAddress = getAddressFromPrivateKey(randomPrivateKey);
return callReadOnlyBnsFunction({
functionName: bnsFunctionName,
senderAddress: randomAddress,
functionArgs: [bufferCVFromString(namespace)],
network,
}).then((responseCV) => {
if (responseCV.type === ClarityType.ResponseOk) {
if (responseCV.value.type === ClarityType.Int || responseCV.value.type === ClarityType.UInt) {
return BigInt(responseCV.value.value);
}
else {
throw new Error('Response did not contain a number');
}
}
else if (responseCV.type === ClarityType.ResponseErr) {
throw new Error(cvToString(responseCV.value));
}
else {
throw new Error(`Unexpected Clarity Value type: ${getCVTypeString(responseCV)}`);
}
});
}
export async function getNamePrice({ fullyQualifiedName, network, }) {
const bnsFunctionName = 'get-name-price';
const { subdomain, namespace, name } = decodeFQN(fullyQualifiedName);
if (subdomain) {
throw new Error('Cannot get subdomain name price');
}
const randomPrivateKey = makeRandomPrivKey();
const randomAddress = getAddressFromPrivateKey(randomPrivateKey);
return callReadOnlyBnsFunction({
functionName: bnsFunctionName,
senderAddress: randomAddress,
functionArgs: [bufferCVFromString(namespace), bufferCVFromString(name)],
network,
}).then((responseCV) => {
if (responseCV.type === ClarityType.ResponseOk) {
if (responseCV.value.type === ClarityType.Int || responseCV.value.type === ClarityType.UInt) {
return BigInt(responseCV.value.value);
}
else {
throw new Error('Response did not contain a number');
}
}
else {
const errorResponse = responseCV;
throw new Error(cvToString(errorResponse.value));
}
});
}
export async function buildPreorderNamespaceTx({ namespace, salt, stxToBurn, publicKey, network, }) {
const bnsFunctionName = 'namespace-preorder';
const saltedNamespaceBytes = utf8ToBytes(`${namespace}${salt}`);
const hashedSaltedNamespace = hash160(saltedNamespaceBytes);
const burnSTXPostCondition = {
type: 'stx-postcondition',
address: publicKeyToAddress(network.addressVersion.singleSig, publicKey),
condition: 'eq',
amount: intToBigInt(stxToBurn),
};
return makeBnsContractCall({
functionName: bnsFunctionName,
functionArgs: [bufferCV(hashedSaltedNamespace), uintCV(stxToBurn)],
publicKey,
network,
postConditions: [burnSTXPostCondition],
});
}
export async function buildRevealNamespaceTx({ namespace, salt, priceFunction, lifetime, namespaceImportAddress, publicKey, network, }) {
const bnsFunctionName = 'namespace-reveal';
return makeBnsContractCall({
functionName: bnsFunctionName,
functionArgs: [
bufferCVFromString(namespace),
bufferCVFromString(salt),
uintCV(priceFunction.base),
uintCV(priceFunction.coefficient),
uintCV(priceFunction.b1),
uintCV(priceFunction.b2),
uintCV(priceFunction.b3),
uintCV(priceFunction.b4),
uintCV(priceFunction.b5),
uintCV(priceFunction.b6),
uintCV(priceFunction.b7),
uintCV(priceFunction.b8),
uintCV(priceFunction.b9),
uintCV(priceFunction.b10),
uintCV(priceFunction.b11),
uintCV(priceFunction.b12),
uintCV(priceFunction.b13),
uintCV(priceFunction.b14),
uintCV(priceFunction.b15),
uintCV(priceFunction.b16),
uintCV(priceFunction.nonAlphaDiscount),
uintCV(priceFunction.noVowelDiscount),
uintCV(lifetime),
standardPrincipalCV(namespaceImportAddress),
],
publicKey,
network,
});
}
export async function buildImportNameTx({ namespace, name, beneficiary, zonefile, publicKey, network, }) {
const bnsFunctionName = 'name-import';
const zonefileHash = getZonefileHash(zonefile);
return makeBnsContractCall({
functionName: bnsFunctionName,
functionArgs: [
bufferCVFromString(namespace),
bufferCVFromString(name),
standardPrincipalCV(beneficiary),
bufferCV(zonefileHash),
],
publicKey,
network,
});
}
export async function buildReadyNamespaceTx({ namespace, publicKey, network, }) {
const bnsFunctionName = 'namespace-ready';
return makeBnsContractCall({
functionName: bnsFunctionName,
functionArgs: [bufferCVFromString(namespace)],
publicKey,
network,
});
}
export async function buildPreorderNameTx({ fullyQualifiedName, salt, stxToBurn, publicKey, network, }) {
const bnsFunctionName = 'name-preorder';
const { subdomain } = decodeFQN(fullyQualifiedName);
if (subdomain) {
throw new Error('Cannot preorder a subdomain using preorderName()');
}
const saltedNamesBytes = utf8ToBytes(`${fullyQualifiedName}${salt}`);
const hashedSaltedName = hash160(saltedNamesBytes);
const burnSTXPostCondition = {
type: 'stx-postcondition',
address: publicKeyToAddress(network.addressVersion.singleSig, publicKey),
condition: 'eq',
amount: intToBigInt(stxToBurn),
};
return makeBnsContractCall({
functionName: bnsFunctionName,
functionArgs: [bufferCV(hashedSaltedName), uintCV(stxToBurn)],
publicKey,
network,
postConditions: [burnSTXPostCondition],
});
}
export async function buildRegisterNameTx({ fullyQualifiedName, salt, zonefile, publicKey, network, }) {
const bnsFunctionName = 'name-register';
const { subdomain, namespace, name } = decodeFQN(fullyQualifiedName);
if (subdomain) {
throw new Error('Cannot register a subdomain using registerName()');
}
const zonefileHash = getZonefileHash(zonefile);
return makeBnsContractCall({
functionName: bnsFunctionName,
functionArgs: [
bufferCVFromString(namespace),
bufferCVFromString(name),
bufferCVFromString(salt),
bufferCV(zonefileHash),
],
network,
publicKey,
});
}
export async function buildUpdateNameTx({ fullyQualifiedName, zonefile, publicKey, network, }) {
const bnsFunctionName = 'name-update';
const { subdomain, namespace, name } = decodeFQN(fullyQualifiedName);
if (subdomain) {
throw new Error('Cannot update a subdomain using updateName()');
}
const zonefileHash = getZonefileHash(zonefile);
return makeBnsContractCall({
functionName: bnsFunctionName,
functionArgs: [bufferCVFromString(namespace), bufferCVFromString(name), bufferCV(zonefileHash)],
publicKey,
network,
});
}
export async function buildTransferNameTx({ fullyQualifiedName, newOwnerAddress, zonefile, publicKey, network, }) {
const bnsFunctionName = 'name-transfer';
const { subdomain, namespace, name } = decodeFQN(fullyQualifiedName);
if (subdomain) {
throw new Error('Cannot transfer a subdomain using transferName()');
}
const functionArgs = [
bufferCVFromString(namespace),
bufferCVFromString(name),
standardPrincipalCV(newOwnerAddress),
zonefile ? someCV(bufferCV(getZonefileHash(zonefile))) : noneCV(),
];
const postConditionSender = {
type: 'nft-postcondition',
address: publicKeyToAddress(network.addressVersion.singleSig, publicKey),
condition: 'sent',
asset: `${network.bootAddress}.bns::names`,
assetId: tupleCV({
name: bufferCVFromString(name),
namespace: bufferCVFromString(namespace),
}),
};
const postConditionReceiver = {
type: 'nft-postcondition',
address: newOwnerAddress,
condition: 'not-sent',
asset: `${network.bootAddress}.bns::names`,
assetId: tupleCV({
name: bufferCVFromString(name),
namespace: bufferCVFromString(namespace),
}),
};
return makeBnsContractCall({
functionName: bnsFunctionName,
functionArgs,
publicKey,
network,
postConditions: [postConditionSender, postConditionReceiver],
});
}
export async function buildRevokeNameTx({ fullyQualifiedName, publicKey, network, }) {
const bnsFunctionName = 'name-revoke';
const { subdomain, namespace, name } = decodeFQN(fullyQualifiedName);
if (subdomain) {
throw new Error('Cannot revoke a subdomain using revokeName()');
}
return makeBnsContractCall({
functionName: bnsFunctionName,
functionArgs: [bufferCVFromString(namespace), bufferCVFromString(name)],
publicKey,
network,
});
}
export async function buildRenewNameTx({ fullyQualifiedName, stxToBurn, newOwnerAddress, zonefile, publicKey, network, }) {
const bnsFunctionName = 'name-renewal';
const { subdomain, namespace, name } = decodeFQN(fullyQualifiedName);
if (subdomain) {
throw new Error('Cannot renew a subdomain using renewName()');
}
const functionArgs = [
bufferCVFromString(namespace),
bufferCVFromString(name),
uintCV(stxToBurn),
newOwnerAddress ? someCV(standardPrincipalCV(newOwnerAddress)) : noneCV(),
zonefile ? someCV(bufferCV(getZonefileHash(zonefile))) : noneCV(),
];
const burnSTXPostCondition = {
type: 'stx-postcondition',
address: publicKeyToAddress(network.addressVersion.singleSig, publicKey),
condition: 'eq',
amount: intToBigInt(stxToBurn),
};
return makeBnsContractCall({
functionName: bnsFunctionName,
functionArgs,
publicKey,
network,
postConditions: [burnSTXPostCondition],
});
}
//# sourceMappingURL=index.js.map