# Universal Smart Contracts

{% hint style="danger" %}
**Outdated Documentation:** This page covers the architecture used for USC V1, which was deprecated on 2026-03-30. For the latest documentation, please visit: [usc documentation](https://docs.creditcoin.org/usc)
{% endhint %}

## What is the Universal Smart Contract

**Universal Smart Contracts** (USC) act as an adapter which extends existing smart contracts with crosschain capabilities, allowing contracts to *query*, *retrieve*, and *verify* data from external blockchains via the **Creditcoin Decentralized Oracle**.

Unlike traditional omnichain or crosschain solutions that focus narrowly on token transfers or specific assets, USC provides a **general-purpose execution layer**. This enables contracts to act on externally verified data *without needing to rewrite core logic*. By adopting USC into their tech stack, developers can transform their contracts into *universal* components powered by seamless crosschain data, allowing for novel patterns of interoperability across multiple blockchains.

## Universal Smart Contract (USC) Components

As best practice, we separate the functions of a universal DApp into at least 2 contracts:

1. The **core USC contract**, which allows users and other contracts alike to **query and retrieve verified cross-chain data** in a trust-minimized and protocol-agnostic way.
2. **USC Business Logic contracts**, where the custom logic of a DApp is implemented. These contracts include **hook functions** that are triggered when the core USC contract processes new cross-chain data. With **hook functions**, cross-chain data passes seamlessly from the Creditcoin oracle, through the core USC contract, and finally to the USC Business Logic contracts where it triggers DApp logic. This makes it easy for builders to **integrate USC functionality seamlessly** into a wide range of decentralized applications.

## Core USC Contract

The Core Universal Smart Contract includes functions to read data from **prover contract** after it has been verified by validators. The core functions of USC Contract are as follows:

{% code overflow="wrap" %}

```solidity
function isQueryUsed(
    address user,
    bytes32 queryId
) public view returns (bool) {Offchain Oracle Workers
    // ...
}
```

{% endcode %}

**type:** *Public*

Checks if a given `queryId` from a specific user has already been processed. **Duplicate queries will be rejected to avoid replay attacks.**

***

{% code overflow="wrap" %}

```solidity
function _markQueryUsed(address user, bytes32 queryId) internal {
    // ...
}
```

{% endcode %}

**type:** *Internal*

Marks a `queryId` as used for a given user. This ensures that **once a query has been processed, it cannot be executed again**.

***

{% code overflow="wrap" fullWidth="false" %}

```solidity
function _processOracleResults(
    address proverContractAddr,
    bytes32 queryId
) internal returns(
    bytes32 functionSignature, 
    ResultSegment[] memory eventSegments
) {
    // ...
}
```

{% endcode %}

**type:** *Internal*

Core function responsible for processing the results of oracle queries:

* Validates that the `queryId` has not been used before.
* Retrieves query details from the **Prover contract**.
* Extracts the function signature and event data (`ResultSegment[]`).
* Prepares the parsed data for further handling by the business logic contract.
* Calls  `_onQueryValidated()` hook function to trigger USC business logic contract.

***

{% code overflow="wrap" %}

```solidity
function _onQueryValidated(
    bytes32 queryId,
    bytes32 functionSignature,
    ResultSegment[] memory eventSegments
) internal virtual;
```

{% endcode %}

A **hook function** implemented by the USC Business Logic contract and called from the Core USC contract. When called, it triggers any DApp specific actions based on the new cross chain data. (e.g., executing transactions, updating state, or triggering events).

### Contract Code

{% code lineNumbers="true" fullWidth="false" %}

```solidity
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;

import "@gluwa/creditcoin-public-prover/contracts/sol/Types.sol";
import {ICreditcoinPublicProver} from "@gluwa/creditcoin-public-prover/contracts/sol/Prover.sol";

abstract contract UniversalSmartContract_Core  {
    bytes32 private constant USCStorageLocation =
        0x6873a84df95308b16f5c8aa284ac06f406edc8315b9626bdacc9b42f5ee1d200;

    struct USCStorage {
        mapping(address => mapping(bytes32 => bool)) usedQueryId;
    }

    function _getUSCStorage() internal pure returns (USCStorage storage $) {
        assembly {
            $.slot := USCStorageLocation
        }
    }

    function isQueryUsed(
        address user,
        bytes32 queryId
    ) public view returns (bool) {
        return _getUSCStorage().usedQueryId[user][queryId];
    }

    function _markQueryUsed(address user, bytes32 queryId) internal {
        _getUSCStorage().usedQueryId[user][queryId] = true;
    }

    /// @notice Processes a query from the prover and extracts data, deferring 
    /// action to child contract
    function _processOracleResults(
        address proverContractAddr,
        bytes32 queryId
    ) internal returns(
        bytes32 functionSignature,
        ResultSegment[] memory eventSegments
    ) {
        USCStorage storage $ = _getUSCStorage();
        require(
            !$.usedQueryId[proverContractAddr][queryId],
            "QueryId already used"
        );

        ICreditcoinPublicProver prover = ICreditcoinPublicProver(
            proverContractAddr
        );
        QueryDetails memory queryDetails = prover.getQueryDetails(queryId);
        ResultSegment[] memory resultSegments = queryDetails.resultSegments;

        require(resultSegments.length >= 8, "Invalid result length");
        functionSignature = resultSegments[4].abiBytes;

        uint256 resultLength = resultSegments.length;
        eventSegments = new ResultSegment[](resultLength - 5);
        for (uint256 i = 5; i < resultLength;) {
            eventSegments[i] = resultSegments[i];
            unchecked {
                ++i;
            }
        }       

        // Hook validation logic for implementation contract to use 
        _onQueryValidated(queryId, functionSignature, eventSegments);
    }

    /// @dev Must be implemented by child contract to handle validated data
    function _onQueryValidated(
        bytes32 queryId,
        bytes32 functionSignature,
        ResultSegment[] memory eventSegments
    ) internal virtual;
}

```

{% endcode %}

### Query Processing Flow

When a contract inherits from the Universal Smart Contract (`USC`), either directly through the Core contract or via `USC` Business Logic contracts, it gains the ability to **retrieve and interpret verified data from other blockchains**. This data is returned in a standardized format as `ResultSegment[]`. The contract can then use these Result Segments to **decide what actions to take on the current chain** (destination chain) based on its own business logic.

Below is the flow of events handled by the USC after a Creditcoin Oracle query has already been processed and result segments have been stored in the oracle's `prover contract`. An offchain worker listens for completed oracle queries and kicks off the USC process by triggering the Core USC's main function `processOracleResults.`    &#x20;

{% @mermaid/diagram content="sequenceDiagram
autonumber
participant OW as Offchain Worker
participant CUSC as Core USC
participant PC as Prover Contract
participant BL as USC Business Logic
participant USR as End User
OW ->> CUSC: Worker calls<br/>processOracleResults
activate CUSC
CUSC ->> PC: Retrieve result segments<br/> for query from oracle<br/> storage
PC ->> CUSC: Result segments returned
CUSC ->> CUSC: Interpret<br/> result segments
Note left of CUSC: Core USC checks for replay<br/> attacks, checks quantity and<br/>contents of result segments,<br/>and parses result segments<br/> into typed data
CUSC ->> BL: Core USC calls hook function (\_onQueryValidated),<br/> passing along interpreted result segment data
deactivate CUSC
activate BL
BL ->> BL: Cross chain data triggers<br/> DApp state updates
BL ->> USR: Events emitted and<br/>picked up by DApp<br/> Client
deactivate BL
" %}

## Result Segments

A `ResultSegment` is a **unit of ABI-encoded data** representing a specific piece of information returned by a `USC` query. Result Segments are stored collectively in an array that describes in a structured way the full transaction or event the query is targeting.&#x20;

Key characteristics:

* **ABI-encoded:** each segment contains a raw `bytes32` that encodes a Solidity type (e.g. `address`, `uint256`, `bool`). Users can convert that `bytes32` back to the target data type to retrieve its value.
* **Single value**: each segment encodes **a single piece of data** (e.g., the sender address, receiver address, token amount, or event signature) and *not* an entire transaction or event.
* **Sequence of related data**: result segments are stored in an array where their position in the array determines the meaning of their data.

> :information\_source: A `ResultSegment[]`  can have different length depending on the event or transaction which is being queried by the `USC`.

* **Deterministic:** for each `ResultSegments[]` array, there is always an event signature (before event data), which will be used by the implementation contract to determine correct actions when consuming the `ResultSegments[]`

### **Example Structure**

The  following are the Result Segments for a query which returns an `ERC-20` burn transaction event (transfer to `address(0)`):

<pre data-line-numbers><code><strong>0x0000000000000000000000000000000000000000000000000000000000000001
</strong>000000000000000000000000016e7bfe4a7213e18516ca0cb84cf2750d360b33
0000000000000000000000008928f05a197215ad7fffec1f0e4e9159e8c4c403
0000000000000000000000008928f05a197215ad7fffec1f0e4e9159e8c4c403
ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
000000000000000000000000016e7bfe4a7213e18516ca0cb84cf2750d360b33
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000032
</code></pre>

They are interpreted by the Prover contract as follows:

<table><thead><tr><th width="167">Segment</th><th>Segment Data (Hex)</th><th>Description</th></tr></thead><tbody><tr><td><code>resultSegments[0]</code></td><td>0x0000000000000000000000000000000000000000000000000000000000000001</td><td>Original transaction status.</td></tr><tr><td><code>resultSegments[1]</code></td><td>0x000000000000000000000000016e7bfe4a7213e18516ca0cb84cf2750d360b33</td><td>Sender address.</td></tr><tr><td><code>resultSegments[2]</code></td><td>0x0000000000000000000000008928f05a197215ad7fffec1f0e4e9159e8c4c403</td><td>Target contract address.</td></tr><tr><td><code>resultSegments[3]</code></td><td>0x0000000000000000000000008928f05a197215ad7fffec1f0e4e9159e8c4c403</td><td>Final contract address (which emit the event).<br><strong>Note:</strong> the final contract address and the target contract address can be different when the target contract interacts with other contract(s) to execute the transaction.</td></tr><tr><td><code>resultSegments[4]</code></td><td>0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef</td><td>The event signature which will be used by the implementation contract to determine action. In this case it is <code>Transfer(address,address,address).</code></td></tr><tr><td><code>resultSegments[5]</code></td><td>0x000000000000000000000000016e7bfe4a7213e18516ca0cb84cf2750d360b33</td><td>The <code>from</code> address in the transfer event.</td></tr><tr><td><code>resultSegments[6]</code></td><td>0x0000000000000000000000000000000000000000000000000000000000000000</td><td>The <code>to</code> address in the transfer event.</td></tr><tr><td><code>resultSegments[7]</code></td><td>0x0000000000000000000000000000000000000000000000000000000000000032</td><td>The <code>amount</code> in the transfer event.</td></tr></tbody></table>

## USC Extension Contracts

To ensure `USC` can be easily integrated into existing contracts, we provide a suite of **extension contracts** built on top of the **USC Core**. These extensions adapt `USC`’s capabilities to various real-world use cases, allowing builders to seamlessly embed cross-chain data into their logic. Common use cases include, but are not limited to:

* **Burn-and-Mint**: Mint a new token on one chain by reading a burn event from another, enabling cross-chain asset transfers.
* **Lock-and-Mint**: Mint a token on a destination chain based on a lock event on the source chain.
* **Cross-Chain Swap**: Swap token A for token B across different blockchains using verified event data.
* **Multi-Chain Loan Aggregation**: Aggregate repayment data across chains for credit scoring and underwriting.

> :information\_source: builders can also work directly with the **Core USC contract** if they wish to fully customize behaviors and integrate USC capabilities more tightly into their own business logic.

### Architecture&#x20;

<figure><img src="/files/I1PS0BcVSgxMF4bV5Q7s" alt=""><figcaption></figcaption></figure>

### Example

`MintableUSCBridge` is an extension of a `USC` Core contract that can be used by other `ERC-20` token to enable the crosschain movement of funds:

{% code lineNumbers="true" %}

```solidity
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;

import "@gluwa/creditcoin-public-prover/contracts/sol/Types.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {UniversalSmartContract_Core} from "./UniversalSmartContract_Core.sol";

abstract contract MintableUSCBridge is UniversalSmartContract_Core, ERC20 {
    event TokensMinted(
        address indexed token,
        address indexed recipient,
        uint256 amount,
        bytes32 indexed queryId
    );

    error InvalidFunctionSignature();
    error InvalidSegmentLength();
    error ZeroAmount();
    error InvalidBurnAddress();
    error InvalidRecipient();
    error QueryAlreadyProcessed();

    bytes4 private constant TRANSFER_EVENT_SIG = 0xddf252ad;
    mapping(bytes32 => bool) public processedQueries;

    /// @notice Executes USC query processing and mints based on extracted
    /// Transfer event
    function mintFromQuery(
        address proverContractAddr,
        bytes32 queryId
    ) external {
        if (processedQueries[queryId]) revert QueryAlreadyProcessed();
        processedQueries[queryId] = true;

        // Get function signature and event data
        (
            bytes32 functionSig,
            ResultSegment[] memory eventSegments
        ) = _processUSCQuery(proverContractAddr, queryId);

        if (bytes4(functionSig) != TRANSFER_EVENT_SIG)
            revert InvalidFunctionSignature();
        if (eventSegments.length < 3) revert InvalidSegmentLength();

        address from = address(
            uint160(uint256(bytes32(eventSegments[0].abiBytes)))
        );
        address to = address(
            uint160(uint256(bytes32(eventSegments[1].abiBytes)))
        );
        uint256 amount = uint256(bytes32(eventSegments[2].abiBytes));
        
        if (amount == 0) revert ZeroAmount();
        if (to != address(0)) revert InvalidBurnAddress();
        if (from == address(0)) revert InvalidRecipient();

        // Mint tokens on destination chain
        _mint(from, amount);

        emit TokensMinted(address(this), from, amount, queryId);
    }

    function _toAddress(bytes memory data) internal pure returns (address) {
        return abi.decode(data, (address));
    }

    function _toUint256(bytes memory data) internal pure returns (uint256) {
        return abi.decode(data, (uint256));
    }

    function _onQueryValidated(
        bytes32,
        bytes32,
        ResultSegment[] memory
    ) internal virtual override {       
        // More function below...
    }
 
    // More function below...
}

```

{% endcode %}

Builders can then inherit from `MintableUSCBridge` in their contracts to leverage the full capabilities of `USC`.

{% code lineNumbers="true" %}

```solidity
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "./MintableUSCBridge.sol";

contract YourToken is ERC20, MintableUSCBridge {  

    constructor(
        string memory name_,
        string memory symbol_
    ) ERC20(name_, symbol_) {
        // Your constructor business logic will be here...
    }

    function _onQueryValidated(
        bytes32,
        bytes32,
        ResultSegment[] memory
    ) internal override {
        // Your business logic will be here...
    }

    // More logic below...
   
}
```

{% endcode %}

## Next Steps

[DApp Design Patterns](/usc/usc-v1/dapp-builder-infrastructure/dapp-design-patterns.md)

*Check out* [*this tutorial*](https://github.com/gluwa/ccnext-testnet-bridge-examples) *for an example of how to use the Creditcoin stack to set up a decentralized trustless bridge.*


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.creditcoin.org/usc/usc-v1/dapp-builder-infrastructure/universal-smart-contracts.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
