Skip to content

Commit bdb9ec8

Browse files
authored
refactor(sdk): reorganize utility functions into public/internal (#6)
Reorganized utility functions in the SDK to clearly separate public and internal APIs: - Created a new `internal` directory for utility functions not meant for public use - Moved `array.ts`, `bytes.ts`, and `state.ts` to the internal directory - Updated import paths across the codebase - Updated `utils/README.md` to document all public utility functions This change improves maintainability and provides clearer API boundaries for SDK users.
1 parent 54d2b1c commit bdb9ec8

File tree

11 files changed

+194
-88
lines changed

11 files changed

+194
-88
lines changed

packages/sdk/src/api-client.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { client } from './api/client.gen'
22
import { nfdGetLookup, nfdGetNfd, nfdSearchV2 } from './api/sdk.gen'
33
import { NfdApiBaseUrl, NfdRegistryId } from './constants'
4-
import { chunkArray } from './utils/array'
4+
import { chunkArray } from './utils/internal/array'
55

66
import type { Nfd, SearchOptions, SearchResponse } from './types'
77

packages/sdk/src/modules/lookup.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { Address } from 'algosdk'
22

3-
import { determineNfdState, generateMetaTags, isValidName } from '../utils/nfd'
4-
import { parseAddress, parseString, parseUint64 } from '../utils/state'
3+
import { determineNfdState, generateMetaTags } from '../utils/internal/nfd'
4+
import { parseAddress, parseString, parseUint64 } from '../utils/internal/state'
5+
import { isValidName } from '../utils/nfd'
56

67
import { BaseModule } from './base'
78

packages/sdk/src/modules/manager.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { AlgoAmount } from '@algorandfoundation/algokit-utils/types/amount'
22

3-
import { strToUint8Array, concatUint8Arrays } from '../utils/bytes'
43
import { parseTransactionError } from '../utils/error-parser'
4+
import { strToUint8Array, concatUint8Arrays } from '../utils/internal/bytes'
55

66
import { BaseModule } from './base'
77

packages/sdk/src/utils/README.md

+107-9
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,125 @@
11
# NFD SDK Utilities
22

3-
## Error Parser
3+
The NFD SDK provides utility functions to help developers work with NFDs and handle errors when interacting with the Algorand blockchain.
44

5-
The error parser utility provides user-friendly error messages for common Algorand transaction errors. It helps developers and users understand what went wrong when a transaction fails, without having to decipher the complex error messages returned by the Algorand blockchain.
5+
## Importing Utilities
66

7-
### Usage
7+
All public utility functions can be imported directly from the main package:
88

99
```typescript
10-
import { parseTransactionError, withErrorParsing } from '@txnlab/nfd-sdk'
10+
import {
11+
// NFD utilities
12+
isValidName,
13+
isSegmentName,
14+
extractParentName,
15+
getNfdBasename,
16+
isSegmentMintingUnlocked,
17+
canMintSegment,
18+
19+
// Error handling utilities
20+
parseTransactionError,
21+
withErrorParsing,
22+
} from '@txnlab/nfd-sdk'
23+
```
24+
25+
## NFD Utilities
26+
27+
### `isValidName(name: string): boolean`
28+
29+
Checks if a string is a valid NFD name according to the naming rules.
1130

12-
// Basic usage
31+
```typescript
32+
import { isValidName } from '@txnlab/nfd-sdk'
33+
34+
// Check if a name is valid
35+
const isValid = isValidName('alice.algo')
36+
// Returns true
37+
```
38+
39+
### `isSegmentName(name: string): boolean`
40+
41+
Determines if a name is a segment NFD. A segment is a child NFD that is created under a parent NFD (e.g., 'sub.alice.algo' is a segment of 'alice.algo'). Segments are sovereign NFDs with all functionality once minted.
42+
43+
```typescript
44+
import { isSegmentName } from '@txnlab/nfd-sdk'
45+
46+
// Check if a name is a segment
47+
const isSegment = isSegmentName('sub.alice.algo')
48+
// Returns true
49+
```
50+
51+
### `extractParentName(segmentName: string): string`
52+
53+
Extracts the parent name from a segment name.
54+
55+
```typescript
56+
import { extractParentName } from '@txnlab/nfd-sdk'
57+
58+
// Get the parent name
59+
const parentName = extractParentName('sub.alice.algo')
60+
// Returns 'alice.algo'
61+
```
62+
63+
### `getNfdBasename(name: string): string`
64+
65+
Gets the base name of an NFD (without the .algo extension).
66+
67+
```typescript
68+
import { getNfdBasename } from '@txnlab/nfd-sdk'
69+
70+
// Get the base name
71+
const baseName = getNfdBasename('alice.algo')
72+
// Returns 'alice'
73+
```
74+
75+
### `isSegmentMintingUnlocked(nfd: Nfd | null): boolean`
76+
77+
Checks if segment minting is unlocked for a parent/root NFD. This determines whether anyone besides the owner can mint segments under this NFD. Note that the owner of the parent NFD always has permission to mint segments regardless of this setting.
78+
79+
```typescript
80+
import { isSegmentMintingUnlocked } from '@txnlab/nfd-sdk'
81+
82+
// Check if segment minting is unlocked for a parent NFD
83+
const isUnlocked = isSegmentMintingUnlocked(parentNfd)
84+
```
85+
86+
### `canMintSegment(nfd: Nfd | null, callerAddress: string): boolean`
87+
88+
Checks if the caller is authorized to mint a segment for the given parent NFD.
89+
90+
```typescript
91+
import { canMintSegment } from '@txnlab/nfd-sdk'
92+
93+
// Check if the caller can mint a segment
94+
const canMint = canMintSegment(parentNfd, userAddress)
95+
```
96+
97+
## Error Handling Utilities
98+
99+
### `parseTransactionError(error: unknown): string`
100+
101+
Parses an error message and returns a user-friendly version.
102+
103+
```typescript
104+
import { parseTransactionError } from '@txnlab/nfd-sdk'
105+
106+
// Parse an error to get a user-friendly message
13107
try {
14-
// Some operation that might throw an error
15108
await nfd.setSigner(addr, signer).manage(nfdName).linkAddress(address)
16109
} catch (error) {
17-
// Parse the error to get a user-friendly message
18110
const friendlyError = parseTransactionError(error)
19111
console.error(friendlyError)
20112
}
113+
```
114+
115+
### `withErrorParsing<T>(fn: () => Promise<T>): Promise<T>`
116+
117+
A wrapper function that automatically parses errors thrown by the wrapped function.
118+
119+
```typescript
120+
import { withErrorParsing } from '@txnlab/nfd-sdk'
21121

22-
// Using the wrapper function
23122
const safeFunction = withErrorParsing(async () => {
24-
// Some operation that might throw an error
25123
return await nfd.setSigner(addr, signer).manage(nfdName).linkAddress(address)
26124
})
27125

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { isValidName, isSegmentName, getNfdBasename } from '../nfd'
2+
3+
/**
4+
* Determine the state of an NFD based on its properties
5+
* @param params - Parameters to determine the state
6+
* @returns The NFD state
7+
*/
8+
export function determineNfdState(params: {
9+
expired: boolean
10+
owner: string
11+
nfdAccount: string
12+
reservedFor?: string
13+
sellAmount: number
14+
isMinting: boolean
15+
}): 'available' | 'minting' | 'reserved' | 'forSale' | 'owned' | 'expired' {
16+
// Check expiration first
17+
if (params.expired) {
18+
return 'expired'
19+
}
20+
21+
// Check if reserved
22+
if (params.owner === params.nfdAccount && params.reservedFor) {
23+
return 'reserved'
24+
}
25+
26+
// Check if for sale (any non-zero sell amount)
27+
if (params.sellAmount !== 0) {
28+
return 'forSale'
29+
}
30+
31+
// Check if minting
32+
if (params.isMinting) {
33+
return 'minting'
34+
}
35+
36+
// Check if owned by someone other than the NFD account
37+
if (params.owner !== params.nfdAccount) {
38+
return 'owned'
39+
}
40+
41+
// Default to available
42+
return 'available'
43+
}
44+
45+
/**
46+
* Generate meta tags for an NFD based on its properties
47+
* @param name - The NFD name
48+
* @param segmentCount - The number of segments
49+
* @returns An array of meta tags
50+
*/
51+
export function generateMetaTags(name: string, segmentCount: number): string[] {
52+
const tags: string[] = []
53+
54+
// Only process valid NFD names
55+
if (isValidName(name)) {
56+
// Add character count tag based on basename length
57+
const basenameLength = getNfdBasename(name).length
58+
if (basenameLength < 10) {
59+
tags.push(`${basenameLength}_letters`)
60+
} else {
61+
tags.push('10+_letters')
62+
}
63+
64+
// Add segment status tags
65+
if (isSegmentName(name)) {
66+
tags.push('segment')
67+
} else if (segmentCount === 0) {
68+
tags.push('pristine')
69+
}
70+
}
71+
72+
return tags
73+
}

packages/sdk/src/utils/nfd.ts

-72
Original file line numberDiff line numberDiff line change
@@ -77,75 +77,3 @@ export function getNfdBasename(name: string): string {
7777
const parts = name.split('.')
7878
return parts[parts.length - 2]
7979
}
80-
81-
/**
82-
* Determine the state of an NFD based on its properties
83-
* @param params - Parameters to determine the state
84-
* @returns The NFD state
85-
*/
86-
export function determineNfdState(params: {
87-
expired: boolean
88-
owner: string
89-
nfdAccount: string
90-
reservedFor?: string
91-
sellAmount: number
92-
isMinting: boolean
93-
}): 'available' | 'minting' | 'reserved' | 'forSale' | 'owned' | 'expired' {
94-
// Check expiration first
95-
if (params.expired) {
96-
return 'expired'
97-
}
98-
99-
// Check if reserved
100-
if (params.owner === params.nfdAccount && params.reservedFor) {
101-
return 'reserved'
102-
}
103-
104-
// Check if for sale (any non-zero sell amount)
105-
if (params.sellAmount !== 0) {
106-
return 'forSale'
107-
}
108-
109-
// Check if minting
110-
if (params.isMinting) {
111-
return 'minting'
112-
}
113-
114-
// Check if owned by someone other than the NFD account
115-
if (params.owner !== params.nfdAccount) {
116-
return 'owned'
117-
}
118-
119-
// Default to available
120-
return 'available'
121-
}
122-
123-
/**
124-
* Generate meta tags for an NFD based on its properties
125-
* @param name - The NFD name
126-
* @param segmentCount - The number of segments
127-
* @returns An array of meta tags
128-
*/
129-
export function generateMetaTags(name: string, segmentCount: number): string[] {
130-
const tags: string[] = []
131-
132-
// Only process valid NFD names
133-
if (isValidName(name)) {
134-
// Add character count tag based on basename length
135-
const basenameLength = getNfdBasename(name).length
136-
if (basenameLength < 10) {
137-
tags.push(`${basenameLength}_letters`)
138-
} else {
139-
tags.push('10+_letters')
140-
}
141-
142-
// Add segment status tags
143-
if (isSegmentName(name)) {
144-
tags.push('segment')
145-
} else if (segmentCount === 0) {
146-
tags.push('pristine')
147-
}
148-
}
149-
150-
return tags
151-
}

packages/sdk/tests/utils/nfd.test.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
import { describe, it, expect } from 'vitest'
22

3+
import {
4+
determineNfdState,
5+
generateMetaTags,
6+
} from '../../src/utils/internal/nfd'
37
import {
48
isSegmentMintingUnlocked,
59
canMintSegment,
610
isValidName,
711
isSegmentName,
812
extractParentName,
913
getNfdBasename,
10-
determineNfdState,
11-
generateMetaTags,
1214
} from '../../src/utils/nfd'
1315

1416
// Create a minimal mock of the Nfd type for testing

packages/sdk/tests/utils/state.test.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import { AppState } from '@algorandfoundation/algokit-utils/types/app'
22
import { describe, it, expect, vi } from 'vitest'
33

4-
import { parseString, parseUint64, parseAddress } from '../../src/utils/state'
4+
import {
5+
parseString,
6+
parseUint64,
7+
parseAddress,
8+
} from '../../src/utils/internal/state'
59

610
// Mock the algosdk decodeUint64 function
711
vi.mock('algosdk', () => ({

0 commit comments

Comments
 (0)