Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
e6b4318
add: external api reference implementation
redpanda-f Jan 27, 2026
8770c76
fix: issues
redpanda-f Jan 27, 2026
3584ef1
fix: CI issues
redpanda-f Jan 27, 2026
55872bc
makehappy: fmt
redpanda-f Jan 27, 2026
4385d00
fixes: CI
redpanda-f Jan 27, 2026
ff98c00
stablize: symlinks for /state/latest, simplify CI check
redpanda-f Jan 27, 2026
fd96904
add: nodejs 20 installation
redpanda-f Jan 28, 2026
6bba3f0
Update .github/workflows/ci.yml, use nodejs LTS
redpanda-f Jan 28, 2026
14c6f6d
Extract magic number for user account count to constant (#46)
Copilot Jan 28, 2026
3fe8052
item 1: remove balance fields from export
redpanda-f Jan 29, 2026
fcd2727
item 2: paths as source of truth
redpanda-f Jan 29, 2026
d25e24a
item 3: error handling with Result
redpanda-f Jan 29, 2026
9e31838
item 5: improve symlink test
redpanda-f Jan 29, 2026
593e4e0
item 6: add zod schema validation
redpanda-f Jan 29, 2026
df0a434
item 7: remove dead code
redpanda-f Jan 29, 2026
8b3797f
item 9: rename curio_providers to pdp
redpanda-f Jan 29, 2026
9baab25
fix: remove unnecessary to_string calls
redpanda-f Jan 29, 2026
2d8163b
fixes: schema validation
redpanda-f Jan 29, 2026
7ac9dea
makehappy: fmt
redpanda-f Jan 29, 2026
ff1a487
Update examples/read-devnet-info.js
redpanda-f Jan 29, 2026
0e50ebe
Update examples/read-devnet-info.js
redpanda-f Jan 29, 2026
2aac53f
fix: warn message
redpanda-f Jan 29, 2026
3ebe2eb
fix: use expect() for yugabyte
redpanda-f Jan 29, 2026
2677791
add: expect()
redpanda-f Jan 29, 2026
c970eae
fix: export
redpanda-f Jan 29, 2026
f79cc5c
fix: examples README
redpanda-f Jan 29, 2026
415de9c
remove: eslint
redpanda-f Jan 29, 2026
cf31642
docs: enhance README with link to examples
BigLep Jan 29, 2026
3f5cb81
feat: linkify README
redpanda-f Jan 30, 2026
9d9ed2d
update: examples
redpanda-f Jan 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 38 additions & 22 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -272,49 +272,37 @@ jobs:
# On failure, collect and print Docker container logs for debugging
- name: "EXEC: {Collect Docker logs on failure}, independent"
run: |
echo "+++++++++++ foc-devnet version run"
cat ~/.foc-devnet/state/latest/version.txt 2>/dev/null || echo "No version file found"

echo "+++++++++++ Listing runs in foc-devnet..."
ls -1 ~/.foc-devnet/run/ 2>/dev/null || echo "No runs directory found"

echo "+++++++++++ Get latest run ID"
LATEST_RUN=$(ls -t ~/.foc-devnet/run/ 2>/dev/null | head -1)
if [ -n "$LATEST_RUN" ]; then
echo "Latest run: $LATEST_RUN"
RUN_DIR="$HOME/.foc-devnet/run/$LATEST_RUN"
else
RUN_DIR="$HOME/.foc-devnet/state/latest"
fi
RUN_DIR="$HOME/.foc-devnet/state/latest"

echo "+++++++++++ foc-devnet version"
cat "$RUN_DIR/version.txt" 2>/dev/null || echo "No version file found"

echo "+++++++++++ Disk space..."
echo "+++++++++++ Disk space"
sudo df -h 2>/dev/null || echo "df command failed"

echo "+++++++++++ Latest Run Directory"
ls -lath "$RUN_DIR" 2>/dev/null || echo "No run directory found at $RUN_DIR"
ls -lath "$RUN_DIR/logs" 2>/dev/null || echo "No logs directory found at $RUN_DIR/logs"
echo "+++++++++++ Run Directory Contents"
ls -lath "$RUN_DIR" 2>/dev/null || echo "No run directory found"

echo "+++++++++++ Contract Addresses"
cat "$RUN_DIR/contract_addresses.json" 2>/dev/null || echo "No contract addresses file found"

echo "+++++++++++ Step Context"
cat "$RUN_DIR/step_context.json" 2>/dev/null || echo "No step context file found"

echo "+++++++++++ FOC metadata"
echo "+++++++++++ FOC Metadata"
cat "$RUN_DIR/foc_metadata.json" 2>/dev/null || echo "No foc metadata file found"

echo "+++++++++++ Container Logs"
if [ -d "$RUN_DIR/logs" ]; then
for logfile in "$RUN_DIR/logs"/*; do
if [ -f "$logfile" ]; then
echo ""
echo "📰📰📰📰📰📰📰📰📰📰📰 Logs from $(basename "$logfile") 📰📰📰📰📰📰📰📰📰📰📰📰📰"
echo "📰 Logs from $(basename "$logfile") 📰"
Comment thread
rvagg marked this conversation as resolved.
cat "$logfile" 2>/dev/null || echo "Failed to read $logfile"
echo "📰📰📰📰📰📰📰📰📰📰📰📰📰📰📰📰📰📰📰📰📰📰📰📰📰📰📰📰📰📰📰📰📰📰📰📰📰"
fi
done
else
echo "No container logs directory found at $RUN_DIR/logs"
echo "No container logs directory found"
fi

# Verify cluster is running correctly
Expand All @@ -326,6 +314,34 @@ jobs:
echo "Containers using foc-* images (running or exited):"
docker ps -a --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}'

# Verify devnet-info.json was exported successfully
- name: "CHECK: {Verify devnet-info.json exists}"
if: steps.start_cluster.outcome == 'success'
run: |
DEVNET_INFO="$HOME/.foc-devnet/state/latest/devnet-info.json"
test -f "$DEVNET_INFO" || exit 1
echo "✓ devnet-info.json created"
jq '.version' "$DEVNET_INFO"

# Setup Node.js for JavaScript examples
- name: "EXEC: {Setup Node.js}, independent"
if: steps.start_cluster.outcome == 'success'
uses: actions/setup-node@v4
with:
node-version: '20'

# Validate schema using zod
- name: "CHECK: {Validate devnet-info.json schema}"
if: steps.start_cluster.outcome == 'success'
run: |
DEVNET_INFO="$HOME/.foc-devnet/state/latest/devnet-info.json"
cd examples
npm install
node validate-schema.js "$DEVNET_INFO"
node read-devnet-info.js "$DEVNET_INFO"
node check-balances.js "$DEVNET_INFO"
echo "✓ All examples ran well"

# Clean shutdown
- name: "EXEC: {Stop cluster}, independent"
run: ./foc-devnet stop
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
target/
contracts/MockUSDFC/lib/
contracts/MockUSDFC/broadcast/
artifacts/
artifacts/
.vscode/
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ This will:

**That's it!** Your local Filecoin network is running.

### Step 4: Use the Network

See [examples/README.md](examples/README.md) for how you can easily consume network addresses, parameters, etc. and hook them into Synapse, etc.

---

## ✨ Key Features
Expand Down Expand Up @@ -161,6 +165,12 @@ See **[README_ADVANCED.md](README_ADVANCED.md)** for comprehensive documentation

---

## 🚶 Examples

See [examples/README.md](examples/README.md).

---

## 📝 License

MIT License - see [LICENSE](LICENSE) file for details.
Expand Down
20 changes: 20 additions & 0 deletions examples/.eslintrc.json
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rm this file

Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"env": {
"node": true,
"es2022": true
},
"parserOptions": {
"ecmaVersion": 2022,
"sourceType": "module"
},
"extends": "eslint:recommended",
"rules": {
"no-console": "off",
"no-unused-vars": [
"error",
{
"argsIgnorePattern": "^_"
}
]
}
}
2 changes: 2 additions & 0 deletions examples/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules/
package-lock.json
44 changes: 44 additions & 0 deletions examples/README.md
Comment thread
redpanda-f marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# FOC DevNet Examples

This directory contains examples demonstrating how to interact with a running FOC DevNet instance using the exported `devnet-info.json` file.

## Files
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realize the building blocks are here, but is there a reason we don't have an exmaple of starting up synapse using the content from read-devnet-info.js (per #7 ) as that is a common usecase?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's part of a larger chunk of task, that in my opinion can be separated from this.

In any case, this API-providing PR has to be (by git workflow) merged into main separate from the actual use of it.

Would not want to do synapse work in this PR since now endorsements are part of mainline foc-devnet and synapse is in flux w.r.t endorsements.

What I would be able to agree to, is whether #7 should be closed by this PR or not (which it is only getting unblocked by, not being closed)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I agree this PR unblocks #7 but doesn't close it. I'll reopen #7 since the done criteria haven't been met.


- [read-devnet-info.js](read-devnet-info.js) - JavaScript example showing how to read and use the DevNet info
- [check-balances.js](check-balances.js) - JavaScript example demonstrating how to check user balances
- [devnet-schema.js](devnet-schema.js) - Zod schema for validating DevNet info exports
- [validate-schema.js](validate-schema.js) - CLI tool for validating devnet-info.json against the schema

## Prerequisites

- Node.js 20+ installed
- A running FOC DevNet instance (`foc-devnet start`)
- npm packages: `viem`, `zod`

## Usage

1. Start the DevNet:
```bash
foc-devnet start
```

2. Find the devnet-info.json file:
```bash
# The file is located in the run directory
cat ~/.foc-devnet/state/latest/devnet-info.json
```

3. Install dependencies and run examples:
```bash
cd examples
npm install

# Validate the schema
npm run validate-schema ~/.foc-devnet/state/latest/devnet-info.json

# Read DevNet info
npm run read-info ~/.foc-devnet/state/latest/devnet-info.json

# Check balances
npm run check-balances ~/.foc-devnet/state/latest/devnet-info.json
```
159 changes: 159 additions & 0 deletions examples/check-balances.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/**
* FOC DevNet Balance Checker
*
* This example demonstrates how to use ethers.js to check balances
* of user accounts on the DevNet using the exported devnet-info.json.
*
* Usage:
* node check-balances.js [path-to-devnet-info.json]
*
* If no path is provided, defaults to ~/.foc-devnet/state/latest/devnet-info.json
*/

import { readFileSync, existsSync } from "fs";
import { homedir } from "os";
import { join } from "path";
import { createPublicClient, http, parseAbi } from "viem";
import { formatUnits } from "viem";

// ERC20 ABI (minimal for balanceOf)
const ERC20_ABI = parseAbi([
"function balanceOf(address owner) view returns (uint256)",
"function decimals() view returns (uint8)",
"function symbol() view returns (string)",
]);

/**
* Load the devnet-info.json file.
* @param {string} filePath - Path to the devnet-info.json file
* @returns {object} The parsed DevNet info
*/
function loadDevnetInfo(filePath) {
if (!existsSync(filePath)) {
throw new Error(`DevNet info file not found: ${filePath}`);
}
const content = readFileSync(filePath, "utf8");
return JSON.parse(content);
}

/**
* Format wei to ether with specified decimals.
* @param {bigint} wei - Amount in wei
* @param {number} decimals - Decimal places
* @returns {string} Formatted amount
*/
function formatBalance(wei, decimals = 18) {
return formatUnits(wei, decimals);
}

/**
* Check native FIL balance for an address.
* @param {object} client - Viem public client
* @param {string} address - Address to check
* @returns {Promise<string>} Balance in FIL
*/
async function checkNativeBalance(client, address) {
const balance = await client.getBalance({ address });
return formatBalance(balance);
}

/**
* Check ERC20 token balance for an address.
* @param {object} client - Viem public client
* @param {string} tokenAddress - Token contract address
* @param {string} userAddress - User address to check
* @returns {Promise<{balance: string, symbol: string}>} Balance and symbol
*/
async function checkTokenBalance(client, tokenAddress, userAddress) {
const [balance, decimals, symbol] = await Promise.all([
client.readContract({
address: tokenAddress,
abi: ERC20_ABI,
functionName: "balanceOf",
args: [userAddress],
}),
client.readContract({
address: tokenAddress,
abi: ERC20_ABI,
functionName: "decimals",
}),
client.readContract({
address: tokenAddress,
abi: ERC20_ABI,
functionName: "symbol",
}),
]);
return {
balance: formatBalance(balance, decimals),
symbol,
};
}

// Main execution
async function main() {
// Determine file path
const defaultPath = join(
homedir(),
".foc-devnet",
"state",
"latest",
"devnet-info.json"
);
const filePath = process.argv[2] || defaultPath;

console.log(`Loading DevNet info from: ${filePath}\n`);

try {
const { info } = loadDevnetInfo(filePath);

// Connect to the Lotus RPC
const client = createPublicClient({
transport: http(info.lotus.host_rpc_url),
});
console.log(`Connected to: ${info.lotus.host_rpc_url}`);

// Check if network is accessible
const blockNumber = await client.getBlockNumber();
console.log(`Current block: ${blockNumber}\n`);

console.log("═══════════════════════════════════════════════════════════");
console.log(" Account Balances");
console.log("═══════════════════════════════════════════════════════════\n");

// Check balances for all users
for (const user of info.users) {
console.log(`${user.name} (${user.evm_addr}):`);

// Check native FIL balance
const filBalance = await checkNativeBalance(client, user.evm_addr);
console.log(` Native FIL: ${filBalance} tFIL`);

// Check MockUSDFC balance
if (info.contracts.mockusdfc_addr) {
try {
const { balance, symbol } = await checkTokenBalance(
client,
info.contracts.mockusdfc_addr,
user.evm_addr
);
console.log(` ${symbol}: ${balance}`);
} catch (e) {
console.log(` MockUSDFC: Error - ${e.message}`);
}
}
console.log();
}

console.log("═══════════════════════════════════════════════════════════\n");
} catch (error) {
console.error(`Error: ${error.message}`);
if (error.code === "ECONNREFUSED") {
console.error(
"Could not connect to the DevNet. Make sure foc-devnet is running."
);
}
process.exit(1);
}
}

main();
Loading