UNPKG

@stacks/bns

Version:

Library for working with the Stacks Blockchain Naming System BNS.

312 lines 12.3 kB
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