create-0g-app 1.0.0 → 1.0.2
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.
- package/dist/index.js +36 -39
- package/dist/index.js.map +1 -1
- package/dist/scaffold.d.ts +8 -0
- package/dist/scaffold.d.ts.map +1 -0
- package/dist/scaffold.js +229 -0
- package/dist/scaffold.js.map +1 -0
- package/package.json +9 -2
- package/templates/{storage → base}/_gitignore +0 -9
- package/templates/base/packages/web/app/layout.tsx +42 -0
- package/templates/{storage → base}/packages/web/components/Navbar.module.css +2 -34
- package/templates/{storage → base}/packages/web/components/Navbar.tsx +3 -15
- package/templates/{storage → base}/packages/web/lib/wagmi.ts +2 -2
- package/templates/{storage → base}/packages/web/next.config.ts +1 -12
- package/templates/compute/packages/web/app/api/compute/route.ts +91 -0
- package/templates/compute/packages/web/components/ComputeSection.module.css +84 -0
- package/templates/compute/packages/web/components/ComputeSection.tsx +102 -0
- package/templates/compute/packages/web/lib/0g-compute.ts +38 -0
- package/templates/contracts/packages/contracts/contracts/MyContract.sol +21 -0
- package/templates/{storage → contracts}/packages/contracts/hardhat.config.ts +11 -21
- package/templates/contracts/packages/contracts/scripts/deploy.ts +33 -0
- package/templates/storage/packages/web/app/api/download/[rootHash]/route.ts +34 -0
- package/templates/storage/packages/web/app/api/upload/route.ts +51 -0
- package/templates/storage/packages/web/components/StorageSection.module.css +80 -0
- package/templates/storage/packages/web/components/StorageSection.tsx +92 -0
- package/templates/storage/packages/web/lib/0g-storage.ts +16 -37
- package/templates/storage/scripts/patch-0g-sdk.js +139 -0
- package/templates/storage/README.md +0 -106
- package/templates/storage/package-lock.json +0 -15358
- package/templates/storage/package.json +0 -15
- package/templates/storage/packages/contracts/contracts/FileRegistry.sol +0 -49
- package/templates/storage/packages/contracts/scripts/deploy.ts +0 -43
- package/templates/storage/packages/web/.env.example +0 -12
- package/templates/storage/packages/web/app/layout.tsx +0 -25
- package/templates/storage/packages/web/app/page.module.css +0 -74
- package/templates/storage/packages/web/app/page.tsx +0 -29
- package/templates/storage/packages/web/app/storage/page.module.css +0 -110
- package/templates/storage/packages/web/app/storage/page.tsx +0 -167
- package/templates/storage/packages/web/lib/abi.ts +0 -48
- package/templates/storage/packages/web/next-env.d.ts +0 -6
- package/templates/storage/packages/web/package.json +0 -26
- /package/templates/{storage → base}/packages/web/app/globals.css +0 -0
- /package/templates/{storage → base}/packages/web/app/providers.tsx +0 -0
- /package/templates/{storage → base}/packages/web/tsconfig.json +0 -0
- /package/templates/{storage → contracts}/packages/contracts/package.json +0 -0
|
@@ -1,40 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Upload a file to 0G decentralized storage.
|
|
9
|
-
* Returns the content ID (merkle root) that can be used to retrieve the file later.
|
|
10
|
-
*/
|
|
11
|
-
export async function uploadFile(
|
|
12
|
-
file: File,
|
|
13
|
-
signer: ethers.Signer
|
|
14
|
-
): Promise<{ contentId: string }> {
|
|
15
|
-
const zgFile = await ZgFile.fromNodeFileBuffer(
|
|
16
|
-
Buffer.from(await file.arrayBuffer()),
|
|
17
|
-
file.name
|
|
18
|
-
);
|
|
19
|
-
|
|
20
|
-
const [tree, treeErr] = await zgFile.merkleTree();
|
|
21
|
-
if (treeErr || !tree) throw new Error(`Failed to build merkle tree: ${treeErr}`);
|
|
22
|
-
|
|
23
|
-
const contentId = tree.rootHash();
|
|
24
|
-
|
|
25
|
-
const indexer = new Indexer(INDEXER_URL);
|
|
26
|
-
const [, uploadErr] = await indexer.upload(zgFile, 0, signer);
|
|
27
|
-
if (uploadErr) throw new Error(`Upload failed: ${uploadErr}`);
|
|
28
|
-
|
|
29
|
-
return { contentId };
|
|
1
|
+
export async function uploadFile(file: File): Promise<{ rootHash: string; txHash: string }> {
|
|
2
|
+
const form = new FormData();
|
|
3
|
+
form.append("file", file);
|
|
4
|
+
const res = await fetch("/api/upload", { method: "POST", body: form });
|
|
5
|
+
if (!res.ok) throw new Error(await res.text());
|
|
6
|
+
return res.json();
|
|
30
7
|
}
|
|
31
8
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
9
|
+
export async function downloadFile(rootHash: string, filename?: string): Promise<void> {
|
|
10
|
+
const res = await fetch(`/api/download/${rootHash}`);
|
|
11
|
+
if (!res.ok) throw new Error(await res.text());
|
|
12
|
+
const blob = await res.blob();
|
|
13
|
+
const url = URL.createObjectURL(blob);
|
|
14
|
+
const a = document.createElement("a");
|
|
15
|
+
a.href = url;
|
|
16
|
+
a.download = filename ?? rootHash;
|
|
17
|
+
a.click();
|
|
18
|
+
URL.revokeObjectURL(url);
|
|
40
19
|
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Patches @0glabs/0g-ts-sdk to match the current 0G testnet contract ABI.
|
|
4
|
+
*
|
|
5
|
+
* The Submission struct was updated on-chain to add an `address submitter` field,
|
|
6
|
+
* changing the submit() selector from 0xef3e12dc → 0xbc8c11f8.
|
|
7
|
+
* The npm package (v0.3.3) still ships the old ABI, so we patch it here.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
|
|
13
|
+
const SDK = '@0glabs/0g-ts-sdk';
|
|
14
|
+
|
|
15
|
+
function findSdkRoot() {
|
|
16
|
+
const candidates = [
|
|
17
|
+
path.join(__dirname, '..', 'node_modules', SDK),
|
|
18
|
+
path.join(__dirname, '..', 'packages', 'web', 'node_modules', SDK),
|
|
19
|
+
];
|
|
20
|
+
for (const p of candidates) {
|
|
21
|
+
if (fs.existsSync(p)) return p;
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const OLD_SUBMIT_ABI = ` components: [
|
|
27
|
+
{
|
|
28
|
+
internalType: "uint256",
|
|
29
|
+
name: "length",
|
|
30
|
+
type: "uint256",
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
internalType: "bytes",
|
|
34
|
+
name: "tags",
|
|
35
|
+
type: "bytes",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
components: [
|
|
39
|
+
{
|
|
40
|
+
internalType: "bytes32",
|
|
41
|
+
name: "root",
|
|
42
|
+
type: "bytes32",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
internalType: "uint256",
|
|
46
|
+
name: "height",
|
|
47
|
+
type: "uint256",
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
internalType: "struct SubmissionNode[]",
|
|
51
|
+
name: "nodes",
|
|
52
|
+
type: "tuple[]",
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
internalType: "struct Submission",
|
|
56
|
+
name: "submission",
|
|
57
|
+
type: "tuple",`;
|
|
58
|
+
|
|
59
|
+
const NEW_SUBMIT_ABI = ` components: [
|
|
60
|
+
{
|
|
61
|
+
components: [
|
|
62
|
+
{
|
|
63
|
+
internalType: "uint256",
|
|
64
|
+
name: "length",
|
|
65
|
+
type: "uint256",
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
internalType: "bytes",
|
|
69
|
+
name: "tags",
|
|
70
|
+
type: "bytes",
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
components: [
|
|
74
|
+
{
|
|
75
|
+
internalType: "bytes32",
|
|
76
|
+
name: "root",
|
|
77
|
+
type: "bytes32",
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
internalType: "uint256",
|
|
81
|
+
name: "height",
|
|
82
|
+
type: "uint256",
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
internalType: "struct SubmissionNode[]",
|
|
86
|
+
name: "nodes",
|
|
87
|
+
type: "tuple[]",
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
internalType: "struct SubmissionData",
|
|
91
|
+
name: "data",
|
|
92
|
+
type: "tuple",
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
internalType: "address",
|
|
96
|
+
name: "submitter",
|
|
97
|
+
type: "address",
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
internalType: "struct Submission",
|
|
101
|
+
name: "submission",
|
|
102
|
+
type: "tuple",`;
|
|
103
|
+
|
|
104
|
+
const OLD_UPLOADER_LINE = `[submission], txOpts, retryOpts)`;
|
|
105
|
+
const NEW_UPLOADER_LINE = `[{ data: submission, submitter: await this.flow.runner.getAddress() }], txOpts, retryOpts)`;
|
|
106
|
+
|
|
107
|
+
function patchFile(filePath, oldStr, newStr, label) {
|
|
108
|
+
if (!fs.existsSync(filePath)) return;
|
|
109
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
110
|
+
if (!content.includes(oldStr)) {
|
|
111
|
+
console.log(` skip (already patched): ${label}`);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
fs.writeFileSync(filePath, content.replace(oldStr, newStr));
|
|
115
|
+
console.log(` patched: ${label}`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const sdkRoot = findSdkRoot();
|
|
119
|
+
if (!sdkRoot) {
|
|
120
|
+
console.log('patch-0g-sdk: SDK not found, skipping.');
|
|
121
|
+
process.exit(0);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
console.log(`patch-0g-sdk: found SDK at ${sdkRoot}`);
|
|
125
|
+
|
|
126
|
+
for (const variant of ['lib.esm', 'lib.commonjs']) {
|
|
127
|
+
patchFile(
|
|
128
|
+
path.join(sdkRoot, variant, 'contracts', 'flow', 'factories', 'FixedPriceFlow__factory.js'),
|
|
129
|
+
OLD_SUBMIT_ABI, NEW_SUBMIT_ABI,
|
|
130
|
+
`${variant}/FixedPriceFlow__factory.js`
|
|
131
|
+
);
|
|
132
|
+
patchFile(
|
|
133
|
+
path.join(sdkRoot, variant, 'transfer', 'Uploader.js'),
|
|
134
|
+
OLD_UPLOADER_LINE, NEW_UPLOADER_LINE,
|
|
135
|
+
`${variant}/Uploader.js`
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
console.log('patch-0g-sdk: done.');
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
# 0G Storage App
|
|
2
|
-
|
|
3
|
-
A decentralized file registry built on the [0G network](https://0g.ai).
|
|
4
|
-
|
|
5
|
-
- **Files** are stored on 0G's decentralized storage network via `@0glabs/0g-ts-sdk`
|
|
6
|
-
- **Provenance** (who uploaded what, and when) is recorded on-chain via `FileRegistry.sol`
|
|
7
|
-
- **Frontend** is a Next.js 15 app with wagmi wallet connect
|
|
8
|
-
|
|
9
|
-
## Quick Start
|
|
10
|
-
|
|
11
|
-
### 1. Install dependencies
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
npm install
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
### 2. Configure environment
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
cp packages/web/.env.example packages/web/.env.local
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
Edit `packages/web/.env.local` and add your private key:
|
|
24
|
-
|
|
25
|
-
```env
|
|
26
|
-
PRIVATE_KEY=0xYOUR_PRIVATE_KEY_HERE
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
> Get testnet OG tokens from the [0G faucet](https://faucet.0g.ai) if your wallet is empty.
|
|
30
|
-
|
|
31
|
-
### 3. Start the dev server (no deploy needed)
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
npm run dev
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
Open [http://localhost:3000](http://localhost:3000). You can browse the UI, but uploads need the contract deployed first.
|
|
38
|
-
|
|
39
|
-
### 4. Deploy the contract to Galileo testnet
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
|
-
npm run deploy
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
This will:
|
|
46
|
-
1. Compile `FileRegistry.sol`
|
|
47
|
-
2. Deploy it to the 0G Galileo testnet (chain ID 16601)
|
|
48
|
-
3. Automatically write the contract address to `packages/web/.env.local`
|
|
49
|
-
|
|
50
|
-
Restart the dev server after deploying to pick up the new address.
|
|
51
|
-
|
|
52
|
-
### 5. Verify the contract (optional)
|
|
53
|
-
|
|
54
|
-
```bash
|
|
55
|
-
npm run verify
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
View your contract on the [0G Galileo explorer](https://chainscan-galileo.0g.ai).
|
|
59
|
-
|
|
60
|
-
## Project Structure
|
|
61
|
-
|
|
62
|
-
```
|
|
63
|
-
├── packages/
|
|
64
|
-
│ ├── contracts/ Solidity + Hardhat
|
|
65
|
-
│ │ ├── contracts/
|
|
66
|
-
│ │ │ └── FileRegistry.sol
|
|
67
|
-
│ │ ├── scripts/
|
|
68
|
-
│ │ │ └── deploy.ts
|
|
69
|
-
│ │ └── hardhat.config.ts
|
|
70
|
-
│ └── web/ Next.js frontend
|
|
71
|
-
│ ├── app/
|
|
72
|
-
│ ├── lib/
|
|
73
|
-
│ │ ├── 0g-storage.ts 0G SDK wrapper
|
|
74
|
-
│ │ ├── abi.ts FileRegistry ABI
|
|
75
|
-
│ │ └── wagmi.ts Wallet config
|
|
76
|
-
│ └── .env.example
|
|
77
|
-
└── README.md
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
## How It Works
|
|
81
|
-
|
|
82
|
-
1. User connects their wallet (MetaMask or any injected wallet)
|
|
83
|
-
2. User drops a file onto the upload zone
|
|
84
|
-
3. The file is uploaded to 0G storage via the indexer — a content ID (merkle root) is returned
|
|
85
|
-
4. The content ID is written on-chain to `FileRegistry.sol` via `registerFile()`
|
|
86
|
-
5. The file gallery reads all content IDs from the contract for the connected wallet
|
|
87
|
-
6. Clicking "Download" fetches the file from 0G storage by content ID
|
|
88
|
-
|
|
89
|
-
## Troubleshooting
|
|
90
|
-
|
|
91
|
-
**"No contract deployed yet" warning**
|
|
92
|
-
Run `npm run deploy` and restart the dev server.
|
|
93
|
-
|
|
94
|
-
**Transaction fails / insufficient funds**
|
|
95
|
-
Get testnet OG tokens from [https://faucet.0g.ai](https://faucet.0g.ai).
|
|
96
|
-
|
|
97
|
-
**Upload hangs**
|
|
98
|
-
The 0G storage indexer may be temporarily unavailable. Check [https://docs.0g.ai](https://docs.0g.ai) for status.
|
|
99
|
-
|
|
100
|
-
**Wrong network in MetaMask**
|
|
101
|
-
Add the 0G Galileo testnet manually:
|
|
102
|
-
- Network name: 0G Galileo Testnet
|
|
103
|
-
- RPC URL: https://evmrpc-test.0g.ai
|
|
104
|
-
- Chain ID: 16601
|
|
105
|
-
- Currency: OG
|
|
106
|
-
- Explorer: https://chainscan-galileo.0g.ai
|