@tw3/esp
Version:
Storage protocol built on ethereum using datapoint archetecture and a registry contract for handling royalties.
323 lines (261 loc) • 9.45 kB
Markdown
# Ethereum Storage Protocol (ESP) - LLM Context Guide
**Version:** 0.3.3
**Package:** `ethereum-storage` or `@tw3/esp`
**License:** AGPL-3.0
## Project Summary
ESP is a decentralized storage protocol built on Ethereum that provides:
- **Immutable, content-addressed storage** via DataPointStorage contract
- **Economic incentive layer** via DataPointRegistry contract with publisher royalties
- **TypeScript-first integration** with full TypeChain types and utilities
- **Production-ready contracts** deployed on multiple networks
## Architecture
```
DataPointRegistry (Economic Layer)
↓ delegates storage to
DataPointStorage (Storage Layer)
```
## Package Structure
### Main Exports (`ethereum-storage`)
```typescript
// Contract types & factories (TypeChain generated)
import {
DataPointRegistry, DataPointRegistry__factory,
DataPointStorage, DataPointStorage__factory,
IDataPointRegistry, IDataPointRegistry__factory,
IDataPointStorage, IDataPointStorage__factory
} from 'ethereum-storage';
// Deployment utilities
import {
espDeployments, loadContract, getContractAddress,
getDeploymentInfo, getSupportedChainIds
} from 'ethereum-storage';
// All TypeScript types
import type {
ContractTransaction, ContractTransactionResponse,
BigNumberish, BytesLike, Signer, Provider,
BaseOverrides, PayableOverrides, Overrides
} from 'ethereum-storage';
```
### Subpath Exports
```typescript
// Contract ABIs and utilities
import {
DataPointRegistryABI, DataPointStorageABI,
getContractFactory, ContractNames
} from 'ethereum-storage/contracts';
// Deployment-specific utilities
import {
espDeployments, loadContract, getContractAddress
} from 'ethereum-storage/deployments';
// Type-only imports
import type {
DataPointRegistry, DataPointStorage,
IDataPointRegistry, IDataPointStorage
} from 'ethereum-storage/types';
```
## Contract Interfaces
### DataPointStorage (Core Storage Layer)
```typescript
interface IDataPointStorage {
// Write immutable data, returns content address
writeDataPoint(data: BytesLike): Promise<ContractTransaction>;
// Read data by content address
readDataPoint(address: string): Promise<string>;
// Calculate content address without writing
calculateAddress(data: BytesLike): Promise<string>;
// Get size of stored data
dataPointSize(address: string): Promise<BigNumber>;
}
```
### DataPointRegistry (Economic Layer)
```typescript
interface IDataPointRegistry {
// Register data with publisher royalties
registerDataPoint(
data: BytesLike,
publisher: string,
overrides?: PayableOverrides
): Promise<ContractTransaction>;
// Get royalty cost for accessing data
getDataPointRoyalty(address: string): Promise<BigNumber>;
// Pay royalty and read data
readDataPoint(
address: string,
overrides?: PayableOverrides
): Promise<ContractTransaction>;
// Collect earned royalties
collectRoyalties(
amount: BigNumberish,
withdrawTo: string
): Promise<ContractTransaction>;
}
```
## Working with Deployments
### Chain ID System
ESP uses standard Ethereum chain IDs:
- **Sepolia Testnet:** 11155111
- **Ethereum Mainnet:** 1
- **Polygon:** 137
- **Hardhat/Local:** 31337
### Deployment Utilities
```typescript
import {
getSupportedChainIds, getContractAddress,
loadContract, getDeploymentInfo
} from 'ethereum-storage';
// Get all supported networks
const chainIds = getSupportedChainIds(); // [11155111]
// Get contract addresses
const registryAddr = getContractAddress(11155111, 'dpr');
const storageAddr = getContractAddress(11155111, 'dps');
// Get full deployment info
const deployInfo = getDeploymentInfo(11155111, 'dpr');
/* Returns: {
contractAddress: '0x...',
deployerAddress: '0x...',
txHash: '0x...',
deployedAt: '2025-05-31T18:59:12.000Z',
constructors: { ... }
} */
// Load contract instances directly
const registry = loadContract(11155111, 'dpr', provider);
const storage = loadContract(11155111, 'dps', signer);
```
## Complete Integration Examples
### Basic Usage Pattern
```typescript
import { ethers } from 'ethers';
import {
DataPointRegistry__factory,
loadContract,
getContractAddress
} from 'ethereum-storage';
const provider = new ethers.JsonRpcProvider('YOUR_RPC_URL');
const signer = new ethers.Wallet('PRIVATE_KEY', provider);
const chainId = 11155111; // Sepolia
// Method 1: Manual connection
const registryAddr = getContractAddress(chainId, 'dpr');
const registry = DataPointRegistry__factory.connect(registryAddr, signer);
// Method 2: Helper function (recommended)
const registry2 = loadContract(chainId, 'dpr', signer);
// Store data with royalties
const data = ethers.toUtf8Bytes("Hello ESP!");
const tx = await registry.registerDataPoint(data, signer.address);
const receipt = await tx.wait();
// Extract data point address from event
const event = receipt.logs.find(log =>
log.topics[0] === registry.interface.getEventTopic('DataPointRegistered')
);
const dataPointAddress = event.topics[1];
// Read data (free from storage layer)
const storage = loadContract(chainId, 'dps', provider);
const storedData = await storage.readDataPoint(dataPointAddress);
```
### Advanced Usage with Royalties
```typescript
import { parseEther, formatUnits } from 'ethers';
import { loadContract, getContractAddress } from 'ethereum-storage';
const registry = loadContract(11155111, 'dpr', signer);
// Check royalty cost before reading
const royaltyCost = await registry.getDataPointRoyalty(dataPointAddress);
console.log(`Royalty: ${formatUnits(royaltyCost, 'ether')} ETH`);
// Read data with royalty payment
const readTx = await registry.readDataPoint(dataPointAddress, {
value: royaltyCost
});
await readTx.wait();
// Publisher collects royalties
const balance = await provider.getBalance(publisherAddress);
await registry.collectRoyalties(balance, publisherAddress);
```
### Working with ABIs Directly
```typescript
import { DataPointRegistryABI, DataPointStorageABI } from 'ethereum-storage/contracts';
import { ethers } from 'ethers';
// Create contract instance with ABI
const registry = new ethers.Contract(
registryAddress,
DataPointRegistryABI,
signer
);
// Use for external tools (Wagmi, Web3.js, etc.)
const wagmiConfig = {
address: registryAddress,
abi: DataPointRegistryABI,
// ...
};
```
## Type Safety Guidelines
### Contract Type Assertions
```typescript
import type { DataPointRegistry, DataPointStorage } from 'ethereum-storage';
// Type-safe contract instances
const registry = loadContract(chainId, 'dpr', signer) as DataPointRegistry;
const storage = loadContract(chainId, 'dps', provider) as DataPointStorage;
// Type-safe transaction handling
import type { ContractTransaction } from 'ethereum-storage';
const tx: ContractTransaction = await registry.registerDataPoint(data, publisher);
```
### Override Types
```typescript
import type { PayableOverrides, Overrides } from 'ethereum-storage';
// For payable functions
const payableOptions: PayableOverrides = {
value: parseEther('0.01'),
gasLimit: 500000
};
// For non-payable functions
const options: Overrides = {
gasLimit: 300000
};
```
## Common Integration Patterns
### Error Handling
```typescript
try {
const tx = await registry.registerDataPoint(data, publisher);
await tx.wait();
} catch (error) {
if (error.reason === 'InsufficientRoyaltyPayment') {
// Handle specific contract error
}
// Handle other errors
}
```
### Event Listening
```typescript
// Listen for data point registrations
registry.on('DataPointRegistered', (dataPointAddress, publisher, event) => {
console.log(`New data point: ${dataPointAddress} by ${publisher}`);
});
// Listen for royalty payments
registry.on('RoyaltiesPaid', (dataPointAddress, payer, amount, event) => {
console.log(`Royalty paid: ${formatUnits(amount, 'ether')} ETH`);
});
```
### Multi-chain Support
```typescript
const networks = [1, 11155111, 137]; // Mainnet, Sepolia, Polygon
const registries = networks.map(chainId => {
const provider = getProvider(chainId); // Your provider logic
return {
chainId,
registry: loadContract(chainId, 'dpr', provider)
};
}).filter(({ registry }) => registry !== undefined);
```
## Key Implementation Notes
1. **Content Addressing**: ESP uses deterministic addresses based on keccak256(data)
2. **Gas Optimization**: Storage layer is append-only, registry handles all logic
3. **Royalty Model**: Publishers set rates, protocol collects fees automatically
4. **Upgradeability**: Registry is upgradeable, storage layer is immutable
5. **Security**: Full reentrancy protection, comprehensive access controls
## Quick Reference
| Function | Contract | Purpose | Payable |
|----------|----------|---------|---------|
| `writeDataPoint()` | Storage | Store data directly | No |
| `registerDataPoint()` | Registry | Store + set royalties | Yes |
| `readDataPoint()` | Storage | Read (free) | No |
| `readDataPoint()` | Registry | Read + pay royalty | Yes |
| `getDataPointRoyalty()` | Registry | Check royalty cost | No |
| `collectRoyalties()` | Registry | Withdraw earnings | No |