Advanced

Advanced Topics

This section covers advanced FlowNodes usage: adding custom Solidity logic, building upgradeable contracts with UUPS proxies, integrating Chainlink data feeds, and managing project versions.

Custom Solidity functions

The Custom Function block lets you write arbitrary Solidity logic inside a managed node. This is useful when no existing block covers your specific requirement.

Adding a Custom Function block

Drag the custom_function block from the Advanced category onto the canvas. Select it to open the Properties Panel, then configure:

PropertyDescriptionExample
functionNameThe Solidity function name (camelCase)withdrawEth
visibilitypublic / external / internal / privateexternal
stateMutabilityview / pure / payable / (empty for state-modifying)
paramsArray of {name, type} parameter objects[{name:"amount",type:"uint256"}]
returnsArray of {type} return type objects[{type:"bool"}]
bodyRaw Solidity for the function bodypayable(owner()).transfer(amount);

Example: custom withdrawal function

solidity
// Generated from Custom Function block config:
// functionName: withdrawEth
// visibility: external
// stateMutability: (none — state-modifying)
// params: [{name: "to", type: "address"}, {name: "amount", type: "uint256"}]

function withdrawEth(address to, uint256 amount) external onlyOwner {
    require(address(this).balance >= amount, "Insufficient balance");
    (bool success, ) = to.call{value: amount}("");
    require(success, "Transfer failed");
}
Tip
The onlyOwner modifier is only available if an Ownable block is connected. If you connect AccessControl, use theonlyRole(ROLE_NAME) modifier instead.

Custom Modifier block

Similarly, the custom_modifier block defines a reusable function guard. Reference it from a Custom Function block by adding its name to the function's modifiers list.

Custom Event block

The custom_event_def block defines a Solidity event. Addemit EventName(arg1, arg2) inside a Custom Function block body to emit it. Events are automatically added to the ABI and indexed by the Alchemy notification pipeline.

State Variable block

Use custom_state_variable to add contract-level storage variables. Configure the type, name, visibility, and initial value in the Properties Panel.

Upgradeable contracts (UUPS)

UUPS (Universal Upgradeable Proxy Standard, EIP-1822) stores the upgrade logic in the implementation contract rather than the proxy. This is more gas-efficient than Transparent Proxy and the recommended upgrade pattern for new contracts.

Required block configuration

To build a UUPS upgradeable contract, connect these blocks:

  1. 1
    oz_uups_proxyAdds the UUPS proxy storage slot and upgrade authorization hook.
  2. 2
    oz_initializerReplaces the constructor. The initialize() function is called once at proxy deployment. All other blocks' constructors become initializer calls.
  3. 3
    oz_ownable2stepStrongly recommended — the _authorizeUpgrade() function should be restricted to the owner (or multi-sig). Ownable2Step prevents accidental ownership renouncement.

Generated UUPS pattern

solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";

contract MyTokenV1 is
    Initializable,
    ERC20Upgradeable,
    Ownable2StepUpgradeable,
    UUPSUpgradeable
{
    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() { _disableInitializers(); }

    function initialize(address initialOwner) public initializer {
        __ERC20_init("MyToken", "MTK");
        __Ownable2Step_init();
        __UUPSUpgradeable_init();
        _transferOwnership(initialOwner);
    }

    function _authorizeUpgrade(address newImpl)
        internal override onlyOwner {}
}
Warning
Storage layout must be preserved between upgrades. Never remove or reorder storage variables in a new implementation version. Only add new variables at the end or use storage gaps (uint256[50] private __gap).

Deploying an upgradeable contract

The Deploy Modal detects UUPS blocks and automatically deploys both the implementation contract and a ERC1967Proxy pointing to it. The proxy address is what users interact with — it is the address shown in your Deployed Contracts list.

Project versioning

FlowNodes auto-saves your canvas to the database every 30 seconds when there are unsaved changes. The save indicator in the top-left of the editor shows the current status (Saved / Unsaved / Saving...).

Manual save

Press ⌘S (Ctrl+S on Windows) to save immediately. This is useful before running a security scan or deployment.

Graph JSON format

The canvas state is stored as a FlowNodesGraph JSON object in the projects table. You can inspect it (or use it to reproduce a project) by reading thegraphJson field via the API.

Compiled artifact caching

Each time you compile, the ABI, bytecode, and Solidity source are cached in the project record. This allows the Deploy Modal to work immediately on page load without recompiling.

Advanced — FlowNodes Docs | FlowNodes