fangorn-sdk 0.0.4 → 2026.2.24
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/README.md +142 -99
- package/lib/cli.js +123 -20
- package/lib/config.d.ts +0 -1
- package/lib/config.js +2 -4
- package/lib/fangorn.d.ts +2 -1
- package/lib/fangorn.js +5 -2
- package/lib/interface/datasource-registry/abi.d.ts +3 -0
- package/lib/interface/datasource-registry/abi.js +3 -0
- package/lib/interface/datasource-registry/dataSourceRegistry.d.ts +1 -1
- package/lib/interface/datasource-registry/dataSourceRegistry.js +2 -2
- package/lib/modules/encryption/lit.d.ts +15 -2
- package/lib/modules/encryption/lit.js +28 -7
- package/lib/test/testbed.d.ts +2 -1
- package/lib/test/testbed.js +8 -11
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -1,58 +1,55 @@
|
|
|
1
1
|
# Fangorn
|
|
2
2
|
|
|
3
|
+
Programmable data.
|
|
4
|
+
|
|
3
5
|
## Overview
|
|
4
6
|
|
|
5
|
-
Fangorn lets you publish encrypted data under public conditions, or predicates, such that it can only be decrypted if you meet the condition
|
|
7
|
+
Fangorn is a programmable data framework that lets you **register datasources** and **publish encrypted data** under public conditions, or predicates, such that it can only be decrypted if you meet the condition(s).
|
|
8
|
+
|
|
9
|
+
## Supported Networks
|
|
10
|
+
|
|
11
|
+
Fangorn can be deployed on any EVM chain that has a brige to Lit protocol. Currently, contracts are deployed to both Arbitrum Sepolia and Base Sepolia. See the [contracts](#contracts) section for more info.
|
|
6
12
|
|
|
7
13
|
## Build
|
|
8
14
|
|
|
9
|
-
|
|
15
|
+
`pnpm i
|
|
10
16
|
|
|
11
17
|
### Usage
|
|
12
18
|
|
|
13
19
|
Fangorn is a programmable data framework. It provides tools to register data sources that can be accessed based on owner-defined conditions, like payment.
|
|
14
20
|
|
|
15
|
-
## Supported Networks
|
|
16
|
-
|
|
17
|
-
- arbitrum sepolia
|
|
18
|
-
- base sepolia
|
|
19
|
-
|
|
20
|
-
### Encryption
|
|
21
|
-
|
|
22
|
-
The library is modular and can support various key management systems. We recommend, and use by default, Lit protocol as the main KMS, or in this case, DKMS.
|
|
23
|
-
|
|
24
|
-
Each datasource points to the content identifier (CID) of a "manifest" stored in IPFS. Each manifest stores a complete record of (encrypted) content, descriptions, and prices inserted by the data owner.
|
|
25
|
-
|
|
26
21
|
#### Quickstart
|
|
27
22
|
|
|
28
|
-
Coming soon ;)
|
|
29
|
-
|
|
30
23
|
```js
|
|
31
|
-
#
|
|
24
|
+
# Coming soon
|
|
32
25
|
```
|
|
33
26
|
|
|
34
27
|
#### Full Guide
|
|
35
28
|
|
|
29
|
+
#### Setup
|
|
30
|
+
|
|
36
31
|
```js
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
// setup the Lit client
|
|
49
|
-
// client to interact with LIT proto
|
|
32
|
+
// provide the account, rpcurl, and chain externally
|
|
33
|
+
// Initalize a wallet client
|
|
34
|
+
const walletClient = createWalletClient({
|
|
35
|
+
account,,
|
|
36
|
+
transport: http(rpcUrl),
|
|
37
|
+
chain,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// For ArbSep, also supports BaseSepolia (wallet client must match)
|
|
41
|
+
const config: AppConfig = FangornConfig.ArbitrumSepolia;
|
|
42
|
+
|
|
43
|
+
// setup the Lit client (for encryption)
|
|
50
44
|
const litClient = await createLitClient({
|
|
51
45
|
network: nagaDev,
|
|
52
46
|
});
|
|
47
|
+
// and the encryption service
|
|
48
|
+
const encryptionService = new LitEncryptionService(delegateeLitClient, {
|
|
49
|
+
chainName: chain,
|
|
50
|
+
});
|
|
53
51
|
|
|
54
|
-
// setup the storage client
|
|
55
|
-
// storage via Pinata
|
|
52
|
+
// setup the storage client
|
|
56
53
|
const pinata = new PinataSDK({
|
|
57
54
|
pinataJwt: jwt,
|
|
58
55
|
pinataGateway: gateway,
|
|
@@ -61,85 +58,94 @@ const pinata = new PinataSDK({
|
|
|
61
58
|
const storage = new PinataStorage(pinata);
|
|
62
59
|
|
|
63
60
|
// the domain/webserver where Fangorn is used
|
|
64
|
-
const domain = "localhost
|
|
61
|
+
const domain = "localhost";
|
|
65
62
|
|
|
66
63
|
const fangorn = await Fangorn.init(
|
|
67
|
-
|
|
64
|
+
walletClient,
|
|
68
65
|
storage,
|
|
69
|
-
|
|
66
|
+
encryptionService,
|
|
70
67
|
domain,
|
|
71
68
|
config,
|
|
72
69
|
);
|
|
70
|
+
```
|
|
73
71
|
|
|
74
|
-
|
|
75
|
-
const vaultName = "myvault-001";
|
|
76
|
-
const vaultId = await fangorn.registerDataSource(vaultName);
|
|
72
|
+
##### Datasource Registration
|
|
77
73
|
|
|
78
|
-
|
|
74
|
+
Now that you have a Fangorn client, you can create a _datasource_. A datasource is an on-chain asset that stores a commitment to its storage root along with an optional `agentId` field for associating the datasource with an ERC-8004 identity.
|
|
75
|
+
|
|
76
|
+
```js
|
|
77
|
+
const name = "demo";
|
|
78
|
+
// id = sha256(name || owner), agentId = ""
|
|
79
|
+
const id = await this.delegatorFangorn.registerDataSource(name, "");
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
##### Encryption
|
|
83
|
+
|
|
84
|
+
Once a datasource exists, the owner can update its storage root to point it to data. Fangorn leverages Lit protocol for encryption and access control.
|
|
85
|
+
|
|
86
|
+
Encryption works by specifying a [gadget](./src/modules/gadgets/README.md), code that represents a logical statement that you want to encrypt under. The gadgets framework is extensible and customizable, allowing for easy custom implementations. For now, we have three gadgets:
|
|
87
|
+
|
|
88
|
+
- empty wallet: must have an empty wallet to decrypt
|
|
89
|
+
- identity: must have a specific identity to decrypt
|
|
90
|
+
- payment: must submit a specific payment to decrypt
|
|
91
|
+
|
|
92
|
+
```js
|
|
93
|
+
// configure data using a json array, note that all data in this array will be encrypted under the same condition
|
|
94
|
+
// each entry has as (tag, data, extension, fileTpe)
|
|
79
95
|
let filedata = [
|
|
80
96
|
{
|
|
81
97
|
tag: "test0",
|
|
82
98
|
data: "content0",
|
|
83
99
|
extension: ".txt",
|
|
84
100
|
fileType: "text/plain",
|
|
85
|
-
price: "0.0001",
|
|
86
101
|
},
|
|
87
102
|
{
|
|
88
103
|
tag: "test1",
|
|
89
104
|
data: "content1",
|
|
90
105
|
extension: ".png",
|
|
91
106
|
fileType: "image/png",
|
|
92
|
-
price: "0.15",
|
|
93
107
|
},
|
|
94
108
|
];
|
|
95
|
-
await fangorn.upload(vaultId, filedata);
|
|
96
109
|
|
|
97
|
-
//
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
tag: "test3",
|
|
113
|
-
data: "content3",
|
|
114
|
-
extension: ".js",
|
|
115
|
-
fileType: "text/javascript",
|
|
116
|
-
},
|
|
117
|
-
];
|
|
118
|
-
await fangorn.upload(vaultId, filedata, true);
|
|
110
|
+
// this encrypts the file under a USDC payment condition, useful for x402
|
|
111
|
+
await fangorn.upload(datasourceName, filedata, async (file) => {
|
|
112
|
+
const commitment = await computeTagCommitment(
|
|
113
|
+
this.delegatorAddress,
|
|
114
|
+
datasourceName,
|
|
115
|
+
file.tag,
|
|
116
|
+
usdcPrice,
|
|
117
|
+
);
|
|
118
|
+
return new PaymentGadget({
|
|
119
|
+
commitment: fieldToHex(commitment),
|
|
120
|
+
chainName: this.config.chainName,
|
|
121
|
+
settlementTrackerContractAddress,
|
|
122
|
+
usdcPrice,
|
|
123
|
+
});
|
|
124
|
+
});
|
|
119
125
|
```
|
|
120
126
|
|
|
121
127
|
### Decryption
|
|
122
128
|
|
|
123
|
-
Decryption
|
|
129
|
+
Decryption mandates that the caller has met the condition specified by the ciphertext. If unknown, the condition can be decoded by fetching the entry from storage (pinata) in which we store a `gadgetDescriptor`, providing pertinent information about the gadget used to encrypt the plaintext and how to satisfy it.
|
|
124
130
|
|
|
125
131
|
```js
|
|
126
|
-
//
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
const plaintext = await fangorn.decryptFile(
|
|
134
|
-
|
|
135
|
-
console.log("we got the plaintext " +
|
|
132
|
+
// the address of the owner of the datasource
|
|
133
|
+
const owner = "0xabc123...";
|
|
134
|
+
// the name of the datasource
|
|
135
|
+
const name = "demo";
|
|
136
|
+
// the tag of the data we want to fetch
|
|
137
|
+
const tag = "test0";
|
|
138
|
+
|
|
139
|
+
const plaintext = await fangorn.decryptFile(owner, name, tag);
|
|
140
|
+
const outputAsString = new TextDecoder().decode(output);
|
|
141
|
+
console.log("we got the plaintext " + outputAsString);
|
|
136
142
|
```
|
|
137
143
|
|
|
138
144
|
## Testing
|
|
139
145
|
|
|
140
146
|
### Unit Tests
|
|
141
147
|
|
|
142
|
-
Run the tests with
|
|
148
|
+
Run the tests with:
|
|
143
149
|
|
|
144
150
|
```sh
|
|
145
151
|
pnpm test
|
|
@@ -149,7 +155,7 @@ pnpm test
|
|
|
149
155
|
|
|
150
156
|
#### Setup
|
|
151
157
|
|
|
152
|
-
The e2e test suite requires various environment variables that must be manually configured.
|
|
158
|
+
The e2e test suite requires various environment variables that must be manually configured. In addition, it must be executed on an actual testnet in order to establish comms with Lit protocol.
|
|
153
159
|
|
|
154
160
|
Testnet tokens (ETH on Base Sepolia) can be obtained from Coinbase's official faucet https://portal.cdp.coinbase.com/
|
|
155
161
|
|
|
@@ -159,19 +165,46 @@ Testnet tokens (ETH on Base Sepolia) can be obtained from Coinbase's official fa
|
|
|
159
165
|
- Needs to have a balance of test ETH to send transactions
|
|
160
166
|
- `DELEGATEE_ETH_PRIVATE_KEY`: The private key of the delegatee account
|
|
161
167
|
- Needs to have a balance of test ETH to send transactions
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
168
|
+
|
|
169
|
+
- `PINATA_JWT`: The JWT for Pinata
|
|
170
|
+
- Can be obtained from: https://app.pinata.cloud/developers/api-keys
|
|
171
|
+
- `PINATA_GATEWAY`: The gateway for Pinata
|
|
172
|
+
- Can use your own gateway or the default 'https://gateway.pinata.cloud'
|
|
173
|
+
- `CHAIN_NAME`: arbitrumSepolia or baseSepolia
|
|
174
|
+
- `CAIP2`: The CAIP-2 identifier for the network.
|
|
175
|
+
- 421614 for Arbitrum Sepolia, 84532 for Base Sepolia
|
|
176
|
+
- `CHAIN_RPC_URL`: The RPC URL of the chain
|
|
177
|
+
- Expected to be Base sepolia testnet: https://base-sepolia-public.nodies.app or Arbitrum Sepolia: https://sepolia-rollup.arbitrum.io/rpc
|
|
178
|
+
- `USDC_CONTRACT_ADDRESS`: The address of the deployed USDC contract
|
|
179
|
+
- 0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d for Arbitrum Sepolia 0x036CbD53842c5426634e7929541eC2318f3dCF7e for Base Sepolia
|
|
180
|
+
- `DS_REGISTRY_ADDR`: The address of the deployed data registry contract
|
|
181
|
+
- Can be deployed by the test script, else use 0x5bd547ce3b5964c2fc0325f523679f66de391d6f for Arbitrum Sepolia and 0x6fd0e50073dbd8169bcaf066bb4a4991bfa48eeb on Base Sepolia
|
|
182
|
+
- `SETTLEMENT_TRACKER_ADDR`: The address of the deployed settlement tracker contract address. This is only needed if you plan to run tests using the payment gadget.
|
|
183
|
+
- Can be deployed by the test script, else use 0x7c6ae9eb3398234eb69b2f3acfae69065505ff69 for Arbitrum Sepolia
|
|
184
|
+
|
|
185
|
+
Sample:
|
|
186
|
+
|
|
187
|
+
For Arbitrum Sepolia
|
|
188
|
+
|
|
189
|
+
```sh
|
|
190
|
+
CHAIN_NAME=arbitrumSepolia
|
|
191
|
+
CAIP2=421614
|
|
192
|
+
CHAIN_RPC_URL=https://sepolia-rollup.arbitrum.io/rpc
|
|
193
|
+
USDC_CONTRACT_ADDRESS=0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d
|
|
194
|
+
DS_REGISTRY_ADDR=0x602aedafe1096004d4db591b6537bc39d7ac71a6
|
|
195
|
+
SETTLEMENT_TRACKER_ADDR=0x7c6ae9eb3398234eb69b2f3acfae69065505ff69
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
For Base Sepolia
|
|
199
|
+
|
|
200
|
+
```sh
|
|
201
|
+
CHAIN_NAME=baseSepolia
|
|
202
|
+
CAIP2=84532
|
|
203
|
+
CHAIN_RPC_URL=https://base-sepolia-public.nodies.app
|
|
204
|
+
USDC_CONTRACT_ADDRESS=0x036CbD53842c5426634e7929541eC2318f3dCF7e
|
|
205
|
+
DS_REGISTRY_ADDR=0x6fd0e50073dbd8169bcaf066bb4a4991bfa48eeb
|
|
206
|
+
SETTLEMENT_TRACKER_ADDR=0x708751829f5f5f584da4142b62cd5cc9235c8a18
|
|
207
|
+
```
|
|
175
208
|
|
|
176
209
|
### Running the tests
|
|
177
210
|
|
|
@@ -182,6 +215,15 @@ The tests will:
|
|
|
182
215
|
1. Build and deploy the solidity verifier to base sepolia (unless it is defined in .env)
|
|
183
216
|
2. Upload the Lit Action to IPFS (unless it is defined in .env)
|
|
184
217
|
|
|
218
|
+
## Contracts
|
|
219
|
+
|
|
220
|
+
| | Arbitrum Sepolia | Base Sepolia |
|
|
221
|
+
| -------------------------------------- | ------------------------------------------------------------ | -------------------------------------------------------------- |
|
|
222
|
+
| Datsource Registry Contract Deployment | 0x602aedafe1096004d4db591b6537bc39d7ac71a6 | 0x6fd0e50073dbd8169bcaf066bb4a4991bfa48eeb |
|
|
223
|
+
| Datsource Registry Contract Code | [lib.rs](./contracts/arbitrum/DatasourceRegistry/src/lib.rs) | [DSRegistry.sol](./contracts/src/DSRegistry.sol) |
|
|
224
|
+
| Settlement Tracker Contract Deployment | 0x7c6ae9eb3398234eb69b2f3acfae69065505ff69 | 0x708751829f5f5f584da4142b62cd5cc9235c8a18 |
|
|
225
|
+
| Settlement Tracker Contract Code | [lib.rs](./contracts/arbitrum/SettlementTracker//src/lib.rs) | [SettlementTracker.sol](./contracts/src/SettlementTracker.sol) |
|
|
226
|
+
|
|
185
227
|
## CLI
|
|
186
228
|
|
|
187
229
|
To install locally:
|
|
@@ -191,21 +233,22 @@ chmod +x update_clci.sh
|
|
|
191
233
|
./update_cli.sh
|
|
192
234
|
```
|
|
193
235
|
|
|
194
|
-
```
|
|
236
|
+
```sh
|
|
195
237
|
Usage: Fangorn [options] [command]
|
|
196
238
|
|
|
197
239
|
CLI for Fangorn
|
|
198
240
|
|
|
199
241
|
Options:
|
|
200
|
-
-V, --version
|
|
201
|
-
-h, --help
|
|
242
|
+
-V, --version output the version number
|
|
243
|
+
-h, --help display help for command
|
|
202
244
|
|
|
203
245
|
Commands:
|
|
204
|
-
register [options] <name>
|
|
205
|
-
upload [options] <name> <files...>
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
decrypt [options] <name> <tag> Decrypt a file from a vault
|
|
209
|
-
entry [options] <name> <tag> Get info about a specific vault entry
|
|
210
|
-
help [command] display help for command
|
|
246
|
+
register [options] <name> Register a new datasource as an agent.
|
|
247
|
+
upload [options] <name> <files...> Upload file(s) to a data source
|
|
248
|
+
decrypt [options] <owner> <name> <tag> Decrypt a file from a vault
|
|
249
|
+
help [command] display help for command
|
|
211
250
|
```
|
|
251
|
+
|
|
252
|
+
## License
|
|
253
|
+
|
|
254
|
+
MIT
|
package/lib/cli.js
CHANGED
|
@@ -10,34 +10,49 @@ import { Command } from "commander";
|
|
|
10
10
|
import { confirm, intro, isCancel, multiselect, note, outro, select, spinner, text } from "@clack/prompts";
|
|
11
11
|
import { createWalletClient, http } from "viem";
|
|
12
12
|
import { privateKeyToAccount } from "viem/accounts";
|
|
13
|
-
import { createLitClient } from "@lit-protocol/lit-client";
|
|
14
|
-
import { nagaDev } from "@lit-protocol/networks";
|
|
15
|
-
import { readFileSync, writeFileSync } from "fs";
|
|
16
|
-
import { basename, extname } from "path";
|
|
17
13
|
import "dotenv/config";
|
|
18
14
|
import { PinataSDK } from "pinata";
|
|
19
15
|
import { SDK } from "agent0-sdk";
|
|
16
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
17
|
+
import { basename, extname, join } from "path";
|
|
18
|
+
import { homedir } from "os";
|
|
20
19
|
|
|
21
20
|
//#region src/cli.ts
|
|
21
|
+
const CONFIG_DIR = join(homedir(), ".fangorn");
|
|
22
|
+
const CONFIG_PATH = join(CONFIG_DIR, "config.json");
|
|
22
23
|
let _config = null;
|
|
23
24
|
let _account = null;
|
|
24
25
|
let _fangorn = null;
|
|
25
26
|
function loadConfig() {
|
|
26
27
|
if (_config) return _config;
|
|
28
|
+
const privateKey = process.env.DELEGATOR_ETH_PRIVATE_KEY;
|
|
27
29
|
const jwt = process.env.PINATA_JWT;
|
|
28
30
|
const gateway = process.env.PINATA_GATEWAY;
|
|
29
|
-
const privateKey = process.env.DELEGATOR_ETH_PRIVATE_KEY;
|
|
30
31
|
const chainName = process.env.CHAIN_NAME;
|
|
31
|
-
if (
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
if (privateKey && jwt && gateway && chainName) {
|
|
33
|
+
_config = buildConfig({
|
|
34
|
+
privateKey,
|
|
35
|
+
jwt,
|
|
36
|
+
gateway,
|
|
37
|
+
chainName
|
|
38
|
+
});
|
|
39
|
+
return _config;
|
|
40
|
+
}
|
|
41
|
+
if (existsSync(CONFIG_PATH)) {
|
|
42
|
+
_config = buildConfig(JSON.parse(readFileSync(CONFIG_PATH, "utf-8")));
|
|
43
|
+
return _config;
|
|
44
|
+
}
|
|
45
|
+
throw new Error("No configuration found. Run `fangorn init` to set up your credentials, or set DELEGATOR_ETH_PRIVATE_KEY, PINATA_JWT, PINATA_GATEWAY, and CHAIN_NAME env vars.");
|
|
46
|
+
}
|
|
47
|
+
function buildConfig({ privateKey, jwt, gateway, chainName }) {
|
|
48
|
+
let cfg = FangornConfig.BaseSepolia;
|
|
49
|
+
if (chainName === SupportedNetworks.ArbitrumSepolia.name) cfg = FangornConfig.ArbitrumSepolia;
|
|
50
|
+
return {
|
|
51
|
+
privateKey,
|
|
35
52
|
jwt,
|
|
36
53
|
gateway,
|
|
37
|
-
|
|
38
|
-
cfg: config
|
|
54
|
+
cfg
|
|
39
55
|
};
|
|
40
|
-
return _config;
|
|
41
56
|
}
|
|
42
57
|
function getAccount() {
|
|
43
58
|
if (_account) return _account;
|
|
@@ -52,7 +67,6 @@ async function getFangorn(chain) {
|
|
|
52
67
|
transport: http(cfg.cfg.rpcUrl),
|
|
53
68
|
chain
|
|
54
69
|
});
|
|
55
|
-
const litClient = await createLitClient({ network: nagaDev });
|
|
56
70
|
const appConfig = cfg.cfg;
|
|
57
71
|
const domain = process.env.DOMAIN || "localhost:3000";
|
|
58
72
|
const storage = new PinataStorage(new PinataSDK({
|
|
@@ -60,7 +74,7 @@ async function getFangorn(chain) {
|
|
|
60
74
|
pinataGateway: cfg.gateway
|
|
61
75
|
}));
|
|
62
76
|
const chainName = process.env.CHAIN_NAME;
|
|
63
|
-
const encryptionService =
|
|
77
|
+
const encryptionService = await LitEncryptionService.init(chainName);
|
|
64
78
|
_fangorn = await Fangorn.init(walletClient, storage, encryptionService, domain, appConfig);
|
|
65
79
|
return _fangorn;
|
|
66
80
|
}
|
|
@@ -85,7 +99,54 @@ const selectChain = async () => {
|
|
|
85
99
|
return getNetwork(chainChoice.toString());
|
|
86
100
|
};
|
|
87
101
|
const program = new Command();
|
|
88
|
-
program.name("Fangorn").description("CLI for Fangorn").version("
|
|
102
|
+
program.name("Fangorn").description("CLI for Fangorn").version("0.0.1");
|
|
103
|
+
program.command("init").description("Configure your Fangorn credentials").action(async () => {
|
|
104
|
+
intro("Fangorn Setup");
|
|
105
|
+
const privateKey = await text({
|
|
106
|
+
message: "Your wallet private key (stored locally, never transmitted):",
|
|
107
|
+
placeholder: "0x...",
|
|
108
|
+
validate: (v) => {
|
|
109
|
+
if (!v.startsWith("0x") || v.length !== 66) return "Must be a valid 0x-prefixed 32-byte hex key";
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
handleCancel(privateKey);
|
|
113
|
+
const jwt = await text({
|
|
114
|
+
message: "Pinata JWT:",
|
|
115
|
+
validate: (v) => {
|
|
116
|
+
if (!v) return "Required";
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
handleCancel(jwt);
|
|
120
|
+
const gateway = await text({
|
|
121
|
+
message: "Pinata Gateway URL:",
|
|
122
|
+
placeholder: "https://your-gateway.mypinata.cloud",
|
|
123
|
+
validate: (v) => {
|
|
124
|
+
if (!v) return "Required";
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
handleCancel(gateway);
|
|
128
|
+
const chainName = await select({
|
|
129
|
+
message: "Default chain:",
|
|
130
|
+
options: [{
|
|
131
|
+
value: SupportedNetworks.ArbitrumSepolia.name,
|
|
132
|
+
label: "Arbitrum Sepolia"
|
|
133
|
+
}, {
|
|
134
|
+
value: SupportedNetworks.BaseSepolia.name,
|
|
135
|
+
label: "Base Sepolia"
|
|
136
|
+
}]
|
|
137
|
+
});
|
|
138
|
+
handleCancel(chainName);
|
|
139
|
+
const stored = {
|
|
140
|
+
privateKey,
|
|
141
|
+
jwt,
|
|
142
|
+
gateway,
|
|
143
|
+
chainName
|
|
144
|
+
};
|
|
145
|
+
if (!existsSync(CONFIG_DIR)) mkdirSync(CONFIG_DIR, { recursive: true });
|
|
146
|
+
writeFileSync(CONFIG_PATH, JSON.stringify(stored, null, 2), "utf-8");
|
|
147
|
+
chmodSync(CONFIG_PATH, 384);
|
|
148
|
+
outro(`Config saved to ${CONFIG_PATH}`);
|
|
149
|
+
});
|
|
89
150
|
program.command("register").description("Register a new datasource as an agent.").argument("<name>", "Name of the datasource").option("-s, --skip-card", "Skip agent card creation").option("-e, --skip-erc", "Skip ERC-8007 registrion").option("-d, --skip-ds", "Skip datasource registrion").action(async (name, options) => {
|
|
90
151
|
try {
|
|
91
152
|
intro("Chain selection");
|
|
@@ -326,7 +387,7 @@ program.command("register").description("Register a new datasource as an agent."
|
|
|
326
387
|
if (agentsList.length > 0) datasourceAgentId = agentsList[0].agentId;
|
|
327
388
|
else throw new Error(`Agent with name ${name} was not found on the ERC-8004 on ${chain.name}`);
|
|
328
389
|
}
|
|
329
|
-
note(`Data source ${name} registered with id = ${await (await getFangorn(chain)).registerDataSource(datasourceAgentId)}`);
|
|
390
|
+
note(`Data source ${name} registered with id = ${await (await getFangorn(chain)).registerDataSource(name, datasourceAgentId)}`);
|
|
330
391
|
}
|
|
331
392
|
process.exit(0);
|
|
332
393
|
} catch (err) {
|
|
@@ -351,12 +412,10 @@ program.command("upload").description("Upload file(s) to a data source").argumen
|
|
|
351
412
|
};
|
|
352
413
|
});
|
|
353
414
|
const cid = await fangorn.upload(name, filedata, async (file) => {
|
|
354
|
-
const commitment = await computeTagCommitment(owner, name, file.tag, options.price);
|
|
355
|
-
console.log("using commitment " + fieldToHex(commitment));
|
|
356
415
|
return new PaymentGadget({
|
|
357
|
-
commitment: fieldToHex(
|
|
416
|
+
commitment: fieldToHex(await computeTagCommitment(owner, name, file.tag, options.price)),
|
|
358
417
|
chainName: "arbitrumSepolia",
|
|
359
|
-
settlementTrackerContractAddress: "
|
|
418
|
+
settlementTrackerContractAddress: "0x7c6ae9eb3398234eb69b2f3acfae69065505ff69",
|
|
360
419
|
usdcPrice: options.price
|
|
361
420
|
});
|
|
362
421
|
}, options.overwrite);
|
|
@@ -367,6 +426,50 @@ program.command("upload").description("Upload file(s) to a data source").argumen
|
|
|
367
426
|
process.exit(1);
|
|
368
427
|
}
|
|
369
428
|
});
|
|
429
|
+
program.command("list").description("List contents (index) of a data source").argument("<name>", "Data source name").option("-c, --chain <chain>", "The chain to use as the backend (arbitrumSepolia or baseSepolia)").action(async (name, options) => {
|
|
430
|
+
try {
|
|
431
|
+
const owner = getAccount().address;
|
|
432
|
+
const manifest = await (await getFangorn(getChain(options.chain))).getManifest(owner, name);
|
|
433
|
+
if (!manifest) {
|
|
434
|
+
console.log("The data source is empty. \n");
|
|
435
|
+
console.log("Upload data with `fangorn upload <dataSourceName> <file> --price <set-price>");
|
|
436
|
+
process.exit(0);
|
|
437
|
+
}
|
|
438
|
+
console.log(`Datasource: ${name} (${owner})`);
|
|
439
|
+
console.log(`Entries (${manifest.entries.length}):`);
|
|
440
|
+
for (const entry of manifest.entries) console.log(` - ${entry.tag} | gadget descriptor: ${JSON.stringify(entry.gadgetDescriptor)} | cid: ${entry.cid}`);
|
|
441
|
+
process.exit(0);
|
|
442
|
+
} catch (err) {
|
|
443
|
+
console.error("Failed to list vault:", err.message);
|
|
444
|
+
process.exit(1);
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
program.command("info").description("Get data source info from contract").argument("<name>", "Data source name").option("-c, --chain <chain>", "The chain to use as the backend (arbitrumSepolia or baseSepolia)").action(async (name, options) => {
|
|
448
|
+
try {
|
|
449
|
+
const owner = getAccount().address;
|
|
450
|
+
const vault = await (await getFangorn(getChain(options.chain))).getDataSource(owner, name);
|
|
451
|
+
console.log(`Datasource: ${name} (${owner})`);
|
|
452
|
+
console.log(`Owner: ${vault.owner}`);
|
|
453
|
+
console.log(`Manifest CID: ${vault.manifestCid == "" ? "Upload data with `fangorn upload <vaultName> <file> --price <set-price>`" : vault.manifestCid}`);
|
|
454
|
+
process.exit(0);
|
|
455
|
+
} catch (err) {
|
|
456
|
+
console.error("Failed to get vault info:", err.message);
|
|
457
|
+
process.exit(1);
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
program.command("entry").description("Get info about a specific entry").argument("<name>", "Vault name").argument("<tag>", "File tag").option("-c, --chain <chain>", "The chain to use as the backend (arbitrumSepolia or baseSepolia").action(async (name, tag, options) => {
|
|
461
|
+
try {
|
|
462
|
+
const owner = getAccount().address;
|
|
463
|
+
const entry = await (await getFangorn(getChain(options.chain))).getDataSourceData(owner, name, tag);
|
|
464
|
+
console.log(`Entry: ${tag}`);
|
|
465
|
+
console.log(` CID: ${entry.cid}`);
|
|
466
|
+
console.log(` Gadget Descriptor: ${JSON.stringify(entry.gadgetDescriptor)}`);
|
|
467
|
+
process.exit(0);
|
|
468
|
+
} catch (err) {
|
|
469
|
+
console.error("Failed to get entry:", err.message);
|
|
470
|
+
process.exit(1);
|
|
471
|
+
}
|
|
472
|
+
});
|
|
370
473
|
program.command("decrypt").description("Decrypt a file from a vault").argument("<owner>", "The owner of the datasource").argument("<name>", "The name of the datasource").argument("<tag>", "File tag").option("-c, --chain <chain>", "The chain to use as the backend (arbitrumSepolia or baseSepolia").option("-o, --output <path>", "Output file path").action(async (owner, name, tag, options) => {
|
|
371
474
|
try {
|
|
372
475
|
const decrypted = await (await getFangorn(getChain(options.chain))).decryptFile(owner, name, tag);
|
package/lib/config.d.ts
CHANGED
package/lib/config.js
CHANGED
|
@@ -27,16 +27,14 @@ function getNetwork(name) {
|
|
|
27
27
|
let FangornConfig;
|
|
28
28
|
(function(_FangornConfig) {
|
|
29
29
|
_FangornConfig.ArbitrumSepolia = {
|
|
30
|
-
|
|
31
|
-
dataSourceRegistryContractAddress: "0x4bd96b4d274bdc845dccd06bb886ecdc0d708bdb",
|
|
30
|
+
dataSourceRegistryContractAddress: "0x602aedafe1096004d4db591b6537bc39d7ac71a6",
|
|
32
31
|
chainName: "arbitrumSepolia",
|
|
33
32
|
chain: arbitrumSepolia,
|
|
34
33
|
rpcUrl: "https://sepolia-rollup.arbitrum.io/rpc",
|
|
35
34
|
caip2: 421614
|
|
36
35
|
};
|
|
37
36
|
_FangornConfig.BaseSepolia = {
|
|
38
|
-
|
|
39
|
-
dataSourceRegistryContractAddress: "0x4e7c79e79da6d98e3747b72147c0bfd9330c95a6",
|
|
37
|
+
dataSourceRegistryContractAddress: "0x6fd0e50073dbd8169bcaf066bb4a4991bfa48eeb",
|
|
40
38
|
chainName: "baseSepolia",
|
|
41
39
|
chain: baseSepolia,
|
|
42
40
|
rpcUrl: "https://sepolia.base.org",
|
package/lib/fangorn.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ declare class Fangorn {
|
|
|
23
23
|
/**
|
|
24
24
|
* Register a new named data source owned by the current wallet.
|
|
25
25
|
*/
|
|
26
|
-
registerDataSource(name: string): Promise<Hex>;
|
|
26
|
+
registerDataSource(name: string, agentId?: string): Promise<Hex>;
|
|
27
27
|
/**
|
|
28
28
|
* Upload files to a vault with the given gadget for access control.
|
|
29
29
|
*/
|
|
@@ -48,6 +48,7 @@ declare class Fangorn {
|
|
|
48
48
|
*/
|
|
49
49
|
decryptFile(owner: Address, name: string, tag: string, authContext?: any): Promise<Uint8Array>;
|
|
50
50
|
getDataSource(owner: Address, name: string): Promise<Vault>;
|
|
51
|
+
registry(): DataSourceRegistry;
|
|
51
52
|
getManifest(owner: Address, name: string): Promise<VaultManifest | undefined>;
|
|
52
53
|
getDataSourceData(owner: Address, name: string, tag: string): Promise<VaultEntry>;
|
|
53
54
|
getAddress(): Hex;
|
package/lib/fangorn.js
CHANGED
|
@@ -23,8 +23,8 @@ var Fangorn = class Fangorn {
|
|
|
23
23
|
/**
|
|
24
24
|
* Register a new named data source owned by the current wallet.
|
|
25
25
|
*/
|
|
26
|
-
async registerDataSource(name) {
|
|
27
|
-
return await this.dataSourceRegistry.registerDataSource(name);
|
|
26
|
+
async registerDataSource(name, agentId) {
|
|
27
|
+
return await this.dataSourceRegistry.registerDataSource(name, agentId || "");
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
30
30
|
* Upload files to a vault with the given gadget for access control.
|
|
@@ -107,6 +107,9 @@ var Fangorn = class Fangorn {
|
|
|
107
107
|
async getDataSource(owner, name) {
|
|
108
108
|
return await this.dataSourceRegistry.getDataSource(owner, name);
|
|
109
109
|
}
|
|
110
|
+
registry() {
|
|
111
|
+
return this.dataSourceRegistry;
|
|
112
|
+
}
|
|
110
113
|
async getManifest(owner, name) {
|
|
111
114
|
const vault = await this.getDataSource(owner, name);
|
|
112
115
|
if (!vault.manifestCid || vault.manifestCid === "") return;
|
|
@@ -16,7 +16,7 @@ declare class DataSourceRegistry {
|
|
|
16
16
|
getContractAddress(): `0x${string}`;
|
|
17
17
|
getDataSource(owner: Address, name: string): Promise<Vault>;
|
|
18
18
|
getOwnedDataSources(address: Address): Promise<Hex[]>;
|
|
19
|
-
registerDataSource(name: string): Promise<Hex>;
|
|
19
|
+
registerDataSource(name: string, agentId: string): Promise<Hex>;
|
|
20
20
|
updateDataSource(name: string, newManifestCid: string): Promise<Hash>;
|
|
21
21
|
waitForTransaction(hash: Hash): Promise<viem143.TransactionReceipt>;
|
|
22
22
|
}
|
|
@@ -42,13 +42,13 @@ var DataSourceRegistry = class {
|
|
|
42
42
|
args: [address]
|
|
43
43
|
});
|
|
44
44
|
}
|
|
45
|
-
async registerDataSource(name) {
|
|
45
|
+
async registerDataSource(name, agentId) {
|
|
46
46
|
const { chain, account } = this.getWriteConfig();
|
|
47
47
|
const hash = await this.walletClient.writeContract({
|
|
48
48
|
address: this.contractAddress,
|
|
49
49
|
abi: DS_REGISTRY_ABI,
|
|
50
50
|
functionName: "registerDataSource",
|
|
51
|
-
args: [name],
|
|
51
|
+
args: [name, agentId],
|
|
52
52
|
chain,
|
|
53
53
|
account
|
|
54
54
|
});
|
|
@@ -11,9 +11,22 @@ interface LitEncryptionServiceConfig {
|
|
|
11
11
|
}
|
|
12
12
|
declare class LitEncryptionService implements EncryptionService {
|
|
13
13
|
private litClient;
|
|
14
|
-
private
|
|
15
|
-
constructor(litClient: LitClient,
|
|
14
|
+
private chainName;
|
|
15
|
+
constructor(litClient: LitClient, chainName: string);
|
|
16
|
+
static init(chain: string): Promise<LitEncryptionService>;
|
|
17
|
+
/**
|
|
18
|
+
* Encrypt filedata under the given gadget
|
|
19
|
+
* @param file The filedata to encrypt
|
|
20
|
+
* @param gadget The gadget to use
|
|
21
|
+
* @returns The ciphertext bundle
|
|
22
|
+
*/
|
|
16
23
|
encrypt(file: Filedata, gadget: Gadget): Promise<EncryptedPayload>;
|
|
24
|
+
/**
|
|
25
|
+
* Attempt to decrypt some encrypted data
|
|
26
|
+
* @param payload The encrytped bundle to recover
|
|
27
|
+
* @param authContext The authorization context
|
|
28
|
+
* @returns The decrytped output (on success), else empty
|
|
29
|
+
*/
|
|
17
30
|
decrypt(payload: EncryptedPayload, authContext: AuthContextWrapper): Promise<DecryptedPayload>;
|
|
18
31
|
createAuthContext(walletClient: WalletClient, domain: string): Promise<AuthContextWrapper>;
|
|
19
32
|
private createAuthSig;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { decryptData, encryptData } from "./aes.js";
|
|
2
2
|
import { LitAccessControlConditionResource, LitActionResource, LitPKPResource, createSiweMessage } from "@lit-protocol/auth-helpers";
|
|
3
|
+
import { createLitClient } from "@lit-protocol/lit-client";
|
|
3
4
|
import { createAuthManager, storagePlugins } from "@lit-protocol/auth";
|
|
5
|
+
import { nagaDev } from "@lit-protocol/networks";
|
|
4
6
|
|
|
5
7
|
//#region src/modules/encryption/lit.ts
|
|
6
8
|
const createDecryptLitActionCode = (chainName) => `(async () => {
|
|
@@ -23,18 +25,27 @@ const createDecryptLitActionCode = (chainName) => `(async () => {
|
|
|
23
25
|
});
|
|
24
26
|
}
|
|
25
27
|
})();`;
|
|
26
|
-
var LitEncryptionService = class {
|
|
27
|
-
constructor(litClient,
|
|
28
|
+
var LitEncryptionService = class LitEncryptionService {
|
|
29
|
+
constructor(litClient, chainName) {
|
|
28
30
|
this.litClient = litClient;
|
|
29
|
-
this.
|
|
31
|
+
this.chainName = chainName;
|
|
30
32
|
}
|
|
33
|
+
static async init(chain) {
|
|
34
|
+
return new LitEncryptionService(await createLitClient({ network: nagaDev }), chain);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Encrypt filedata under the given gadget
|
|
38
|
+
* @param file The filedata to encrypt
|
|
39
|
+
* @param gadget The gadget to use
|
|
40
|
+
* @returns The ciphertext bundle
|
|
41
|
+
*/
|
|
31
42
|
async encrypt(file, gadget) {
|
|
32
43
|
const { encryptedData, keyMaterial } = await encryptData(file.data);
|
|
33
44
|
const acc = await gadget.toAccessCondition();
|
|
34
45
|
const litEncryptedKey = await this.litClient.encrypt({
|
|
35
46
|
dataToEncrypt: keyMaterial.toString(),
|
|
36
47
|
unifiedAccessControlConditions: acc,
|
|
37
|
-
chain: this.
|
|
48
|
+
chain: this.chainName
|
|
38
49
|
});
|
|
39
50
|
return {
|
|
40
51
|
data: encryptedData,
|
|
@@ -46,6 +57,12 @@ var LitEncryptionService = class {
|
|
|
46
57
|
litAction: gadget.toLitAction()
|
|
47
58
|
};
|
|
48
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Attempt to decrypt some encrypted data
|
|
62
|
+
* @param payload The encrytped bundle to recover
|
|
63
|
+
* @param authContext The authorization context
|
|
64
|
+
* @returns The decrytped output (on success), else empty
|
|
65
|
+
*/
|
|
49
66
|
async decrypt(payload, authContext) {
|
|
50
67
|
const result = await this.litClient.executeJs({
|
|
51
68
|
code: createDecryptLitActionCode(authContext.chainName),
|
|
@@ -61,12 +78,16 @@ var LitEncryptionService = class {
|
|
|
61
78
|
return { data: await decryptData(payload.data, key) };
|
|
62
79
|
}
|
|
63
80
|
async createAuthContext(walletClient, domain) {
|
|
64
|
-
const
|
|
65
|
-
const
|
|
81
|
+
const isWindowUndefined = typeof window === "undefined";
|
|
82
|
+
const account = isWindowUndefined ? walletClient.account : walletClient;
|
|
83
|
+
const sessionContext = await (isWindowUndefined ? createAuthManager({ storage: storagePlugins.localStorageNode({
|
|
66
84
|
appName: "fangorn",
|
|
67
85
|
networkName: "naga-dev",
|
|
68
86
|
storagePath: "./lit-auth-storage"
|
|
69
|
-
}) }).
|
|
87
|
+
}) }) : createAuthManager({ storage: storagePlugins.localStorage({
|
|
88
|
+
appName: "fangorn",
|
|
89
|
+
networkName: "naga-dev"
|
|
90
|
+
}) })).createEoaAuthContext({
|
|
70
91
|
litClient: this.litClient,
|
|
71
92
|
config: { account },
|
|
72
93
|
authConfig: {
|
package/lib/test/testbed.d.ts
CHANGED
|
@@ -11,10 +11,11 @@ declare class TestBed {
|
|
|
11
11
|
private delegateeFangorn;
|
|
12
12
|
private delegatorAddress;
|
|
13
13
|
private storage;
|
|
14
|
+
private usdcContractAddress;
|
|
14
15
|
private vaultIds;
|
|
15
16
|
private config;
|
|
16
17
|
private litChain;
|
|
17
|
-
constructor(delegatorAddress: Address, delegatorFangorn: Fangorn, delegateeFangorn: Fangorn, storage: PinataStorage, config: AppConfig, litChain: EvmChain);
|
|
18
|
+
constructor(delegatorAddress: Address, delegatorFangorn: Fangorn, delegateeFangorn: Fangorn, storage: PinataStorage, config: AppConfig, litChain: EvmChain, usdcContractAddress: Address);
|
|
18
19
|
static init(delegatorWalletClient: WalletClient, delegateeWalletClient: WalletClient, jwt: string, gateway: string, dataSourceRegistryContractAddress: Hex, usdcContractAddress: Hex, rpcUrl: string, chain: string, litChain: EvmChain, caip2: number): Promise<TestBed>;
|
|
19
20
|
registerDatasource(name: string): Promise<Hex>;
|
|
20
21
|
/**
|
package/lib/test/testbed.js
CHANGED
|
@@ -6,8 +6,6 @@ import { PaymentGadget } from "../modules/gadgets/payment.js";
|
|
|
6
6
|
import { emptyWallet } from "./test-gadget.js";
|
|
7
7
|
import { SettlementTracker } from "../interface/settlement-tracker/settlementTracker.js";
|
|
8
8
|
import { createPublicClient, http, keccak256, parseUnits, toHex } from "viem";
|
|
9
|
-
import { createLitClient } from "@lit-protocol/lit-client";
|
|
10
|
-
import { nagaDev } from "@lit-protocol/networks";
|
|
11
9
|
import { arbitrumSepolia, baseSepolia } from "viem/chains";
|
|
12
10
|
import { PinataSDK } from "pinata";
|
|
13
11
|
|
|
@@ -17,10 +15,11 @@ var TestBed = class TestBed {
|
|
|
17
15
|
delegateeFangorn;
|
|
18
16
|
delegatorAddress;
|
|
19
17
|
storage;
|
|
18
|
+
usdcContractAddress;
|
|
20
19
|
vaultIds;
|
|
21
20
|
config;
|
|
22
21
|
litChain;
|
|
23
|
-
constructor(delegatorAddress, delegatorFangorn, delegateeFangorn, storage, config, litChain) {
|
|
22
|
+
constructor(delegatorAddress, delegatorFangorn, delegateeFangorn, storage, config, litChain, usdcContractAddress) {
|
|
24
23
|
this.delegatorAddress = delegatorAddress;
|
|
25
24
|
this.delegatorFangorn = delegatorFangorn;
|
|
26
25
|
this.delegateeFangorn = delegateeFangorn;
|
|
@@ -28,22 +27,20 @@ var TestBed = class TestBed {
|
|
|
28
27
|
this.config = config;
|
|
29
28
|
this.litChain = litChain;
|
|
30
29
|
this.storage = storage;
|
|
30
|
+
this.usdcContractAddress = usdcContractAddress;
|
|
31
31
|
}
|
|
32
32
|
static async init(delegatorWalletClient, delegateeWalletClient, jwt, gateway, dataSourceRegistryContractAddress, usdcContractAddress, rpcUrl, chain, litChain, caip2) {
|
|
33
33
|
let chainImpl = arbitrumSepolia;
|
|
34
34
|
if (chain === "baseSepolia") chainImpl = baseSepolia;
|
|
35
35
|
const config = {
|
|
36
36
|
dataSourceRegistryContractAddress,
|
|
37
|
-
usdcContractAddress,
|
|
38
37
|
chainName: chain,
|
|
39
38
|
chain: chainImpl,
|
|
40
39
|
rpcUrl,
|
|
41
40
|
caip2
|
|
42
41
|
};
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
const delegatorEncryption = new LitEncryptionService(delegatorLitClient, { chainName: chain });
|
|
46
|
-
const delegateeEncryption = new LitEncryptionService(delegateeLitClient, { chainName: chain });
|
|
42
|
+
const delegatorEncryption = await LitEncryptionService.init(chain);
|
|
43
|
+
const delegateeEncryption = await LitEncryptionService.init(chain);
|
|
47
44
|
const pinata = new PinataSDK({
|
|
48
45
|
pinataJwt: jwt,
|
|
49
46
|
pinataGateway: gateway
|
|
@@ -53,12 +50,12 @@ var TestBed = class TestBed {
|
|
|
53
50
|
const domain = "localhost";
|
|
54
51
|
const delegatorFangorn = await Fangorn.init(delegatorWalletClient, delegatorStorage, delegatorEncryption, domain, config);
|
|
55
52
|
const delegateeFangorn = await Fangorn.init(delegateeWalletClient, delegateeStorage, delegateeEncryption, domain, config);
|
|
56
|
-
return new TestBed(delegatorWalletClient.account.address, delegatorFangorn, delegateeFangorn, delegatorStorage, config, litChain);
|
|
53
|
+
return new TestBed(delegatorWalletClient.account.address, delegatorFangorn, delegateeFangorn, delegatorStorage, config, litChain, usdcContractAddress);
|
|
57
54
|
}
|
|
58
55
|
async registerDatasource(name) {
|
|
59
56
|
const existing = this.vaultIds.get(name);
|
|
60
57
|
if (existing) return existing;
|
|
61
|
-
const id = await this.delegatorFangorn.registerDataSource(name);
|
|
58
|
+
const id = await this.delegatorFangorn.registerDataSource(name, "");
|
|
62
59
|
this.vaultIds.set(name, id);
|
|
63
60
|
return id;
|
|
64
61
|
}
|
|
@@ -153,7 +150,7 @@ var TestBed = class TestBed {
|
|
|
153
150
|
};
|
|
154
151
|
}
|
|
155
152
|
async payForFile(owner, name, tag, amount, usdcDomainName, settlementTrackerAddress, walletClient, rpcUrl) {
|
|
156
|
-
const auth = await this.buildUsdcAuthorization(this.delegatorAddress, amount, this.config.caip2, usdcDomainName, this.
|
|
153
|
+
const auth = await this.buildUsdcAuthorization(this.delegatorAddress, amount, this.config.caip2, usdcDomainName, this.usdcContractAddress);
|
|
157
154
|
const commitmentHex = fieldToHex(await computeTagCommitment(owner, name, tag, amount));
|
|
158
155
|
await new SettlementTracker(settlementTrackerAddress, createPublicClient({ transport: http(rpcUrl) }), walletClient).pay({
|
|
159
156
|
commitment: commitmentHex,
|
package/package.json
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fangorn-sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2026.02.24",
|
|
4
4
|
"description": "A zero-knowledge conditional access control framework",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/fangorn-network/fangorn.git"
|
|
8
8
|
},
|
|
9
|
+
"bin": {
|
|
10
|
+
"fangorn": "lib/cli.js"
|
|
11
|
+
},
|
|
9
12
|
"license": "MIT",
|
|
10
13
|
"type": "module",
|
|
11
14
|
"main": "lib/index.js",
|