z0gcode 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,174 @@
1
+ # Security Patterns
2
+
3
+ Cross-cutting security patterns for 0G development: key management, data integrity, contract
4
+ security, and TEE verification.
5
+
6
+ ## Key Management
7
+
8
+ ### Environment Variables Pattern
9
+
10
+ ```bash
11
+ # .env — NEVER commit this file
12
+ PRIVATE_KEY=your_private_key_here
13
+ RPC_URL=https://evmrpc-testnet.0g.ai
14
+ ```
15
+
16
+ ```typescript
17
+ import 'dotenv/config';
18
+
19
+ // CORRECT — load from environment
20
+ const wallet = new ethers.Wallet(process.env.PRIVATE_KEY!, provider);
21
+
22
+ // WRONG — hardcoded key (NEVER do this)
23
+ // const wallet = new ethers.Wallet("0xabc123...", provider);
24
+ ```
25
+
26
+ ### .gitignore Requirements
27
+
28
+ ```gitignore
29
+ # MUST be in .gitignore
30
+ .env
31
+ .env.local
32
+ .env.*.local
33
+ *.key
34
+ *.pem
35
+ ```
36
+
37
+ ### Validation Pattern
38
+
39
+ ```typescript
40
+ function validateEnv() {
41
+ const required = ['PRIVATE_KEY', 'RPC_URL'];
42
+ const missing = required.filter((key) => !process.env[key]);
43
+ if (missing.length > 0) {
44
+ throw new Error(`Missing environment variables: ${missing.join(', ')}`);
45
+ }
46
+ }
47
+ ```
48
+
49
+ ## Data Integrity (Storage)
50
+
51
+ ### Verified Downloads
52
+
53
+ ```typescript
54
+ // ALWAYS use verified=true in production
55
+ await indexer.download(rootHash, outputPath, true);
56
+ // Throws if Merkle proof verification fails
57
+ ```
58
+
59
+ ### Hash Verification
60
+
61
+ ```typescript
62
+ import { ZgFile } from '@0glabs/0g-ts-sdk';
63
+
64
+ async function verifyFile(filePath: string, expectedHash: string): Promise<boolean> {
65
+ const file = await ZgFile.fromFilePath(filePath);
66
+ try {
67
+ const [tree, err] = await file.merkleTree();
68
+ if (err) return false;
69
+ return tree.rootHash() === expectedHash;
70
+ } finally {
71
+ await file.close();
72
+ }
73
+ }
74
+ ```
75
+
76
+ ## TEE Verification (Compute)
77
+
78
+ Always verify provider TEE attestation before sensitive workloads:
79
+
80
+ ```typescript
81
+ const services = await broker.inference.listService();
82
+ const verifiedProviders = services.filter((s) => s.teeVerified === true);
83
+
84
+ if (verifiedProviders.length === 0) {
85
+ throw new Error('No TEE-verified providers available');
86
+ }
87
+ ```
88
+
89
+ ## Smart Contract Security
90
+
91
+ ### Access Control
92
+
93
+ ```solidity
94
+ // SPDX-License-Identifier: MIT
95
+ pragma solidity ^0.8.24;
96
+
97
+ import "@openzeppelin/contracts/access/Ownable.sol";
98
+
99
+ contract SecureStorage is Ownable {
100
+ mapping(bytes32 => address) public fileOwners;
101
+
102
+ function registerFile(bytes32 rootHash) external {
103
+ require(fileOwners[rootHash] == address(0), "Already registered");
104
+ fileOwners[rootHash] = msg.sender;
105
+ }
106
+
107
+ function verifyOwner(bytes32 rootHash, address owner) external view returns (bool) {
108
+ return fileOwners[rootHash] == owner;
109
+ }
110
+ }
111
+ ```
112
+
113
+ ### Input Validation
114
+
115
+ ```solidity
116
+ function store(bytes32 rootHash, string calldata metadata) external {
117
+ require(rootHash != bytes32(0), "Invalid root hash");
118
+ require(bytes(metadata).length > 0, "Empty metadata");
119
+ require(bytes(metadata).length <= 1024, "Metadata too long");
120
+ // ... storage logic
121
+ }
122
+ ```
123
+
124
+ ## API Security
125
+
126
+ ### Rate Limiting Pattern
127
+
128
+ ```typescript
129
+ const requestTimes: number[] = [];
130
+ const MAX_REQUESTS = 10;
131
+ const WINDOW_MS = 60_000;
132
+
133
+ function checkRateLimit(): boolean {
134
+ const now = Date.now();
135
+ const windowStart = now - WINDOW_MS;
136
+ // Remove old entries
137
+ while (requestTimes.length > 0 && requestTimes[0] < windowStart) {
138
+ requestTimes.shift();
139
+ }
140
+ if (requestTimes.length >= MAX_REQUESTS) return false;
141
+ requestTimes.push(now);
142
+ return true;
143
+ }
144
+ ```
145
+
146
+ ### Balance Check Before Operations
147
+
148
+ ```typescript
149
+ async function ensureSufficientBalance(broker: any, minBalance: number) {
150
+ // getLedger() returns tuple: [0]=address, [1]=totalBalance, [2]=availableBalance
151
+ const account = await broker.ledger.getLedger();
152
+ const available = parseFloat(ethers.formatEther(account[2]));
153
+ if (available < minBalance) {
154
+ throw new Error(`Insufficient balance: ${available} 0G available, ${minBalance} 0G required`);
155
+ }
156
+ }
157
+ ```
158
+
159
+ ## Checklist
160
+
161
+ - [ ] Private keys loaded from `.env`, never hardcoded
162
+ - [ ] `.env` is in `.gitignore`
163
+ - [ ] All downloads use verified mode (`true`)
164
+ - [ ] TEE verification checked for compute providers
165
+ - [ ] Smart contracts use access control
166
+ - [ ] Input validation on all public functions
167
+ - [ ] Balance checked before operations
168
+ - [ ] No secrets in logs or error messages
169
+
170
+ ## References
171
+
172
+ - [OWASP Smart Contract Top 10](https://owasp.org/www-project-smart-contract-top-10/)
173
+ - [OpenZeppelin Contracts](https://docs.openzeppelin.com/contracts)
174
+ - See also: [NETWORK_CONFIG.md](./NETWORK_CONFIG.md)
@@ -0,0 +1,167 @@
1
+ # Storage Patterns
2
+
3
+ Architecture, SDK reference, and best practices for 0G decentralized storage.
4
+
5
+ ## Architecture Overview
6
+
7
+ ```
8
+ ┌─────────────────────────────────┐
9
+ │ Log Layer │
10
+ │ (Raw file / blob storage) │
11
+ ├─────────────────────────────────┤
12
+ │ 0G Consensus │
13
+ │ (Data availability proofs) │
14
+ └─────────────────────────────────┘
15
+ ```
16
+
17
+ ## Log Layer (File Storage)
18
+
19
+ ### Core Concepts
20
+
21
+ - **ZgFile**: Represents a file in the 0G storage system
22
+ - **Merkle Tree**: Every file is split into chunks and organized as a Merkle tree
23
+ - **Root Hash**: The Merkle root — unique identifier for the file content
24
+ - **Indexer**: Service that locates data across storage nodes
25
+
26
+ ### File Upload Lifecycle
27
+
28
+ 1. Create a `ZgFile` from local file path or buffer
29
+ 2. Generate the Merkle tree (computes root hash)
30
+ 3. Upload via the Indexer (distributes chunks to storage nodes)
31
+ 4. Store the root hash for later retrieval
32
+ 5. **Close the file handle** (critical — prevents memory leaks)
33
+
34
+ ### Upload Pattern
35
+
36
+ ```typescript
37
+ import { ZgFile, Indexer } from '@0glabs/0g-ts-sdk';
38
+ import { ethers } from 'ethers';
39
+ import 'dotenv/config';
40
+
41
+ const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
42
+ const wallet = new ethers.Wallet(process.env.PRIVATE_KEY!, provider);
43
+
44
+ const indexer = new Indexer(process.env.STORAGE_INDEXER!);
45
+ const file = await ZgFile.fromFilePath(filePath);
46
+
47
+ try {
48
+ const [tree, err] = await file.merkleTree();
49
+ if (err) throw err;
50
+
51
+ const rootHash = tree!.rootHash();
52
+ console.log('Root hash:', rootHash);
53
+
54
+ const [tx, uploadErr] = await indexer.upload(file, process.env.RPC_URL!, wallet);
55
+ if (uploadErr) throw new Error(`Upload failed: ${uploadErr.message}`);
56
+ console.log('Upload tx:', tx);
57
+
58
+ return rootHash;
59
+ } finally {
60
+ await file.close();
61
+ }
62
+ ```
63
+
64
+ ### Download Pattern
65
+
66
+ ```typescript
67
+ import { Indexer } from '@0glabs/0g-ts-sdk';
68
+
69
+ const indexer = new Indexer(process.env.STORAGE_INDEXER!);
70
+
71
+ // download() can throw OR return an error — always use try/catch
72
+ try {
73
+ const err = await indexer.download(rootHash, outputPath, /* verified */ true);
74
+ if (err) throw err;
75
+ } catch (error: any) {
76
+ throw new Error(`Download failed: ${error.message}`);
77
+ }
78
+ ```
79
+
80
+ ### Upload from Buffer
81
+
82
+ ```typescript
83
+ import { ZgFile } from '@0glabs/0g-ts-sdk';
84
+ import * as fs from 'fs';
85
+ import * as os from 'os';
86
+ import * as path from 'path';
87
+
88
+ // Write buffer to temp file (SDK requires file path)
89
+ const tempPath = path.join(os.tmpdir(), `0g-upload-${Date.now()}`);
90
+ fs.writeFileSync(tempPath, buffer);
91
+
92
+ const file = await ZgFile.fromFilePath(tempPath);
93
+ try {
94
+ const [tree, err] = await file.merkleTree();
95
+ if (err) throw err;
96
+ const rootHash = tree!.rootHash();
97
+ const [, uploadErr] = await indexer.upload(file, process.env.RPC_URL!, wallet);
98
+ if (uploadErr) throw new Error(`Upload failed: ${uploadErr.message}`);
99
+ return rootHash;
100
+ } finally {
101
+ await file.close();
102
+ fs.unlinkSync(tempPath); // Clean up temp file
103
+ }
104
+ ```
105
+
106
+ ## Merkle Verification
107
+
108
+ ### Computing Root Hash
109
+
110
+ ```typescript
111
+ import { ZgFile } from '@0glabs/0g-ts-sdk';
112
+
113
+ const file = await ZgFile.fromFilePath(filePath);
114
+ try {
115
+ const [tree, err] = await file.merkleTree();
116
+ if (err) throw err;
117
+ console.log('Root hash:', tree.rootHash());
118
+ } finally {
119
+ await file.close();
120
+ }
121
+ ```
122
+
123
+ ### Verified Download
124
+
125
+ ```typescript
126
+ // The third parameter enables Merkle proof verification
127
+ // Note: download() can throw or return errors — always use try/catch
128
+ try {
129
+ const err = await indexer.download(rootHash, outputPath, true);
130
+ if (err) throw err;
131
+ } catch (error: any) {
132
+ throw new Error(`Download failed: ${error.message}`);
133
+ }
134
+ ```
135
+
136
+ ## Critical Rules
137
+
138
+ ### ALWAYS
139
+
140
+ - Generate the Merkle tree before uploading
141
+ - Close file handles after upload (`file.close()`)
142
+ - Store root hashes — they are the ONLY way to retrieve files
143
+ - Use verified downloads in production (third param = `true`)
144
+ - Clean up temp files when uploading from buffers
145
+
146
+ ### NEVER
147
+
148
+ - Skip Merkle tree generation before upload
149
+ - Forget to close `ZgFile` handles (causes memory leaks)
150
+ - Lose the root hash (data becomes irretrievable)
151
+ - Upload without a funded wallet (transaction will fail)
152
+
153
+ ## Common Errors
154
+
155
+ | Error | Cause | Fix |
156
+ | ----------------------- | -------------------------- | ----------------------------------- |
157
+ | `merkle tree error` | File is empty or corrupted | Verify file exists and has content |
158
+ | `insufficient funds` | Wallet has no 0G | Fund wallet from faucet (testnet) |
159
+ | `indexer not available` | Wrong indexer URL | Check STORAGE_INDEXER in .env |
160
+ | `file not found` | Root hash doesn't exist | Verify root hash is correct |
161
+ | `connection refused` | RPC endpoint down | Try alternative RPC or check status |
162
+
163
+ ## References
164
+
165
+ - [0G Storage SDK Docs](https://docs.0g.ai/build-with-0g/storage-network/sdk)
166
+ - [0G Storage Architecture](https://docs.0g.ai/learn/0g-storage)
167
+ - See also: [NETWORK_CONFIG.md](./NETWORK_CONFIG.md), [SECURITY.md](./SECURITY.md)
@@ -0,0 +1,263 @@
1
+ # Testing Patterns
2
+
3
+ Testing strategies, mock patterns, and testnet workflows for 0G applications.
4
+
5
+ ## Testing Strategy
6
+
7
+ ```
8
+ ┌─────────────────────────────────────┐
9
+ │ Integration Tests │
10
+ │ (Testnet, real transactions) │
11
+ ├─────────────────────────────────────┤
12
+ │ Unit Tests │
13
+ │ (Mocked SDK, local only) │
14
+ ├─────────────────────────────────────┤
15
+ │ Static Analysis │
16
+ │ (TypeScript, Solhint, Slither) │
17
+ └─────────────────────────────────────┘
18
+ ```
19
+
20
+ ## Unit Testing
21
+
22
+ ### Mock Storage SDK
23
+
24
+ ```typescript
25
+ import { describe, it, expect, vi } from 'vitest';
26
+
27
+ // Mock the 0G SDK
28
+ vi.mock('@0glabs/0g-ts-sdk', () => ({
29
+ ZgFile: {
30
+ fromFilePath: vi.fn().mockResolvedValue({
31
+ merkleTree: vi.fn().mockResolvedValue([{ rootHash: () => '0xabc123' }, null]),
32
+ close: vi.fn(),
33
+ }),
34
+ },
35
+ Indexer: vi.fn().mockImplementation(() => ({
36
+ upload: vi.fn().mockResolvedValue('0xtxhash'),
37
+ download: vi.fn().mockResolvedValue(undefined),
38
+ })),
39
+ }));
40
+
41
+ describe('Storage Upload', () => {
42
+ it('should return root hash after upload', async () => {
43
+ const { ZgFile, Indexer } = await import('@0glabs/0g-ts-sdk');
44
+ const file = await ZgFile.fromFilePath('test.txt');
45
+ const [tree] = await file.merkleTree();
46
+ expect(tree.rootHash()).toBe('0xabc123');
47
+ });
48
+ });
49
+ ```
50
+
51
+ ### Mock Compute Broker
52
+
53
+ ```typescript
54
+ vi.mock('@0glabs/0g-serving-broker', () => ({
55
+ createZGComputeNetworkBroker: vi.fn().mockResolvedValue({
56
+ inference: {
57
+ listService: vi
58
+ .fn()
59
+ .mockResolvedValue([
60
+ { providerAddress: '0x123', serviceType: 'chatbot', model: 'test-model' },
61
+ ]),
62
+ getServiceMetadata: vi.fn().mockResolvedValue({
63
+ endpoint: 'http://localhost:3000',
64
+ model: 'test-model',
65
+ }),
66
+ getRequestHeaders: vi.fn().mockResolvedValue({ Authorization: 'Bearer test' }),
67
+ processResponse: vi.fn().mockResolvedValue(true),
68
+ acknowledgeProviderSigner: vi.fn().mockResolvedValue(undefined),
69
+ },
70
+ ledger: {
71
+ getLedger: vi.fn().mockResolvedValue({
72
+ totalBalance: 10000000000000000000n,
73
+ availableBalance: 5000000000000000000n,
74
+ }),
75
+ depositFund: vi.fn(),
76
+ transferFund: vi.fn(),
77
+ },
78
+ }),
79
+ }));
80
+ ```
81
+
82
+ ### Mock ethers
83
+
84
+ ```typescript
85
+ vi.mock('ethers', async () => {
86
+ const actual = await vi.importActual('ethers');
87
+ return {
88
+ ...actual,
89
+ JsonRpcProvider: vi.fn().mockImplementation(() => ({
90
+ getNetwork: vi.fn().mockResolvedValue({ chainId: 16602n }),
91
+ })),
92
+ Wallet: vi.fn().mockImplementation(() => ({
93
+ address: '0xTestAddress',
94
+ provider: {},
95
+ })),
96
+ };
97
+ });
98
+ ```
99
+
100
+ ## Contract Testing
101
+
102
+ ### Hardhat Tests
103
+
104
+ ```typescript
105
+ import { expect } from 'chai';
106
+ import { ethers } from 'hardhat';
107
+
108
+ describe('MyContract', function () {
109
+ it('should deploy and set initial value', async function () {
110
+ const Contract = await ethers.getContractFactory('MyContract');
111
+ const contract = await Contract.deploy(42);
112
+ await contract.waitForDeployment();
113
+
114
+ expect(await contract.getValue()).to.equal(42);
115
+ });
116
+
117
+ it('should update value', async function () {
118
+ const Contract = await ethers.getContractFactory('MyContract');
119
+ const contract = await Contract.deploy(0);
120
+ await contract.waitForDeployment();
121
+
122
+ const tx = await contract.setValue(100);
123
+ await tx.wait();
124
+
125
+ expect(await contract.getValue()).to.equal(100);
126
+ });
127
+ });
128
+ ```
129
+
130
+ ```bash
131
+ npx hardhat test
132
+ ```
133
+
134
+ ### Foundry Tests
135
+
136
+ ```solidity
137
+ // test/MyContract.t.sol
138
+ // SPDX-License-Identifier: MIT
139
+ pragma solidity ^0.8.24;
140
+
141
+ import "forge-std/Test.sol";
142
+ import "../src/MyContract.sol";
143
+
144
+ contract MyContractTest is Test {
145
+ MyContract public myContract;
146
+
147
+ function setUp() public {
148
+ myContract = new MyContract(42);
149
+ }
150
+
151
+ function testGetValue() public view {
152
+ assertEq(myContract.getValue(), 42);
153
+ }
154
+
155
+ function testSetValue() public {
156
+ myContract.setValue(100);
157
+ assertEq(myContract.getValue(), 100);
158
+ }
159
+ }
160
+ ```
161
+
162
+ ```bash
163
+ forge test
164
+ ```
165
+
166
+ ## Testnet Integration Testing
167
+
168
+ ### Test Workflow
169
+
170
+ 1. Use testnet endpoints (never mainnet for tests)
171
+ 2. Fund test wallet from faucet
172
+ 3. Run operations against real infrastructure
173
+ 4. Verify results on block explorer
174
+
175
+ ### Integration Test Example
176
+
177
+ ```typescript
178
+ import { describe, it, expect } from 'vitest';
179
+ import { ethers } from 'ethers';
180
+ import { ZgFile, Indexer } from '@0glabs/0g-ts-sdk';
181
+
182
+ // Only run in integration test mode
183
+ describe.skipIf(!process.env.RUN_INTEGRATION)('Storage Integration', () => {
184
+ const provider = new ethers.JsonRpcProvider('https://evmrpc-testnet.0g.ai');
185
+ const wallet = new ethers.Wallet(process.env.TEST_PRIVATE_KEY!, provider);
186
+ const indexer = new Indexer('https://indexer-storage-testnet-turbo.0g.ai');
187
+
188
+ it('should upload and download a file', async () => {
189
+ // Upload
190
+ const file = await ZgFile.fromFilePath('./test-fixtures/sample.txt');
191
+ try {
192
+ const [tree, err] = await file.merkleTree();
193
+ if (err) throw err;
194
+ const rootHash = tree!.rootHash();
195
+ const [, uploadErr] = await indexer.upload(file, process.env.RPC_URL!, wallet);
196
+ if (uploadErr) throw uploadErr;
197
+
198
+ // Download (can throw — use try/catch)
199
+ const outputPath = './test-output/downloaded.txt';
200
+ const dlErr = await indexer.download(rootHash, outputPath, true);
201
+ if (dlErr) throw dlErr;
202
+
203
+ // Verify
204
+ expect(fs.existsSync(outputPath)).toBe(true);
205
+ } finally {
206
+ await file.close();
207
+ }
208
+ }, 120_000); // Long timeout for network operations
209
+ });
210
+ ```
211
+
212
+ ### Running Integration Tests
213
+
214
+ ```bash
215
+ # Run unit tests only
216
+ npm test
217
+
218
+ # Run with integration tests
219
+ RUN_INTEGRATION=1 TEST_PRIVATE_KEY=0x... npm test
220
+ ```
221
+
222
+ ## Project Setup (vitest)
223
+
224
+ ```typescript
225
+ // vitest.config.ts
226
+ import { defineConfig } from 'vitest/config';
227
+
228
+ export default defineConfig({
229
+ test: {
230
+ globals: true,
231
+ environment: 'node',
232
+ testTimeout: 30_000,
233
+ include: ['test/**/*.test.ts'],
234
+ },
235
+ });
236
+ ```
237
+
238
+ ```json
239
+ // package.json scripts
240
+ {
241
+ "scripts": {
242
+ "test": "vitest run",
243
+ "test:watch": "vitest",
244
+ "test:integration": "RUN_INTEGRATION=1 vitest run"
245
+ }
246
+ }
247
+ ```
248
+
249
+ ## Best Practices
250
+
251
+ 1. **Mock external calls** in unit tests — never hit real networks
252
+ 2. **Use testnet** for integration tests — never mainnet
253
+ 3. **Set long timeouts** for network operations (60-120s)
254
+ 4. **Fund test wallets** with small amounts only
255
+ 5. **Clean up** test artifacts (uploaded files, deployed contracts)
256
+ 6. **Use separate keys** for testing and production
257
+
258
+ ## References
259
+
260
+ - [Vitest Documentation](https://vitest.dev)
261
+ - [Hardhat Testing](https://hardhat.org/tutorial/testing-contracts)
262
+ - [Foundry Testing](https://book.getfoundry.sh/forge/tests)
263
+ - See also: [NETWORK_CONFIG.md](./NETWORK_CONFIG.md)