Solidity: Approvals and Token Handling

This page covers how your contracts prepare ERC‑20 balances and allowances for interacting with the DecentralizedOracle. No governance or deployment details are included.

Key facts:

  • The oracle pulls tokens via transferFrom on:
    • createQuery(...) — pulls the fixed creation deposit.
    • voteOnQuery(...) — pulls the specified collateral.
  • Your contract must hold tokens and approve the oracle before calling these functions.
  • Time is block-based; no special token mechanics occur during resolution beyond payout from the oracle’s holdings.

Funding your contract

Your contract must first receive tokens. Typical patterns:

  • External EOAs transfer tokens to your contract address.
  • Another system contract transfers tokens in a controlled flow.

Example (outside scope of oracle):

// Users send tokens directly to this contract address.
// The contract should expose a withdrawal function with access control if needed.

Approving the oracle

Approve once with a sufficiently large allowance, then reuse for multiple oracle operations.

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

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IOracle {
    function createQuery(uint256, string calldata, string calldata, uint8) external returns (uint256);
    function voteOnQuery(uint256, uint8[] calldata, uint256) external;
}

contract OracleApprovals {
    IOracle public immutable oracle;
    IERC20 public immutable token;

    constructor(address oracle_, address token_) {
        oracle = IOracle(oracle_);
        token = IERC20(token_);
    }

    /// @notice Approve the oracle to pull up to `amount` tokens from this contract.
    function approveOracle(uint256 amount) external {
        // Ensure this contract already holds tokens (fund first).
        require(token.approve(address(oracle), amount), "approve failed");
    }
}

Considerations:

  • Some tokens (non‑standard ERC‑20s) require setting allowance to 0 before updating. If the token enforces that pattern, call approve(oracle, 0) first, then approve the new amount.
  • Persist a generous allowance to minimize repeated approvals, or implement a per‑tx approval flow if stricter control is necessary (adds gas and complexity).

Create and vote using the approved allowance

Once approved, the contract can call oracle methods that pull funds:

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

contract OracleUsage {
    IOracle public immutable oracle;

    constructor(address oracle_) {
        oracle = IOracle(oracle_);
    }

    function openQuery(
        uint256 startOffset,
        string calldata question,
        string calldata metadata,
        uint8 totalOptions
    ) external returns (uint256 queryId) {
        // Assumes allowance covers the required creation deposit
        queryId = oracle.createQuery(block.number + startOffset, question, metadata, totalOptions);
    }

    function voteSingleOption(uint256 queryId, uint8 option, uint256 collateral) external {
        // Assumes allowance covers `collateral`
        uint8[] memory opts = new uint8[](1);
        opts[0] = option;
        oracle.voteOnQuery(queryId, opts, collateral);
    }
}

Allowance management patterns

  • One‑time large allowance: simplest UX and gas‑efficient. Risk: if oracle address is compromised (contract itself should be immutable), you have over‑approved. Typically acceptable for audited/immutable contracts.
  • Per‑call approval (strict): set allowance to the exact amount needed each time. Safer but higher gas and UX complexity. Also be aware of tokens requiring zero‑first semantics.

Security checklist

  • Ensure only authorized actors can call functions that trigger spending (e.g., onlyOwner, role checks).
  • Provide a withdrawal method for stuck tokens with access control.
  • Consider events for internal bookkeeping (e.g., “OracleAllowanceUpdated” or “QueryOpened”).
  • Avoid approving unbounded allowances if you expect to rotate oracle addresses in the future.

Next Pages

Back to: