Common utilities and configuration shared across all apps in the AnySpend x402 monorepo.
This package provides reusable code to ensure consistency across the client, server, and agent applications. It prevents code duplication and serves as a single source of truth for shared logic.
Network definitions for EVM-compatible chains supported by the demo.
import { EVM_NETWORKS } from '@shared/config/networks';
// Access network info
const baseNetwork = EVM_NETWORKS.base;
console.log(baseNetwork.name); // "Base"
console.log(baseNetwork.chainId); // 8453
console.log(baseNetwork.nativeCurrency.symbol); // "ETH"Supported Networks:
- Base (Mainnet & Sepolia)
- Polygon (Mainnet & Amoy)
- Arbitrum (Mainnet & Sepolia)
Network Object Structure:
{
id: number; // Chain ID
name: string; // Display name
chainId: number; // Chain ID (same as id)
nativeCurrency: {
name: string;
symbol: string;
decimals: number;
};
rpcUrls: {
default: { http: string[] };
public: { http: string[] };
};
blockExplorers: {
default: {
name: string;
url: string;
};
};
testnet?: boolean;
}Detection logic for EIP-2612 permit support on ERC20 tokens.
import { checkPermitSupport } from '@shared/utils/permitDetection';
// Check if a token supports gasless permits
const supportsPermit = await checkPermitSupport(
tokenAddress, // Token contract address
rpcUrl, // RPC endpoint
chainId // Chain ID
);
if (supportsPermit) {
// Use permit-based approval (no gas required)
} else {
// Fall back to standard approve()
}How it works:
- Checks if contract implements
permit()function (EIP-2612) - Validates the function signature
- Returns boolean indicating support
- Includes commented EIP-3009 logic for future extensibility
Use Cases:
- Client PaymentModal: Determine if permit UI should be shown
- Agent payment flow: Select optimal approval method
- Token compatibility checking
All apps have path aliases configured:
// Instead of relative paths
import { EVM_NETWORKS } from '../../../shared/config/networks';
import { checkPermitSupport } from '../../../shared/utils/permitDetection';
// Use clean aliases
import { EVM_NETWORKS } from '@shared/config/networks';
import { checkPermitSupport } from '@shared/utils/permitDetection';Each app's tsconfig.json includes:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@shared/*": ["../shared/*"]
}
}
}For Vite (client app), also add to vite.config.ts:
{
resolve: {
alias: {
'@shared': path.resolve(__dirname, '../shared')
}
}
}-
Configuration: Add to
config/directory- Network configs
- Token lists
- Chain-specific constants
-
Utilities: Add to
utils/directory- Pure functions (no side effects)
- Reusable logic used across multiple apps
- Type guards and validators
-
Types: Consider adding a
types/directory for shared TypeScript types
✅ Do:
- Keep functions pure and side-effect free
- Add comprehensive JSDoc comments
- Export everything needed by consuming apps
- Write self-contained, testable code
- Use TypeScript for type safety
❌ Don't:
- Import app-specific dependencies
- Add UI components (those belong in client app)
- Include environment-specific logic
- Create circular dependencies between apps
Since this is shared code, ensure strong typing:
// Good: Explicit types
export function checkPermitSupport(
tokenAddress: `0x${string}`,
rpcUrl: string,
chainId: number
): Promise<boolean>
// Bad: Any types
export function checkPermitSupport(
tokenAddress: any,
rpcUrl: any,
chainId: any
): Promise<any>apps/shared/
├── config/
│ └── networks.ts # EVM network definitions
├── utils/
│ └── permitDetection.ts # EIP-2612 permit detection
├── package.json # Package configuration
└── README.md # This file
Potential shared code to add:
- Token Lists: Common token addresses per chain
- Formatting Utilities: Amount formatting, address shortening
- Validation Functions: Input validation, address checks
- Constants: Common values used across apps
- Type Definitions: Shared TypeScript interfaces
- ABI Definitions: Common contract ABIs
The shared package uses TypeScript with no build step. Files are imported directly using path aliases, and TypeScript compiles them as part of each consuming app's build process.
Note: Compiled .js, .d.ts, and .map files are gitignored to avoid committing build artifacts.
When adding utilities, consider adding unit tests:
# Future: Add testing framework
pnpm test:shared