-
Notifications
You must be signed in to change notification settings - Fork 4
feat: external API support, JS reference example implementation, symlink issue fix for "latest" #45
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e6b4318
8770c76
3584ef1
55872bc
4385d00
ff98c00
fd96904
6bba3f0
14c6f6d
3fe8052
fcd2727
d25e24a
9e31838
593e4e0
df0a434
8b3797f
9baab25
2d8163b
7ac9dea
ff1a487
0e50ebe
2aac53f
3ebe2eb
2677791
c970eae
f79cc5c
415de9c
cf31642
3f5cb81
9d9ed2d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| target/ | ||
| contracts/MockUSDFC/lib/ | ||
| contracts/MockUSDFC/broadcast/ | ||
| artifacts/ | ||
| artifacts/ | ||
| .vscode/ |
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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": "^_" | ||
| } | ||
| ] | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| node_modules/ | ||
| package-lock.json |
|
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 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 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)
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| - [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 | ||
| ``` | ||
| 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(); |
Uh oh!
There was an error while loading. Please reload this page.