Ethereum Configuration
appsettings.json, the default network, wallet credentials, and how the node registers itself
appsettings.json — the RPC endpoint catalogue
The node reads its network catalogue from an Ethereum section in application settings,
bound via IOptions<EthereumNetworkOptions>:
"Ethereum": {
"DefaultNetwork": "ethereum",
"RpcTimeoutSeconds": 30,
"Networks": {
"ethereum": { "RpcUrl": "https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY", "ChainId": 1, "BlockExplorer": "https://etherscan.io" },
"polygon": { "RpcUrl": "https://polygon-mainnet.g.alchemy.com/v2/YOUR_KEY", "ChainId": 137, "BlockExplorer": "https://polygonscan.com" }
}
}
| Key | Type | Required | Description |
|---|---|---|---|
DefaultNetwork | text | ✓ Yes | Network name used when an operation doesn't set its own network field. Must match a key under Networks. |
RpcTimeoutSeconds | number | No (default 30) | HTTP timeout applied to the shared JSON-RPC client. |
Networks | object | ✓ Yes | Map of network name → RPC endpoint configuration. See Networks. |
Per-Network Fields
Each entry under Ethereum:Networks is an EthereumNetworkConfig:
| Field | Type | Required | Description |
|---|---|---|---|
RpcUrl | text | One of RpcUrl/RpcUrlCredentialId | Plain JSON-RPC URL. Only appropriate for endpoints that don't embed a provider API key — local Anvil/Hardhat/Ganache, self-hosted nodes, or public rate-limited endpoints. |
RpcUrlCredentialId | number | One of RpcUrl/RpcUrlCredentialId | Vault credential ID (SERVICE_URL type) whose Service URL field holds the full RPC URL, including the embedded API key. Required for Alchemy/Infura/QuickNode-style endpoints — keeps the key out of appsettings and source control. Takes precedence over RpcUrl when both are set. |
ChainId | number | ✓ Yes | The EVM chain ID (e.g. 1 for Ethereum mainnet, 137 for Polygon). Returned by utility.chainId and used internally by Nethereum's signer. |
BlockExplorer | text | No | Base URL of a block explorer for this network (e.g. Etherscan, Polygonscan) — informational only, not called by the node. |
network value has no matching entry
under Ethereum:Networks, the node throws EthereumNetworkNotConfiguredException before
any RPC call is attempted. See Troubleshooting.
Per-Operation network Field
Every operation accepts an optional network config key. When omitted, it falls back to
Ethereum:DefaultNetwork:
| Field | Type | Required | Description |
|---|---|---|---|
network | select | No | Any key configured under Ethereum:Networks — e.g. ethereum, polygon, or a custom EVM chain you've added. Defaults to Ethereum:DefaultNetwork when unset. |
Wallet Credentials (Write Operations)
Every write operation (transaction.send, token.transfer, contract.write,
NFT transfers, wallet signing, etc.) needs a signing key. The private key is never a config
field — it's resolved from the platform credential vault via the standard credentialID config key
(auto-wired by BaseNodeExecutor) and read with ReadCredentialRawPrimaryAsync.
| Field | Type | Required | Description |
|---|---|---|---|
credentialID | credential reference | ✓ Yes, for writes | Vault entry of type CRYPTO_WALLET. Its PrivateKey is used directly if present; otherwise its SeedPhrase (plus DerivationPath) is used to derive account 0's private key. |
credentialID fails immediately with "No signing credential configured. Set
'credentialID' on this node to a vault entry (CRYPTO_WALLET type) containing a private key or seed
phrase." — no RPC call is attempted.
ToDictionary() or node output data, and the platform's SensitiveDataScrubber
already redacts any config key containing private.
Registration
EthereumDependency.RegisterDefaults(services) registers everything the node needs:
EthereumNetworkOptions, bound lazily from theEthereumappsettings section- The shared JSON-RPC
HttpClient(EthereumRateLimitHandlerretries 429s) EthereumConnectionProvider— a singleton that caches read-only NethereumWeb3clients per network- A separate named
HttpClientplusEthereumNftMetadataFetchersingleton for fetching NFTtokenURImetadata (arbitrary hosts/IPFS gateways — no rate-limit handler, different timeout profile) - One scoped service per resource: account, block, transaction, token, contract, ens, gas, utility, nft, wallet
- The executor itself (scoped) and the
ExecutorRegistryentry forethereum
ProjectReference alone
is not sufficient in this codebase's plugin-loading mechanism — an assembly that is only referenced but never
force-loaded is not guaranteed to appear in AppDomain.CurrentDomain.GetAssemblies() when
assembly-scanning runs. Add new EthereumDependency().RegisterDefaults(services); to
Plugins_RegisterAllNodes(...) in
ServiceCollectionExtensionsForAI.cs. Without this line the node type won't appear in the workflow
designer even though the package is referenced — the same reason Slack's own registration is currently
commented out there.
Project Layout
The node ships as three .NET 9 projects (plus a test project):
| Project | Responsibility |
|---|---|
BizFirst.Integration.Ethereum.Domain | Pure types — result records, network configuration model. Zero project references. |
BizFirst.Integration.Ethereum.Services | Nethereum-backed business logic — one service class per resource, the shared RPC connection provider, a 429-retry HTTP handler. |
BizFirst.Ai.ExecutionNodes.Blockchain.Ethereum | The ExecutionNode itself — config parsing, operation routing, result mapping, DI registration. |
Built on Nethereum over JSON-RPC — talks to Ethereum mainnet and any EVM-compatible network (Polygon, Arbitrum, Optimism, Base, local dev chains, etc.) that you point an RPC URL at.