Developer Experience Guide
Essential information for developing with and contributing to the Astral SDK.
Development Setup
Prerequisites
• Node.js 18+ and pnpm
• A Web3 wallet (MetaMask or similar)
• Access to a blockchain RPC endpoint
Environment Configuration
Create a .env
file in your project root:
# Required for onchain operations
ETHEREUM_RPC_URL=https://sepolia.infura.io/v3/YOUR_KEY
PRIVATE_KEY=your_test_wallet_private_key
# Required for API operations
ASTRAL_API_URL=https://api.astral.global
# Optional
DEBUG=astral:*
※ Security Note: Never commit .env
files or expose private keys in code.
Module Workflow
Creating a New Extension
- Create the extension file:
// src/extensions/location/builtins/MyFormat.ts
import { LocationFormatExtension } from '../../types';
export class MyFormatExtension implements LocationFormatExtension {
name = 'my-format';
validate(location: unknown): boolean {
// Validation logic
}
encode(location: unknown): string {
// Encoding logic
}
decode(encoded: string): unknown {
// Decoding logic
}
}
- Register the extension:
// src/extensions/location/index.ts
import { MyFormatExtension } from './builtins/MyFormat';
export const locationExtensions = [
// ... existing extensions
new MyFormatExtension()
];
- Add tests:
// test/extensions/location/MyFormat.test.ts
import { MyFormatExtension } from '@/extensions/location/builtins/MyFormat';
describe('MyFormatExtension', () => {
const extension = new MyFormatExtension();
it('validates correct format', () => {
expect(extension.validate(validData)).toBe(true);
});
});
JavaScript vs TypeScript
Why the SDK Publishes JavaScript
The published NPM package contains:
• Transpiled JavaScript (ES2020) for broad compatibility
• TypeScript declaration files (.d.ts
) for type safety
• Source maps for debugging
Development is TypeScript
All source code is written in TypeScript: • Full type safety during development • Better IDE support and autocomplete • Compile-time error checking
Build Process
# TypeScript source → JavaScript output
pnpm run build
# Output structure:
dist/
├── index.js # Transpiled JavaScript
├── index.d.ts # Type declarations
└── index.js.map # Source maps
Testing Patterns
Unit Test Setup
import { vi } from 'vitest';
import { AstralSDK } from '@/core/AstralSDK';
// Mock provider
const mockProvider = {
request: vi.fn(),
getSigner: vi.fn()
};
// Test instance
const sdk = new AstralSDK({
provider: mockProvider
});
Integration Test Pattern
describe('Offchain Workflow', () => {
let sdk: AstralSDK;
beforeEach(async () => {
// Use test wallet
const provider = new ethers.JsonRpcProvider(process.env.TEST_RPC);
const signer = new ethers.Wallet(process.env.TEST_PRIVATE_KEY, provider);
sdk = new AstralSDK({ signer });
});
it('creates and verifies attestation', async () => {
const attestation = await sdk.createOffchainLocationAttestation({
location: { type: 'Point', coordinates: [0, 0] }
});
const result = await sdk.verifyOffchainLocationAttestation(attestation);
expect(result.isValid).toBe(true);
});
});
Mocking Best Practices
// Mock ethers provider
vi.mock('ethers', () => ({
ethers: {
JsonRpcProvider: vi.fn(() => mockProvider),
Wallet: vi.fn(() => mockSigner)
}
}));
// Mock network requests
vi.mock('@/api/AstralApiClient', () => ({
AstralApiClient: vi.fn(() => ({
publishAttestation: vi.fn().mockResolvedValue({ uid: '0x123' })
}))
}));
Common Development Tasks
Running Tests
# All tests
pnpm test
# Specific test file
pnpm test MyFormat.test.ts
# Watch mode
pnpm test -- --watch
# Coverage
pnpm test -- --coverage
Debugging
Enable debug logging:
const sdk = new AstralSDK({
provider,
debug: true // Enables console logging
});
Or via environment:
DEBUG=astral:* pnpm test
Type Checking
# Check types
pnpm run typecheck
# Watch mode
pnpm run typecheck -- --watch
Project Structure Best Practices
File Organization
src/
├── core/ # Core SDK logic
├── eas/ # EAS-specific code
├── extensions/ # Extension system
├── api/ # External API clients
├── storage/ # Storage adapters
└── utils/ # Shared utilities
Import Conventions
// 1. Node built-ins
import { readFile } from 'fs/promises';
// 2. External packages
import { ethers } from 'ethers';
import { EAS } from '@ethereum-attestation-service/eas-sdk';
// 3. Internal absolute imports
import { AstralSDK } from '@/core/AstralSDK';
import { LocationFormatExtension } from '@/extensions/types';
// 4. Relative imports
import { validateLocation } from './validation';
Naming Conventions
• Files: PascalCase.ts
for classes, camelCase.ts
for utilities
• Extensions: Suffix with Extension
(e.g., GeoJSONExtension
)
• Tests: Mirror source structure with .test.ts
suffix
• Types: Export from dedicated types.ts
files
Performance Considerations
Batch Operations
// Good: Batch attestations
const attestations = await Promise.all(
locations.map(location =>
sdk.createOffchainLocationAttestation({ location })
)
);
// Avoid: Sequential operations
for (const location of locations) {
await sdk.createOffchainLocationAttestation({ location });
}
Caching Patterns
// Cache provider instances
const providerCache = new Map<string, ethers.Provider>();
function getProvider(rpcUrl: string) {
if (!providerCache.has(rpcUrl)) {
providerCache.set(rpcUrl, new ethers.JsonRpcProvider(rpcUrl));
}
return providerCache.get(rpcUrl)!;
}
Troubleshooting
Common Issues
TypeScript errors after install
# Regenerate types
pnpm run build
Test failures with providers
# Ensure test environment
cp .env.example .env.test
# Add your test RPC and private key
Module resolution errors
# Clear cache and reinstall
rm -rf node_modules pnpm-lock.yaml
pnpm install
Debug Commands
# Verbose logging
DEBUG=* pnpm test
# Check build output
pnpm run build && ls -la dist/
# Verify types
pnpm run typecheck -- --listFiles
Contributing Guidelines
Before Submitting PR
- Run all checks:
pnpm run lint
pnpm run typecheck
pnpm test
-
Update documentation if adding features
-
Follow commit conventions:
feat: add new location format
fix: correct validation logic
docs: update API examples
test: add integration tests
Code Review Checklist
□ Tests pass and cover new code □ Types are properly defined □ Documentation is updated □ No hardcoded values □ Follows project conventions
Next Steps
• Extension System - Build custom extensions