diff --git a/package.json b/package.json index 2de6387..c9bb01a 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,6 @@ "url": "git+https://github.com/friedger/stacks-send-many.git" }, "dependencies": { - "@scure/bip32": "^1.7.0", - "@scure/btc-signer": "^1.8.1", "@stacks/blockchain-api-client": "^8.13.5", "@stacks/common": "7.3.1", "@stacks/connect": "^8.2.3", @@ -28,6 +26,7 @@ "@walletconnect/types": "^2.23.1", "@walletconnect/utils": "^2.23.1", "c32check": "^2.0.0", + "clarity-abitype": "0.3.1", "global": "^4.4.0", "jdenticon": "^3.3.0", "jotai": "^2.16.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0f9092b..08db8d9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,12 +8,6 @@ importers: .: dependencies: - '@scure/bip32': - specifier: ^1.7.0 - version: 1.7.0 - '@scure/btc-signer': - specifier: ^1.8.1 - version: 1.8.1 '@stacks/blockchain-api-client': specifier: ^8.13.5 version: 8.13.5 @@ -62,6 +56,9 @@ importers: c32check: specifier: ^2.0.0 version: 2.0.0 + clarity-abitype: + specifier: 0.3.1 + version: 0.3.1(@stacks/network@7.3.1)(@stacks/transactions@7.3.1)(typescript@5.9.3) global: specifier: ^4.4.0 version: 4.4.0 @@ -2720,6 +2717,23 @@ packages: cjs-module-lexer@1.4.3: resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} + clarity-abitype@0.3.1: + resolution: {integrity: sha512-zT1T4MuMkNKapXShAy6S+1L40riO2TXFaMYFqVsEtP83VNd75OSKdIc0mTzP7L6SfVQoYe1XKhsnwzYelKc9+A==} + peerDependencies: + '@stacks/clarinet-sdk': '>= 3.0.0' + '@stacks/network': '>= 7.3.0' + '@stacks/transactions': '>= 7.3.0' + typescript: '>=5.1.0' + peerDependenciesMeta: + '@stacks/clarinet-sdk': + optional: true + '@stacks/network': + optional: true + '@stacks/transactions': + optional: true + typescript: + optional: true + clean-deep@3.4.0: resolution: {integrity: sha512-Lo78NV5ItJL/jl+B5w0BycAisaieJGXK1qYi/9m4SjR8zbqmrUtO7Yhro40wEShGmmxs/aJLI/A+jNhdkXK8mw==} engines: {node: '>=4'} @@ -9937,6 +9951,12 @@ snapshots: cjs-module-lexer@1.4.3: {} + clarity-abitype@0.3.1(@stacks/network@7.3.1)(@stacks/transactions@7.3.1)(typescript@5.9.3): + optionalDependencies: + '@stacks/network': 7.3.1 + '@stacks/transactions': 7.3.1 + typescript: 5.9.3 + clean-deep@3.4.0: dependencies: lodash.isempty: 4.4.0 diff --git a/src/components/Address.tsx b/src/components/Address.tsx index 722c0d1..c58f4de 100644 --- a/src/components/Address.tsx +++ b/src/components/Address.tsx @@ -1,4 +1,3 @@ -import { ClarityType } from '@stacks/transactions'; import toUnicode from 'punycode2/to-unicode'; import { useEffect, useMemo, useState } from 'react'; import { hex_to_ascii } from '../lib/string-utils'; @@ -15,17 +14,19 @@ export function Address({ addr }: { addr: string }) { const [showAscii, setShowAscii] = useState(false); useEffect(() => { - getNameFromAddress(addr).then(data => { - if (data.type === ClarityType.ResponseOk && data.value.type === ClarityType.OptionalSome) { - const { name, namespace } = data.value.value.value; - - const nameStr = hex_to_ascii(name.value); + const fn = async (addr: string) => { + const data = await getNameFromAddress(addr); + if (data.ok) { + const { name, namespace } = data.ok; + const nameStr = hex_to_ascii(name); const namePunycodeStr = toUnicode(nameStr); - const namespaceStr = hex_to_ascii(namespace.value); + const namespaceStr = hex_to_ascii(namespace); setNameAscii(nameStr === namePunycodeStr ? undefined : `${nameStr}.${namespaceStr}`); setNameOrAddress(`${namePunycodeStr}.${namespaceStr}`); } - }); + }; + + fn(addr); }, [addr]); return ( diff --git a/src/components/SBTCInfo.tsx b/src/components/SBTCInfo.tsx index 51729ae..5c75a4a 100644 --- a/src/components/SBTCInfo.tsx +++ b/src/components/SBTCInfo.tsx @@ -1,6 +1,7 @@ -import { cvToString, fetchCallReadOnlyFunction, PrincipalCV, TupleCV } from '@stacks/transactions'; import { useEffect, useState } from 'react'; +import { typedCallReadOnlyFunction } from 'clarity-abitype'; import { NETWORK } from '../lib/constants'; +import { sbtcRegistryAbi } from '../lib/abi'; export function SBTCInfo({ assetId }: { assetId: string }) { const [info, setInfo] = useState(); @@ -9,17 +10,16 @@ export function SBTCInfo({ assetId }: { assetId: string }) { const fn = async () => { const [contractId, _] = assetId.split('::'); const [contractAddress] = contractId.split('.'); - const response = (await fetchCallReadOnlyFunction({ + const response = await typedCallReadOnlyFunction({ + abi: sbtcRegistryAbi, contractAddress, contractName: 'sbtc-registry', functionName: 'get-current-signer-data', - functionArgs: [], senderAddress: contractAddress, network: NETWORK, - })) as TupleCV<{ 'current-signer-principal': PrincipalCV }>; - setInfo( - `Current sBTC signer Stacks address: ${cvToString(response.value['current-signer-principal'])}` - ); + }); + + setInfo(`Current sBTC signer Stacks address: ${response['current-signer-principal']}`); }; fn().catch(e => { setInfo(`Failed to load signer data. (${e.message})`); diff --git a/src/components/SendManyInputContainer.tsx b/src/components/SendManyInputContainer.tsx index 4dd8877..57d8c15 100644 --- a/src/components/SendManyInputContainer.tsx +++ b/src/components/SendManyInputContainer.tsx @@ -1,15 +1,12 @@ import { AuthType, bufferCVFromString, - ClarityType, contractPrincipalCV, - cvToString, listCV, noneCV, Pc, PostConditionMode, principalCV, - PrincipalCV, someCV, standardPrincipalCV, trueCV, @@ -41,8 +38,9 @@ export type Row = { stx: string; memo?: string; error?: string; - toCV?: PrincipalCV; + toCV?: string; }; + const addrToCV = (addr: string) => { const toParts = addr.split('.'); if (toParts.length === 1) { @@ -52,28 +50,17 @@ const addrToCV = (addr: string) => { } }; -const addToCVValues = async (parts: T[]) => { - return Promise.all( - parts.map(async p => { - if (p.to === '') { - return p; - } - try { - return { ...p, toCV: addrToCV(p.to) }; - } catch (e) { - try { - const owner = await getNameInfo(toAscii(p.to)); - if (owner.type === ClarityType.OptionalSome) { - return { ...p, toCV: owner.value.value.owner }; - } else { - return { ...p, error: `No address for ${p.to}` }; - } - } catch (e2) { - return { ...p, error: `${p.to} not found` }; - } - } - }) - ); +const resolveRecipient = async (recipient: string): Promise => { + try { + c32addressDecode(recipient); + return recipient; + } catch (e) { + const owner = await getNameInfo(toAscii(recipient)); + if (owner?.owner) { + return owner.owner; + } + throw new Error(`No address for ${recipient}`); + } }; function nonEmptyPart(p: Row) { @@ -168,7 +155,7 @@ export function SendManyInputContainer({ setNamesResolved(!!p.toCV); return ( - {p.error || (p.toCV ?
: '...')}:{' '} + {p.error || (p.toCV ?
: '...')}:{' '}

@@ -371,21 +358,36 @@ export function SendManyInputContainer({ let { parts, total, hasMemos } = getPartsFromRows( useAssetForFees ? cloneAndAddFees(rows) : rows ); - const updatedParts = await addToCVValues(parts); - let invalidNames = updatedParts.filter(r => !!r.error); - if (invalidNames.length > 0) { - updatePreview({ parts: updatedParts, total, hasMemos }); + + const resolvedPartsWithErrors = await Promise.all( + parts.map(async p => { + if (p.to === '') { + return p; + } + try { + return { ...p, toCV: await resolveRecipient(p.to) }; + } catch (e) { + return { ...p, error: `${p.to} not found` }; + } + }) + ); + + const errors = resolvedPartsWithErrors.filter(r => !!r.error); + if (errors.length > 0) { + updatePreview({ parts: resolvedPartsWithErrors, total, hasMemos }); setLoading(false); setStatus('Please verify receivers'); return; } + if (!namesResolved) { - updatePreview({ parts: updatedParts, total, hasMemos }); + updatePreview({ parts: resolvedPartsWithErrors, total, hasMemos }); setLoading(false); setNamesResolved(true); return; } - const nonEmptyParts = updatedParts.filter(nonEmptyPart); + + const nonEmptyParts = resolvedPartsWithErrors.filter(nonEmptyPart); console.log(nonEmptyParts[0]); const firstMemo = nonEmptyParts.length > 0 && nonEmptyParts[0].memo ? nonEmptyParts[0].memo.trim() : ''; @@ -406,13 +408,13 @@ export function SendManyInputContainer({ nonEmptyParts.map(p => { return hasMemos ? tupleCV({ - to: p.toCV!, + to: addrToCV(p.toCV!), ustx: uintCV(p.ustx), memo: bufferCVFromString( firstMemoForAll ? firstMemo : p.memo ? p.memo.trim() : '' ), }) - : tupleCV({ to: p.toCV!, ustx: uintCV(p.ustx) }); + : tupleCV({ to: addrToCV(p.toCV!), ustx: uintCV(p.ustx) }); }) ), ], @@ -431,7 +433,7 @@ export function SendManyInputContainer({ functionArgs: [ nonEmptyParts.map(p => { return tupleCV({ - to: p.toCV!, + to: addrToCV(p.toCV!), 'xbtc-in-sats': uintCV(p.ustx), memo: bufferCVFromString( hasMemos ? (firstMemoForAll ? firstMemo : p.memo ? p.memo.trim() : '') : '' @@ -464,7 +466,7 @@ export function SendManyInputContainer({ listCV( nonEmptyParts.map(p => { return tupleCV({ - to: p.toCV!, + to: addrToCV(p.toCV!), sender: principalCV(ownerStxAddress), amount: uintCV(p.ustx), memo: hasMemos @@ -503,7 +505,7 @@ export function SendManyInputContainer({ listCV( nonEmptyParts.map(p => { return tupleCV({ - to: p.toCV!, + to: addrToCV(p.toCV!), amount: uintCV(p.ustx), memo: hasMemos ? firstMemoForAll diff --git a/src/lib/abi.ts b/src/lib/abi.ts new file mode 100644 index 0000000..7c4a351 --- /dev/null +++ b/src/lib/abi.ts @@ -0,0 +1,1384 @@ +export const sbtcRegistryAbi = { + functions: [ + { + name: 'increment-last-withdrawal-request-id', + access: 'private', + args: [], + outputs: { type: 'uint128' }, + }, + { + name: 'complete-deposit', + access: 'public', + args: [ + { name: 'txid', type: { buffer: { length: 32 } } }, + { name: 'vout-index', type: 'uint128' }, + { name: 'amount', type: 'uint128' }, + { name: 'recipient', type: 'principal' }, + { name: 'burn-hash', type: { buffer: { length: 32 } } }, + { name: 'burn-height', type: 'uint128' }, + { name: 'sweep-txid', type: { buffer: { length: 32 } } }, + ], + outputs: { type: { response: { ok: 'bool', error: 'uint128' } } }, + }, + { + name: 'complete-withdrawal-accept', + access: 'public', + args: [ + { name: 'request-id', type: 'uint128' }, + { name: 'bitcoin-txid', type: { buffer: { length: 32 } } }, + { name: 'output-index', type: 'uint128' }, + { name: 'signer-bitmap', type: 'uint128' }, + { name: 'fee', type: 'uint128' }, + { name: 'burn-hash', type: { buffer: { length: 32 } } }, + { name: 'burn-height', type: 'uint128' }, + { name: 'sweep-txid', type: { buffer: { length: 32 } } }, + ], + outputs: { type: { response: { ok: 'bool', error: 'uint128' } } }, + }, + { + name: 'complete-withdrawal-reject', + access: 'public', + args: [ + { name: 'request-id', type: 'uint128' }, + { name: 'signer-bitmap', type: 'uint128' }, + ], + outputs: { type: { response: { ok: 'bool', error: 'uint128' } } }, + }, + { + name: 'create-withdrawal-request', + access: 'public', + args: [ + { name: 'amount', type: 'uint128' }, + { name: 'max-fee', type: 'uint128' }, + { name: 'sender', type: 'principal' }, + { + name: 'recipient', + type: { + tuple: [ + { name: 'hashbytes', type: { buffer: { length: 32 } } }, + { name: 'version', type: { buffer: { length: 1 } } }, + ], + }, + }, + { name: 'height', type: 'uint128' }, + ], + outputs: { type: { response: { ok: 'uint128', error: 'uint128' } } }, + }, + { + name: 'rotate-keys', + access: 'public', + args: [ + { name: 'new-keys', type: { list: { type: { buffer: { length: 33 } }, length: 128 } } }, + { name: 'new-address', type: 'principal' }, + { name: 'new-aggregate-pubkey', type: { buffer: { length: 33 } } }, + { name: 'new-signature-threshold', type: 'uint128' }, + ], + outputs: { type: { response: { ok: 'bool', error: 'uint128' } } }, + }, + { + name: 'update-protocol-contract', + access: 'public', + args: [ + { name: 'contract-type', type: { buffer: { length: 1 } } }, + { name: 'new-contract', type: 'principal' }, + ], + outputs: { type: { response: { ok: 'bool', error: 'uint128' } } }, + }, + { + name: 'get-active-protocol', + access: 'read_only', + args: [{ name: 'contract-flag', type: { buffer: { length: 1 } } }], + outputs: { type: { optional: 'principal' } }, + }, + { + name: 'get-completed-deposit', + access: 'read_only', + args: [ + { name: 'txid', type: { buffer: { length: 32 } } }, + { name: 'vout-index', type: 'uint128' }, + ], + outputs: { + type: { + optional: { + tuple: [ + { name: 'amount', type: 'uint128' }, + { name: 'recipient', type: 'principal' }, + { name: 'sweep-burn-hash', type: { buffer: { length: 32 } } }, + { name: 'sweep-burn-height', type: 'uint128' }, + { name: 'sweep-txid', type: { buffer: { length: 32 } } }, + ], + }, + }, + }, + }, + { + name: 'get-completed-withdrawal-sweep-data', + access: 'read_only', + args: [{ name: 'id', type: 'uint128' }], + outputs: { + type: { + optional: { + tuple: [ + { name: 'sweep-burn-hash', type: { buffer: { length: 32 } } }, + { name: 'sweep-burn-height', type: 'uint128' }, + { name: 'sweep-txid', type: { buffer: { length: 32 } } }, + ], + }, + }, + }, + }, + { + name: 'get-current-aggregate-pubkey', + access: 'read_only', + args: [], + outputs: { type: { buffer: { length: 33 } } }, + }, + { + name: 'get-current-signer-data', + access: 'read_only', + args: [], + outputs: { + type: { + tuple: [ + { name: 'current-aggregate-pubkey', type: { buffer: { length: 33 } } }, + { name: 'current-signature-threshold', type: 'uint128' }, + { name: 'current-signer-principal', type: 'principal' }, + { + name: 'current-signer-set', + type: { list: { type: { buffer: { length: 33 } }, length: 128 } }, + }, + ], + }, + }, + }, + { + name: 'get-current-signer-principal', + access: 'read_only', + args: [], + outputs: { type: 'principal' }, + }, + { + name: 'get-current-signer-set', + access: 'read_only', + args: [], + outputs: { type: { list: { type: { buffer: { length: 33 } }, length: 128 } } }, + }, + { + name: 'get-deposit-status', + access: 'read_only', + args: [ + { name: 'txid', type: { buffer: { length: 32 } } }, + { name: 'vout-index', type: 'uint128' }, + ], + outputs: { type: { optional: 'bool' } }, + }, + { + name: 'get-withdrawal-request', + access: 'read_only', + args: [{ name: 'id', type: 'uint128' }], + outputs: { + type: { + optional: { + tuple: [ + { name: 'amount', type: 'uint128' }, + { name: 'block-height', type: 'uint128' }, + { name: 'max-fee', type: 'uint128' }, + { + name: 'recipient', + type: { + tuple: [ + { name: 'hashbytes', type: { buffer: { length: 32 } } }, + { name: 'version', type: { buffer: { length: 1 } } }, + ], + }, + }, + { name: 'sender', type: 'principal' }, + { name: 'status', type: { optional: 'bool' } }, + ], + }, + }, + }, + }, + { + name: 'is-protocol-caller', + access: 'read_only', + args: [ + { name: 'contract-flag', type: { buffer: { length: 1 } } }, + { name: 'contract', type: 'principal' }, + ], + outputs: { type: { response: { ok: 'bool', error: 'uint128' } } }, + }, + ], + variables: [ + { + name: 'ERR_AGG_PUBKEY_REPLAY', + type: { response: { ok: 'none', error: 'uint128' } }, + access: 'constant', + }, + { + name: 'ERR_INVALID_REQUEST_ID', + type: { response: { ok: 'none', error: 'uint128' } }, + access: 'constant', + }, + { + name: 'ERR_UNAUTHORIZED', + type: { response: { ok: 'none', error: 'uint128' } }, + access: 'constant', + }, + { name: 'deposit-role', type: { buffer: { length: 1 } }, access: 'constant' }, + { name: 'governance-role', type: { buffer: { length: 1 } }, access: 'constant' }, + { name: 'withdrawal-role', type: { buffer: { length: 1 } }, access: 'constant' }, + { name: 'current-aggregate-pubkey', type: { buffer: { length: 33 } }, access: 'variable' }, + { name: 'current-signature-threshold', type: 'uint128', access: 'variable' }, + { name: 'current-signer-principal', type: 'principal', access: 'variable' }, + { + name: 'current-signer-set', + type: { list: { type: { buffer: { length: 33 } }, length: 128 } }, + access: 'variable', + }, + { name: 'last-withdrawal-request-id', type: 'uint128', access: 'variable' }, + ], + maps: [ + { name: 'active-protocol-contracts', key: { buffer: { length: 1 } }, value: 'principal' }, + { name: 'active-protocol-roles', key: 'principal', value: { buffer: { length: 1 } } }, + { name: 'aggregate-pubkeys', key: { buffer: { length: 33 } }, value: 'bool' }, + { + name: 'completed-deposits', + key: { + tuple: [ + { name: 'txid', type: { buffer: { length: 32 } } }, + { name: 'vout-index', type: 'uint128' }, + ], + }, + value: { + tuple: [ + { name: 'amount', type: 'uint128' }, + { name: 'recipient', type: 'principal' }, + { name: 'sweep-burn-hash', type: { buffer: { length: 32 } } }, + { name: 'sweep-burn-height', type: 'uint128' }, + { name: 'sweep-txid', type: { buffer: { length: 32 } } }, + ], + }, + }, + { + name: 'completed-withdrawal-sweep', + key: 'uint128', + value: { + tuple: [ + { name: 'sweep-burn-hash', type: { buffer: { length: 32 } } }, + { name: 'sweep-burn-height', type: 'uint128' }, + { name: 'sweep-txid', type: { buffer: { length: 32 } } }, + ], + }, + }, + { + name: 'deposit-status', + key: { + tuple: [ + { name: 'txid', type: { buffer: { length: 32 } } }, + { name: 'vout-index', type: 'uint128' }, + ], + }, + value: 'bool', + }, + { + name: 'withdrawal-requests', + key: 'uint128', + value: { + tuple: [ + { name: 'amount', type: 'uint128' }, + { name: 'block-height', type: 'uint128' }, + { name: 'max-fee', type: 'uint128' }, + { + name: 'recipient', + type: { + tuple: [ + { name: 'hashbytes', type: { buffer: { length: 32 } } }, + { name: 'version', type: { buffer: { length: 1 } } }, + ], + }, + }, + { name: 'sender', type: 'principal' }, + ], + }, + }, + { name: 'withdrawal-status', key: 'uint128', value: 'bool' }, + ], + fungible_tokens: [], + non_fungible_tokens: [], + epoch: 'Epoch30', + clarity_version: 'Clarity3', +} as const; + +export const bnsV2Abi = { + functions: [ + { + name: 'compute-name-price', + access: 'private', + args: [ + { name: 'name', type: { buffer: { length: 48 } } }, + { + name: 'price-function', + type: { + tuple: [ + { name: 'base', type: 'uint128' }, + { name: 'buckets', type: { list: { type: 'uint128', length: 16 } } }, + { name: 'coeff', type: 'uint128' }, + { name: 'no-vowel-discount', type: 'uint128' }, + { name: 'nonalpha-discount', type: 'uint128' }, + ], + }, + }, + ], + outputs: { type: { response: { ok: 'uint128', error: 'uint128' } } }, + }, + { + name: 'get-exp-at-index', + access: 'private', + args: [ + { name: 'buckets', type: { list: { type: 'uint128', length: 16 } } }, + { name: 'index', type: 'uint128' }, + ], + outputs: { type: 'uint128' }, + }, + { + name: 'handle-existing-name', + access: 'private', + args: [ + { + name: 'name-props', + type: { + tuple: [ + { + name: 'hashed-salted-fqn-preorder', + type: { optional: { buffer: { length: 20 } } }, + }, + { name: 'imported-at', type: { optional: 'uint128' } }, + { name: 'owner', type: 'principal' }, + { name: 'preordered-by', type: { optional: 'principal' } }, + { name: 'registered-at', type: { optional: 'uint128' } }, + { name: 'renewal-height', type: 'uint128' }, + { name: 'stx-burn', type: 'uint128' }, + ], + }, + }, + { name: 'hashed-salted-fqn', type: { buffer: { length: 20 } } }, + { name: 'contract-caller-preorder-height', type: 'uint128' }, + { name: 'stx-burned', type: 'uint128' }, + { name: 'name', type: { buffer: { length: 48 } } }, + { name: 'namespace', type: { buffer: { length: 20 } } }, + { name: 'renewal', type: 'uint128' }, + ], + outputs: { type: { response: { ok: 'uint128', error: 'uint128' } } }, + }, + { + name: 'handle-renewal-after-grace-period', + access: 'private', + args: [ + { name: 'name', type: { buffer: { length: 48 } } }, + { name: 'namespace', type: { buffer: { length: 20 } } }, + { + name: 'name-props', + type: { + tuple: [ + { + name: 'hashed-salted-fqn-preorder', + type: { optional: { buffer: { length: 20 } } }, + }, + { name: 'imported-at', type: { optional: 'uint128' } }, + { name: 'owner', type: 'principal' }, + { name: 'preordered-by', type: { optional: 'principal' } }, + { name: 'registered-at', type: { optional: 'uint128' } }, + { name: 'renewal-height', type: 'uint128' }, + { name: 'stx-burn', type: 'uint128' }, + ], + }, + }, + { name: 'owner', type: 'principal' }, + { name: 'name-index', type: 'uint128' }, + { name: 'new-renewal-height', type: 'uint128' }, + ], + outputs: { type: { response: { ok: 'bool', error: 'uint128' } } }, + }, + { + name: 'handle-renewal-in-grace-period', + access: 'private', + args: [ + { name: 'name', type: { buffer: { length: 48 } } }, + { name: 'namespace', type: { buffer: { length: 20 } } }, + { + name: 'name-props', + type: { + tuple: [ + { + name: 'hashed-salted-fqn-preorder', + type: { optional: { buffer: { length: 20 } } }, + }, + { name: 'imported-at', type: { optional: 'uint128' } }, + { name: 'owner', type: 'principal' }, + { name: 'preordered-by', type: { optional: 'principal' } }, + { name: 'registered-at', type: { optional: 'uint128' } }, + { name: 'renewal-height', type: 'uint128' }, + { name: 'stx-burn', type: 'uint128' }, + ], + }, + }, + { name: 'owner', type: 'principal' }, + { name: 'lifetime', type: 'uint128' }, + { name: 'new-renewal-height', type: 'uint128' }, + ], + outputs: { type: { response: { ok: 'bool', error: 'uint128' } } }, + }, + { + name: 'has-invalid-chars', + access: 'private', + args: [{ name: 'name', type: { buffer: { length: 48 } } }], + outputs: { type: 'bool' }, + }, + { + name: 'has-nonalpha-chars', + access: 'private', + args: [{ name: 'name', type: { buffer: { length: 48 } } }], + outputs: { type: 'bool' }, + }, + { + name: 'has-vowels-chars', + access: 'private', + args: [{ name: 'name', type: { buffer: { length: 48 } } }], + outputs: { type: 'bool' }, + }, + { + name: 'is-char-valid', + access: 'private', + args: [{ name: 'char', type: { buffer: { length: 1 } } }], + outputs: { type: 'bool' }, + }, + { + name: 'is-digit', + access: 'private', + args: [{ name: 'char', type: { buffer: { length: 1 } } }], + outputs: { type: 'bool' }, + }, + { + name: 'is-lowercase-alpha', + access: 'private', + args: [{ name: 'char', type: { buffer: { length: 1 } } }], + outputs: { type: 'bool' }, + }, + { + name: 'is-namespace-available', + access: 'private', + args: [{ name: 'namespace', type: { buffer: { length: 20 } } }], + outputs: { type: 'bool' }, + }, + { + name: 'is-nonalpha', + access: 'private', + args: [{ name: 'char', type: { buffer: { length: 1 } } }], + outputs: { type: 'bool' }, + }, + { + name: 'is-special-char', + access: 'private', + args: [{ name: 'char', type: { buffer: { length: 1 } } }], + outputs: { type: 'bool' }, + }, + { + name: 'is-vowel', + access: 'private', + args: [{ name: 'char', type: { buffer: { length: 1 } } }], + outputs: { type: 'bool' }, + }, + { + name: 'max', + access: 'private', + args: [ + { name: 'a', type: 'uint128' }, + { name: 'b', type: 'uint128' }, + ], + outputs: { type: 'uint128' }, + }, + { + name: 'min', + access: 'private', + args: [ + { name: 'a', type: 'uint128' }, + { name: 'b', type: 'uint128' }, + ], + outputs: { type: 'uint128' }, + }, + { + name: 'purchase-transfer', + access: 'private', + args: [ + { name: 'id', type: 'uint128' }, + { name: 'owner', type: 'principal' }, + { name: 'recipient', type: 'principal' }, + ], + outputs: { type: { response: { ok: 'bool', error: 'uint128' } } }, + }, + { + name: 'register-new-name', + access: 'private', + args: [ + { name: 'id-to-be-minted', type: 'uint128' }, + { name: 'hashed-salted-fqn', type: { buffer: { length: 20 } } }, + { name: 'stx-burned', type: 'uint128' }, + { name: 'name', type: { buffer: { length: 48 } } }, + { name: 'namespace', type: { buffer: { length: 20 } } }, + { name: 'lifetime', type: 'uint128' }, + ], + outputs: { type: { response: { ok: 'uint128', error: 'uint128' } } }, + }, + { + name: 'update-primary-name-owner', + access: 'private', + args: [ + { name: 'id', type: 'uint128' }, + { name: 'owner', type: 'principal' }, + ], + outputs: { type: 'bool' }, + }, + { + name: 'update-primary-name-recipient', + access: 'private', + args: [ + { name: 'id', type: 'uint128' }, + { name: 'recipient', type: 'principal' }, + ], + outputs: { type: 'bool' }, + }, + { + name: 'buy-in-ustx', + access: 'public', + args: [ + { name: 'id', type: 'uint128' }, + { name: 'comm-trait', type: 'trait_reference' }, + ], + outputs: { + type: { + response: { + ok: { + tuple: [ + { name: 'a', type: { 'string-ascii': { length: 11 } } }, + { name: 'id', type: 'uint128' }, + ], + }, + error: 'uint128', + }, + }, + }, + }, + { + name: 'claim-preorder', + access: 'public', + args: [{ name: 'hashed-salted-fqn', type: { buffer: { length: 20 } } }], + outputs: { type: { response: { ok: 'bool', error: 'uint128' } } }, + }, + { + name: 'flip-migration-complete', + access: 'public', + args: [], + outputs: { type: { response: { ok: 'bool', error: 'uint128' } } }, + }, + { + name: 'freeze-manager', + access: 'public', + args: [{ name: 'namespace', type: { buffer: { length: 20 } } }], + outputs: { type: { response: { ok: 'bool', error: 'uint128' } } }, + }, + { + name: 'list-in-ustx', + access: 'public', + args: [ + { name: 'id', type: 'uint128' }, + { name: 'price', type: 'uint128' }, + { name: 'comm-trait', type: 'trait_reference' }, + ], + outputs: { + type: { + response: { + ok: { + tuple: [ + { name: 'a', type: { 'string-ascii': { length: 12 } } }, + { name: 'commission', type: 'principal' }, + { name: 'id', type: 'uint128' }, + { name: 'price', type: 'uint128' }, + ], + }, + error: 'uint128', + }, + }, + }, + }, + { + name: 'mng-burn', + access: 'public', + args: [{ name: 'id', type: 'uint128' }], + outputs: { type: { response: { ok: 'bool', error: 'uint128' } } }, + }, + { + name: 'mng-manager-transfer', + access: 'public', + args: [ + { name: 'new-manager', type: { optional: 'principal' } }, + { name: 'namespace', type: { buffer: { length: 20 } } }, + ], + outputs: { type: { response: { ok: 'bool', error: 'uint128' } } }, + }, + { + name: 'mng-name-preorder', + access: 'public', + args: [{ name: 'hashed-salted-fqn', type: { buffer: { length: 20 } } }], + outputs: { type: { response: { ok: 'uint128', error: 'uint128' } } }, + }, + { + name: 'mng-name-register', + access: 'public', + args: [ + { name: 'namespace', type: { buffer: { length: 20 } } }, + { name: 'name', type: { buffer: { length: 48 } } }, + { name: 'salt', type: { buffer: { length: 20 } } }, + { name: 'send-to', type: 'principal' }, + ], + outputs: { type: { response: { ok: 'uint128', error: 'uint128' } } }, + }, + { + name: 'mng-transfer', + access: 'public', + args: [ + { name: 'id', type: 'uint128' }, + { name: 'owner', type: 'principal' }, + { name: 'recipient', type: 'principal' }, + ], + outputs: { type: { response: { ok: 'bool', error: 'uint128' } } }, + }, + { + name: 'name-airdrop', + access: 'public', + args: [ + { name: 'name', type: { buffer: { length: 48 } } }, + { name: 'namespace', type: { buffer: { length: 20 } } }, + { name: 'registered-at', type: 'uint128' }, + { name: 'lifetime', type: 'uint128' }, + { name: 'owner', type: 'principal' }, + ], + outputs: { type: { response: { ok: 'uint128', error: 'uint128' } } }, + }, + { + name: 'name-claim-fast', + access: 'public', + args: [ + { name: 'name', type: { buffer: { length: 48 } } }, + { name: 'namespace', type: { buffer: { length: 20 } } }, + { name: 'send-to', type: 'principal' }, + ], + outputs: { type: { response: { ok: 'uint128', error: 'uint128' } } }, + }, + { + name: 'name-import', + access: 'public', + args: [ + { name: 'namespace', type: { buffer: { length: 20 } } }, + { name: 'name', type: { buffer: { length: 48 } } }, + { name: 'beneficiary', type: 'principal' }, + ], + outputs: { type: { response: { ok: 'bool', error: 'uint128' } } }, + }, + { + name: 'name-preorder', + access: 'public', + args: [ + { name: 'hashed-salted-fqn', type: { buffer: { length: 20 } } }, + { name: 'stx-to-burn', type: 'uint128' }, + ], + outputs: { type: { response: { ok: 'uint128', error: 'uint128' } } }, + }, + { + name: 'name-register', + access: 'public', + args: [ + { name: 'namespace', type: { buffer: { length: 20 } } }, + { name: 'name', type: { buffer: { length: 48 } } }, + { name: 'salt', type: { buffer: { length: 20 } } }, + ], + outputs: { type: { response: { ok: 'uint128', error: 'uint128' } } }, + }, + { + name: 'name-renewal', + access: 'public', + args: [ + { name: 'namespace', type: { buffer: { length: 20 } } }, + { name: 'name', type: { buffer: { length: 48 } } }, + ], + outputs: { type: { response: { ok: 'bool', error: 'uint128' } } }, + }, + { + name: 'namespace-airdrop', + access: 'public', + args: [ + { name: 'namespace', type: { buffer: { length: 20 } } }, + { + name: 'pricing', + type: { + tuple: [ + { name: 'base', type: 'uint128' }, + { name: 'buckets', type: { list: { type: 'uint128', length: 16 } } }, + { name: 'coeff', type: 'uint128' }, + { name: 'no-vowel-discount', type: 'uint128' }, + { name: 'nonalpha-discount', type: 'uint128' }, + ], + }, + }, + { name: 'lifetime', type: 'uint128' }, + { name: 'namespace-import', type: 'principal' }, + { name: 'namespace-manager', type: { optional: 'principal' } }, + { name: 'can-update-price', type: 'bool' }, + { name: 'manager-transfers', type: 'bool' }, + { name: 'manager-frozen', type: 'bool' }, + { name: 'revealed-at', type: 'uint128' }, + { name: 'launched-at', type: 'uint128' }, + ], + outputs: { type: { response: { ok: { buffer: { length: 20 } }, error: 'uint128' } } }, + }, + { + name: 'namespace-freeze-price', + access: 'public', + args: [{ name: 'namespace', type: { buffer: { length: 20 } } }], + outputs: { type: { response: { ok: 'bool', error: 'uint128' } } }, + }, + { + name: 'namespace-launch', + access: 'public', + args: [{ name: 'namespace', type: { buffer: { length: 20 } } }], + outputs: { type: { response: { ok: 'bool', error: 'uint128' } } }, + }, + { + name: 'namespace-preorder', + access: 'public', + args: [ + { name: 'hashed-salted-namespace', type: { buffer: { length: 20 } } }, + { name: 'stx-to-burn', type: 'uint128' }, + ], + outputs: { type: { response: { ok: 'uint128', error: 'uint128' } } }, + }, + { + name: 'namespace-reveal', + access: 'public', + args: [ + { name: 'namespace', type: { buffer: { length: 20 } } }, + { name: 'namespace-salt', type: { buffer: { length: 20 } } }, + { name: 'p-func-base', type: 'uint128' }, + { name: 'p-func-coeff', type: 'uint128' }, + { name: 'p-func-b1', type: 'uint128' }, + { name: 'p-func-b2', type: 'uint128' }, + { name: 'p-func-b3', type: 'uint128' }, + { name: 'p-func-b4', type: 'uint128' }, + { name: 'p-func-b5', type: 'uint128' }, + { name: 'p-func-b6', type: 'uint128' }, + { name: 'p-func-b7', type: 'uint128' }, + { name: 'p-func-b8', type: 'uint128' }, + { name: 'p-func-b9', type: 'uint128' }, + { name: 'p-func-b10', type: 'uint128' }, + { name: 'p-func-b11', type: 'uint128' }, + { name: 'p-func-b12', type: 'uint128' }, + { name: 'p-func-b13', type: 'uint128' }, + { name: 'p-func-b14', type: 'uint128' }, + { name: 'p-func-b15', type: 'uint128' }, + { name: 'p-func-b16', type: 'uint128' }, + { name: 'p-func-non-alpha-discount', type: 'uint128' }, + { name: 'p-func-no-vowel-discount', type: 'uint128' }, + { name: 'lifetime', type: 'uint128' }, + { name: 'namespace-import', type: 'principal' }, + { name: 'namespace-manager', type: { optional: 'principal' } }, + { name: 'can-update-price', type: 'bool' }, + { name: 'manager-transfers', type: 'bool' }, + { name: 'manager-frozen', type: 'bool' }, + ], + outputs: { type: { response: { ok: 'bool', error: 'uint128' } } }, + }, + { + name: 'namespace-update-price', + access: 'public', + args: [ + { name: 'namespace', type: { buffer: { length: 20 } } }, + { name: 'p-func-base', type: 'uint128' }, + { name: 'p-func-coeff', type: 'uint128' }, + { name: 'p-func-b1', type: 'uint128' }, + { name: 'p-func-b2', type: 'uint128' }, + { name: 'p-func-b3', type: 'uint128' }, + { name: 'p-func-b4', type: 'uint128' }, + { name: 'p-func-b5', type: 'uint128' }, + { name: 'p-func-b6', type: 'uint128' }, + { name: 'p-func-b7', type: 'uint128' }, + { name: 'p-func-b8', type: 'uint128' }, + { name: 'p-func-b9', type: 'uint128' }, + { name: 'p-func-b10', type: 'uint128' }, + { name: 'p-func-b11', type: 'uint128' }, + { name: 'p-func-b12', type: 'uint128' }, + { name: 'p-func-b13', type: 'uint128' }, + { name: 'p-func-b14', type: 'uint128' }, + { name: 'p-func-b15', type: 'uint128' }, + { name: 'p-func-b16', type: 'uint128' }, + { name: 'p-func-non-alpha-discount', type: 'uint128' }, + { name: 'p-func-no-vowel-discount', type: 'uint128' }, + ], + outputs: { type: { response: { ok: 'bool', error: 'uint128' } } }, + }, + { + name: 'set-primary-name', + access: 'public', + args: [{ name: 'primary-name-id', type: 'uint128' }], + outputs: { type: { response: { ok: 'bool', error: 'uint128' } } }, + }, + { + name: 'transfer', + access: 'public', + args: [ + { name: 'id', type: 'uint128' }, + { name: 'owner', type: 'principal' }, + { name: 'recipient', type: 'principal' }, + ], + outputs: { type: { response: { ok: 'bool', error: 'uint128' } } }, + }, + { + name: 'turn-off-manager-transfers', + access: 'public', + args: [{ name: 'namespace', type: { buffer: { length: 20 } } }], + outputs: { type: { response: { ok: 'bool', error: 'uint128' } } }, + }, + { + name: 'unlist-in-ustx', + access: 'public', + args: [{ name: 'id', type: 'uint128' }], + outputs: { + type: { + response: { + ok: { + tuple: [ + { name: 'a', type: { 'string-ascii': { length: 14 } } }, + { name: 'id', type: 'uint128' }, + ], + }, + error: 'uint128', + }, + }, + }, + }, + { + name: 'update-contract-uri', + access: 'public', + args: [{ name: 'new-contract-uri', type: { 'string-ascii': { length: 256 } } }], + outputs: { type: { response: { ok: 'bool', error: 'uint128' } } }, + }, + { + name: 'update-token-uri', + access: 'public', + args: [{ name: 'new-token-uri', type: { 'string-ascii': { length: 256 } } }], + outputs: { type: { response: { ok: 'bool', error: 'uint128' } } }, + }, + { + name: 'can-namespace-be-registered', + access: 'read_only', + args: [{ name: 'namespace', type: { buffer: { length: 20 } } }], + outputs: { type: { response: { ok: 'bool', error: 'none' } } }, + }, + { + name: 'can-resolve-name', + access: 'read_only', + args: [ + { name: 'namespace', type: { buffer: { length: 20 } } }, + { name: 'name', type: { buffer: { length: 48 } } }, + ], + outputs: { + type: { + response: { + ok: { + tuple: [ + { name: 'owner', type: 'principal' }, + { name: 'renewal', type: 'uint128' }, + ], + }, + error: 'uint128', + }, + }, + }, + }, + { + name: 'get-bns-from-id', + access: 'read_only', + args: [{ name: 'id', type: 'uint128' }], + outputs: { + type: { + optional: { + tuple: [ + { name: 'name', type: { buffer: { length: 48 } } }, + { name: 'namespace', type: { buffer: { length: 20 } } }, + ], + }, + }, + }, + }, + { + name: 'get-bns-info', + access: 'read_only', + args: [ + { name: 'name', type: { buffer: { length: 48 } } }, + { name: 'namespace', type: { buffer: { length: 20 } } }, + ], + outputs: { + type: { + optional: { + tuple: [ + { + name: 'hashed-salted-fqn-preorder', + type: { optional: { buffer: { length: 20 } } }, + }, + { name: 'imported-at', type: { optional: 'uint128' } }, + { name: 'owner', type: 'principal' }, + { name: 'preordered-by', type: { optional: 'principal' } }, + { name: 'registered-at', type: { optional: 'uint128' } }, + { name: 'renewal-height', type: 'uint128' }, + { name: 'stx-burn', type: 'uint128' }, + ], + }, + }, + }, + }, + { + name: 'get-contract-uri', + access: 'read_only', + args: [], + outputs: { + type: { + response: { ok: { optional: { 'string-ascii': { length: 256 } } }, error: 'none' }, + }, + }, + }, + { + name: 'get-id-from-bns', + access: 'read_only', + args: [ + { name: 'name', type: { buffer: { length: 48 } } }, + { name: 'namespace', type: { buffer: { length: 20 } } }, + ], + outputs: { type: { optional: 'uint128' } }, + }, + { + name: 'get-last-token-id', + access: 'read_only', + args: [], + outputs: { type: { response: { ok: 'uint128', error: 'none' } } }, + }, + { + name: 'get-name-price', + access: 'read_only', + args: [ + { name: 'namespace', type: { buffer: { length: 20 } } }, + { name: 'name', type: { buffer: { length: 48 } } }, + ], + outputs: { + type: { + response: { ok: { response: { ok: 'uint128', error: 'uint128' } }, error: 'uint128' }, + }, + }, + }, + { + name: 'get-namespace-price', + access: 'read_only', + args: [{ name: 'namespace', type: { buffer: { length: 20 } } }], + outputs: { type: { response: { ok: 'uint128', error: 'uint128' } } }, + }, + { + name: 'get-namespace-properties', + access: 'read_only', + args: [{ name: 'namespace', type: { buffer: { length: 20 } } }], + outputs: { + type: { + response: { + ok: { + tuple: [ + { name: 'namespace', type: { buffer: { length: 20 } } }, + { + name: 'properties', + type: { + tuple: [ + { name: 'can-update-price-function', type: 'bool' }, + { name: 'launched-at', type: { optional: 'uint128' } }, + { name: 'lifetime', type: 'uint128' }, + { name: 'manager-frozen', type: 'bool' }, + { name: 'manager-transferable', type: 'bool' }, + { name: 'namespace-import', type: 'principal' }, + { name: 'namespace-manager', type: { optional: 'principal' } }, + { + name: 'price-function', + type: { + tuple: [ + { name: 'base', type: 'uint128' }, + { name: 'buckets', type: { list: { type: 'uint128', length: 16 } } }, + { name: 'coeff', type: 'uint128' }, + { name: 'no-vowel-discount', type: 'uint128' }, + { name: 'nonalpha-discount', type: 'uint128' }, + ], + }, + }, + { name: 'revealed-at', type: 'uint128' }, + ], + }, + }, + ], + }, + error: 'uint128', + }, + }, + }, + }, + { + name: 'get-owner', + access: 'read_only', + args: [{ name: 'id', type: 'uint128' }], + outputs: { type: { response: { ok: { optional: 'principal' }, error: 'none' } } }, + }, + { + name: 'get-owner-name', + access: 'read_only', + args: [ + { name: 'name', type: { buffer: { length: 48 } } }, + { name: 'namespace', type: { buffer: { length: 20 } } }, + ], + outputs: { type: { response: { ok: { optional: 'principal' }, error: 'uint128' } } }, + }, + { + name: 'get-primary', + access: 'read_only', + args: [{ name: 'owner', type: 'principal' }], + outputs: { + type: { + response: { + ok: { + optional: { + tuple: [ + { name: 'name', type: { buffer: { length: 48 } } }, + { name: 'namespace', type: { buffer: { length: 20 } } }, + ], + }, + }, + error: 'uint128', + }, + }, + }, + }, + { + name: 'get-primary-name', + access: 'read_only', + args: [{ name: 'owner', type: 'principal' }], + outputs: { type: { optional: 'uint128' } }, + }, + { + name: 'get-renewal-height', + access: 'read_only', + args: [{ name: 'id', type: 'uint128' }], + outputs: { type: { response: { ok: 'uint128', error: 'uint128' } } }, + }, + { + name: 'get-token-uri', + access: 'read_only', + args: [{ name: 'id', type: 'uint128' }], + outputs: { + type: { + response: { ok: { optional: { 'string-ascii': { length: 256 } } }, error: 'none' }, + }, + }, + }, + ], + variables: [ + { name: 'DEPLOYER', type: 'principal', access: 'constant' }, + { + name: 'ERR-CHARSET-INVALID', + type: { response: { ok: 'none', error: 'uint128' } }, + access: 'constant', + }, + { + name: 'ERR-FAST-MINTED-BEFORE', + type: { response: { ok: 'none', error: 'uint128' } }, + access: 'constant', + }, + { + name: 'ERR-HASH-MALFORMED', + type: { response: { ok: 'none', error: 'uint128' } }, + access: 'constant', + }, + { + name: 'ERR-IMPORTED-BEFORE', + type: { response: { ok: 'none', error: 'uint128' } }, + access: 'constant', + }, + { + name: 'ERR-LIFETIME-EQUAL-0', + type: { response: { ok: 'none', error: 'uint128' } }, + access: 'constant', + }, + { + name: 'ERR-LISTED', + type: { response: { ok: 'none', error: 'uint128' } }, + access: 'constant', + }, + { + name: 'ERR-MIGRATION-IN-PROGRESS', + type: { response: { ok: 'none', error: 'uint128' } }, + access: 'constant', + }, + { + name: 'ERR-NAME-BLANK', + type: { response: { ok: 'none', error: 'uint128' } }, + access: 'constant', + }, + { + name: 'ERR-NAME-NOT-AVAILABLE', + type: { response: { ok: 'none', error: 'uint128' } }, + access: 'constant', + }, + { + name: 'ERR-NAME-NOT-CLAIMABLE-YET', + type: { response: { ok: 'none', error: 'uint128' } }, + access: 'constant', + }, + { + name: 'ERR-NAME-PREORDERED-BEFORE-NAMESPACE-LAUNCH', + type: { response: { ok: 'none', error: 'uint128' } }, + access: 'constant', + }, + { + name: 'ERR-NAMESPACE-ALREADY-EXISTS', + type: { response: { ok: 'none', error: 'uint128' } }, + access: 'constant', + }, + { + name: 'ERR-NAMESPACE-ALREADY-LAUNCHED', + type: { response: { ok: 'none', error: 'uint128' } }, + access: 'constant', + }, + { + name: 'ERR-NAMESPACE-BLANK', + type: { response: { ok: 'none', error: 'uint128' } }, + access: 'constant', + }, + { + name: 'ERR-NAMESPACE-HAS-MANAGER', + type: { response: { ok: 'none', error: 'uint128' } }, + access: 'constant', + }, + { + name: 'ERR-NAMESPACE-NOT-FOUND', + type: { response: { ok: 'none', error: 'uint128' } }, + access: 'constant', + }, + { + name: 'ERR-NAMESPACE-NOT-LAUNCHED', + type: { response: { ok: 'none', error: 'uint128' } }, + access: 'constant', + }, + { + name: 'ERR-NAMESPACE-PREORDER-LAUNCHABILITY-EXPIRED', + type: { response: { ok: 'none', error: 'uint128' } }, + access: 'constant', + }, + { + name: 'ERR-NO-NAME', + type: { response: { ok: 'none', error: 'uint128' } }, + access: 'constant', + }, + { + name: 'ERR-NO-NAMESPACE-MANAGER', + type: { response: { ok: 'none', error: 'uint128' } }, + access: 'constant', + }, + { + name: 'ERR-NO-PRIMARY-NAME', + type: { response: { ok: 'none', error: 'uint128' } }, + access: 'constant', + }, + { + name: 'ERR-NOT-AUTHORIZED', + type: { response: { ok: 'none', error: 'uint128' } }, + access: 'constant', + }, + { + name: 'ERR-NOT-LISTED', + type: { response: { ok: 'none', error: 'uint128' } }, + access: 'constant', + }, + { + name: 'ERR-OPERATION-UNAUTHORIZED', + type: { response: { ok: 'none', error: 'uint128' } }, + access: 'constant', + }, + { + name: 'ERR-OVERFLOW', + type: { response: { ok: 'none', error: 'uint128' } }, + access: 'constant', + }, + { + name: 'ERR-PREORDER-CLAIMABILITY-EXPIRED', + type: { response: { ok: 'none', error: 'uint128' } }, + access: 'constant', + }, + { + name: 'ERR-PREORDER-NOT-FOUND', + type: { response: { ok: 'none', error: 'uint128' } }, + access: 'constant', + }, + { + name: 'ERR-PREORDERED-BEFORE', + type: { response: { ok: 'none', error: 'uint128' } }, + access: 'constant', + }, + { + name: 'ERR-STX-BURNT-INSUFFICIENT', + type: { response: { ok: 'none', error: 'uint128' } }, + access: 'constant', + }, + { + name: 'ERR-UNWRAP', + type: { response: { ok: 'none', error: 'uint128' } }, + access: 'constant', + }, + { + name: 'ERR-WRONG-COMMISSION', + type: { response: { ok: 'none', error: 'uint128' } }, + access: 'constant', + }, + { name: 'HASH160LEN', type: 'uint128', access: 'constant' }, + { name: 'NAME-GRACE-PERIOD-DURATION', type: 'uint128', access: 'constant' }, + { name: 'NAMESPACE-LAUNCHABILITY-TTL', type: 'uint128', access: 'constant' }, + { + name: 'NAMESPACE-PRICE-TIERS', + type: { list: { type: 'uint128', length: 20 } }, + access: 'constant', + }, + { name: 'PREORDER-CLAIMABILITY-TTL', type: 'uint128', access: 'constant' }, + { name: 'bns-index', type: 'uint128', access: 'variable' }, + { name: 'contract-uri', type: { 'string-ascii': { length: 256 } }, access: 'variable' }, + { name: 'migration-complete', type: 'bool', access: 'variable' }, + { name: 'token-uri', type: { 'string-ascii': { length: 256 } }, access: 'variable' }, + ], + maps: [ + { + name: 'index-to-name', + key: 'uint128', + value: { + tuple: [ + { name: 'name', type: { buffer: { length: 48 } } }, + { name: 'namespace', type: { buffer: { length: 20 } } }, + ], + }, + }, + { + name: 'market', + key: 'uint128', + value: { + tuple: [ + { name: 'commission', type: 'principal' }, + { name: 'price', type: 'uint128' }, + ], + }, + }, + { + name: 'name-preorders', + key: { + tuple: [ + { name: 'buyer', type: 'principal' }, + { name: 'hashed-salted-fqn', type: { buffer: { length: 20 } } }, + ], + }, + value: { + tuple: [ + { name: 'claimed', type: 'bool' }, + { name: 'created-at', type: 'uint128' }, + { name: 'stx-burned', type: 'uint128' }, + ], + }, + }, + { + name: 'name-properties', + key: { + tuple: [ + { name: 'name', type: { buffer: { length: 48 } } }, + { name: 'namespace', type: { buffer: { length: 20 } } }, + ], + }, + value: { + tuple: [ + { name: 'hashed-salted-fqn-preorder', type: { optional: { buffer: { length: 20 } } } }, + { name: 'imported-at', type: { optional: 'uint128' } }, + { name: 'owner', type: 'principal' }, + { name: 'preordered-by', type: { optional: 'principal' } }, + { name: 'registered-at', type: { optional: 'uint128' } }, + { name: 'renewal-height', type: 'uint128' }, + { name: 'stx-burn', type: 'uint128' }, + ], + }, + }, + { name: 'name-single-preorder', key: { buffer: { length: 20 } }, value: 'bool' }, + { + name: 'name-to-index', + key: { + tuple: [ + { name: 'name', type: { buffer: { length: 48 } } }, + { name: 'namespace', type: { buffer: { length: 20 } } }, + ], + }, + value: 'uint128', + }, + { + name: 'namespace-preorders', + key: { + tuple: [ + { name: 'buyer', type: 'principal' }, + { name: 'hashed-salted-namespace', type: { buffer: { length: 20 } } }, + ], + }, + value: { + tuple: [ + { name: 'claimed', type: 'bool' }, + { name: 'created-at', type: 'uint128' }, + { name: 'stx-burned', type: 'uint128' }, + ], + }, + }, + { name: 'namespace-single-preorder', key: { buffer: { length: 20 } }, value: 'bool' }, + { + name: 'namespaces', + key: { buffer: { length: 20 } }, + value: { + tuple: [ + { name: 'can-update-price-function', type: 'bool' }, + { name: 'launched-at', type: { optional: 'uint128' } }, + { name: 'lifetime', type: 'uint128' }, + { name: 'manager-frozen', type: 'bool' }, + { name: 'manager-transferable', type: 'bool' }, + { name: 'namespace-import', type: 'principal' }, + { name: 'namespace-manager', type: { optional: 'principal' } }, + { + name: 'price-function', + type: { + tuple: [ + { name: 'base', type: 'uint128' }, + { name: 'buckets', type: { list: { type: 'uint128', length: 16 } } }, + { name: 'coeff', type: 'uint128' }, + { name: 'no-vowel-discount', type: 'uint128' }, + { name: 'nonalpha-discount', type: 'uint128' }, + ], + }, + }, + { name: 'revealed-at', type: 'uint128' }, + ], + }, + }, + { name: 'primary-name', key: 'principal', value: 'uint128' }, + ], + fungible_tokens: [], + non_fungible_tokens: [{ name: 'BNS-V2', type: 'uint128' }], + epoch: 'Epoch25', + clarity_version: 'Clarity2', +} as const; diff --git a/src/lib/names.ts b/src/lib/names.ts index 5220d7b..f492e33 100644 --- a/src/lib/names.ts +++ b/src/lib/names.ts @@ -1,51 +1,38 @@ -import { - BufferCV, - bufferCVFromString, - fetchCallReadOnlyFunction, - OptionalCV, - PrincipalCV, - principalCV, - ResponseErrorCV, - ResponseOkCV, - TupleCV, - UIntCV, -} from '@stacks/transactions'; +import { typedCallReadOnlyFunction } from 'clarity-abitype'; import { BNS_CONTRACT_ADDRESS, BNS_CONTRACT_NAME, NETWORK } from './constants'; +import { bnsV2Abi } from './abi'; export const getNameFromAddress = async (addr: string) => { - let addrCV = principalCV(addr); - const result = (await fetchCallReadOnlyFunction({ + const result = await typedCallReadOnlyFunction({ + abi: bnsV2Abi, contractAddress: BNS_CONTRACT_ADDRESS, contractName: BNS_CONTRACT_NAME, functionName: 'get-primary', - functionArgs: [addrCV], + functionArgs: [addr], senderAddress: addr, network: NETWORK, - })) as - | ResponseErrorCV - | ResponseOkCV>>; + }); return result; }; export const getNameInfo = async (fqName: string) => { const [name, namespace] = fqName.split('.'); - const result = (await fetchCallReadOnlyFunction({ + const result = await typedCallReadOnlyFunction({ + abi: bnsV2Abi, contractAddress: BNS_CONTRACT_ADDRESS, contractName: BNS_CONTRACT_NAME, functionName: 'get-bns-info', - functionArgs: [bufferCVFromString(name), bufferCVFromString(namespace)], + functionArgs: [ + `0x${Array.from(new TextEncoder().encode(name)) + .map(b => b.toString(16).padStart(2, '0')) + .join('')}`, + `0x${Array.from(new TextEncoder().encode(namespace)) + .map(b => b.toString(16).padStart(2, '0')) + .join('')}`, + ], senderAddress: BNS_CONTRACT_ADDRESS, network: NETWORK, - })) as - | OptionalCV< - TupleCV<{ - owner: PrincipalCV; - 'renewal-height': UIntCV; - 'registered-at': OptionalCV; - 'imported-at': OptionalCV; - }> - > - | ResponseErrorCV; + }); console.log({ result }); return result; }; diff --git a/src/lib/string-utils.ts b/src/lib/string-utils.ts index a01278c..5db46ed 100644 --- a/src/lib/string-utils.ts +++ b/src/lib/string-utils.ts @@ -4,6 +4,9 @@ * @returns ASCII string */ export function hex_to_ascii(hexString: string): string { + if (hexString.startsWith('0x')) { + hexString = hexString.slice(2); + } let result = ''; for (let i = 0; i < hexString.length; i += 2) { const hexByte = hexString.substring(i, i + 2); diff --git a/src/test/names.test.ts b/src/test/names.test.ts index 42313fa..c1b4210 100644 --- a/src/test/names.test.ts +++ b/src/test/names.test.ts @@ -1,6 +1,5 @@ import { describe, it, expect } from 'vitest'; import { getNameFromAddress } from '../lib/names'; -import { ClarityType } from '@stacks/transactions'; import { hex_to_ascii } from '../lib/string-utils'; describe('getNameFromAddress', () => { @@ -10,14 +9,13 @@ describe('getNameFromAddress', () => { const result = await getNameFromAddress(address); // Should return a successful response - expect(result.type).toBe(ClarityType.ResponseOk); - expect(result.value.type).toBe(ClarityType.OptionalSome); - if (result.type === ClarityType.ResponseOk && result.value.type === ClarityType.OptionalSome) { - const { name, namespace } = result.value.value.value; + expect(result.ok).toBeTruthy(); + if (result.ok) { + const { name, namespace } = result.ok; // Convert hex-encoded values to ASCII - const nameStr = hex_to_ascii(name.value); - const namespaceStr = hex_to_ascii(namespace.value); + const nameStr = hex_to_ascii(name); + const namespaceStr = hex_to_ascii(namespace); // Should resolve to friedger.btc expect(nameStr).toBe('friedger'); @@ -25,7 +23,7 @@ describe('getNameFromAddress', () => { console.log(`Resolved: ${nameStr}.${namespaceStr}`); } else { - throw new Error(`Expected ResponseOk with Tuple, got ${result.type}`); + throw new Error('Expected ok'); } }, 10000); // Increase timeout for network call }); diff --git a/tsconfig.json b/tsconfig.json index f24b07e..4a8f6b8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,23 +1,24 @@ { "compilerOptions": { - "target": "ESNext", - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": false, + "target": "ES2023", + "useDefineForClassFields": true, + "lib": ["ES2023", "DOM", "DOM.Iterable"], + "module": "ESNext", + "types": ["vite/client", "vite-plugin-svgr/client"], "skipLibCheck": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "module": "esnext", - "moduleResolution": "node", - "resolveJsonModule": true, - "isolatedModules": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "moduleDetection": "force", "noEmit": true, - "noFallthroughCasesInSwitch": true, "jsx": "react-jsx", - "types": ["vite/client", "vite-plugin-svgr/client"], - "noUnusedLocals": true + + /* Linting */ + "strict": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true, }, "include": ["src"], - "references": [{ "path": "./tsconfig.node.json" }] }