Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Universal Data Feeds (UDF) is a next-generation, high-performance oracle network delivering hyper-fast, cost-efficient, reliable, and highly secure data feeds across multiple blockchains. UDF is the gateway to a new era of decentralized data, enabling real-time, high-frequency data delivery for DeFi, AI-driven applications, and Real-World Assets (RWAs). Supporting both push and pull models through dedicated marketplace contracts deployed on target chains, UDF ensures trust-minimized, low-latency updates while optimizing costs for decentralized applications.
For a deeper analysis, explore the details in our whitepaper.
UDF is a decentralized platform that connects on-chain and off-chain data to smart contracts, facilitating real-time data delivery from a variety of sources. UDF supports both EVM and non-EVM networks with a standardized delivery format that's designed to offer developers the flexibility to work across blockchains — providing a flexible, secure, and scalable infrastructure for creating data-driven solutions across diverse platforms.
By offering both Push and Pull Oracle delivery models, UDF allows developers to choose the most suitable method for their use cases. The Push model continuously updates data at predetermined intervals, while the Pull model gives applications the ability to retrieve data only when needed, optimizing efficiency and cost. UDF empowers decentralized applications to interact with real-time data, whether it’s for trading, cross-chain communication, or any other data-dependent function.
Additionally, UDF offers an intuitive and easy to use marketplace hub, a fully integrated ecosystem, acting as the entry point for users to access feeds. It provides users an all in one page to search for assets and manage their data feed subscriptions.
Entangle is a programmable interoperability layer that seamlessly connects all blockchains, data sources, and the real world, unlocking endless possibilities for future-ready decentralized applications. With Entangle, developers can easily launch, scale, and automate dApps, tokens and AI agents to interact across all blockchains without limitations.
Entangle’s toolkit provides developers access to ultra-fast, verifiable and programmable data from any source while enabling secure, trustless verification of complex off-chain software on-chain.
The technology stack serves as the foundational toolkit to build and scale mass-adoption ready applications including: DeFAI, Autonomous AI Agents, GameFAI, Real World Assets (RWA), DeSci and more.
For any inquiries or assistance, feel free to reach out to the Entangle team via the or the community via the channel.
Entangle's Pull Model ensures real-time data availability for user transactions, eliminating dependency on scheduled updates. This approach enhances the precision and reliability of blockchain applications by providing up-to-date information when needed.
When a transaction is initiated, the application requests the latest data directly from Entangle's decentralized sources. The data is retrieved instantly from a snapshot of verified data, and is authenticated within the same transaction. This ensures only accurate, real-time data is used, minimizes costs, and provides a reliable foundation for cross-chain applications.
When a dApp user wants to perform an action (e.g., open a leveraged position, or open a lending/borrowing position), the latest data feed proof is included in the transaction’s calldata. This action uses the PullMarketplace contract with the functions verifyWithFee
verifyAsSubscriberThe UDF oracle contract verifies signatures from the aggregator (transmitters). If valid, the feed value is accepted for that user’s transaction.
Each user pays a “verification fee” in native tokens (either through Pay-As-You-Go (PAYG) approach or predefined monthly subscriptions) to cover the cost of verifying the aggregator’s signatures and for the aggregator’s services. This suits lower-frequency but latency-sensitive use cases.
Minimal Data Staleness: The feed is as fresh as the aggregator’s most recent update with an update frequency of 200ms.
Lower Latency: Data is fetched during transaction creation and verified during execution, ensuring real-time accuracy.
Decentralized Access: Each user includes the proof, removing reliance on a single push node.
Fine-Grained Cost: Perfect for dApps using PAYG with moderate or unpredictable usage, as they pay only when they need the data.
Transaction Gas Costs: Each transaction requiring oracle verification incurs verification overhead.
Best for On-Demand Use Cases: Ideal for lending protocols, derivatives, and real-time settlement applications.

Entangle Blockchain Audit
TGE Audit
NGL Token Audit




High-Frequency Updates
Ultra-low-latency data updates by leveraging the Entangle Interoperable Blockchain and parallelized Layer 2 rollups. This means 200ms frequency for updates, short block times and reduced data latency. Ideal for high-speed trading, gaming, and AI-driven dApps.
Flexible Data Delivery Models
Choose exactly how your dApp receives oracle data. Select Pull for on-demand data access within transactions (PAYG or subscription), or Push for continuous updates using subscriptions with a fair cost-sharing model. Efficiently and precisely manage your data consumption needs.
Monetization Options
Control your oracle costs with flexible monetization options: Pay-As-You-Go for occasional data requests, Pull Subscriptions for predictable monthly access, and Push Subscriptions with deposits and cost-sharing. UDF provides affordability whatever your scale.
Intuitive Marketplace UI
Find, manage, and buy oracle feeds easily via the intuitive Marketplace UI. Quickly discover available feeds, customize subscription settings, and track your usage seamlessly from a single convenient dashboard. Accessing decentralized data has never been easier.
Customizable Update Parameters
Customize Push updates precisely with selectable price deviation thresholds (0.25%, 0.5%, 1%, 2.5%, 5%) and a default 24-hour heartbeat. Optimize data freshness and costs according to your app’s sensitivity, ensuring timely updates that are aligned with market movements and your specific needs.
Robust Security
Trusted data delivery using Byzantine Fault Tolerance (BFT). Decentralized transmitter nodes securely aggregate and cryptographically verify data, instantly detecting and penalizing malicious activity. UDF guarantees that your application always receives tamper-proof, validated data, even under adverse network conditions.

UDF provides both push and pull oracle solutions through dedicated marketplace contracts deployed on target chains. These contracts enable seamless data access tailored to different use cases. Pull model is like ordering data on demand; push model is like subscribing to a live feed.
At the core of this system is VerificationLib, a smart contract deployed on target chains to aggregate signed oracle votes into a single, consensus-driven value. It supports multiple data types, including price feeds and high-precision floating-point values, ensuring adaptability across various applications.
Both push and pull marketplace contracts operate alongside VerificationLib, leveraging its validation mechanism to guarantee the integrity and reliability of oracle data.
The Pull Model (On-Demand) fetches data only when required, ensuring it is up-to-date and reducing unnecessary updates. This method optimizes for accuracy and cost efficiency since the data is retrieved in real time based on the application’s needs. It eliminates delays caused by maintaining an on-chain state and is ideal for scenarios requiring real-time precision, such as DeFi trades or event-based actions.
On the other hand, for the Push Model (Continuous), data is delivered periodically, irrespective of immediate need, by maintaining an on-chain state. While it ensures that data is consistently available, this approach can introduce delays and inaccuracies due to the cost and technical limitations of frequently updating the on-chain state. As a result, this model is best suited for DEXs or stablecoin issuers requiring continuous price updates for liquidity pools at fixed intervals or price deviations.
UDF’s flexible monetization covers both subscription-based payments for push and pull feeds and a pay-as-you-go (PAYG) model for pull verifications, allowing users to choose plans based on their data needs and cost preferences. Pricing details are implemented via the PullMarketplace and PushMarketplace contracts.
The Push Model delivers regular oracle updates based on configurable price deviations (0.25%, 0.5%, 1%, 2.5%, 5%) or a default heartbeat (every 24 hours). Users deposit funds upfront, and subscription costs are shared among all users. This becomes increasingly cost-effective as more subscribers join the same feed.
The Pull Model lets users fetch oracle data directly within their transactions using either Pay-As-You-Go (PAYG) or monthly subscriptions. PAYG users pay a small fee per data request, ideal for occasional queries, while subscriptions offer unlimited data access at predictable monthly rates. Since data updates occur continuously off-chain, users only pay transaction verification costs, regardless of usage frequency.
Please refer to the UDF for in-depth details on how the prices are calculated.
PAYG (Pay-As-You-Go): Users pay a small protocol fee per request. This model is best for dApps with sporadic or low-frequency data needs, as costs scale with usage. Learn more about the exact formula we use in our . PAYG is solely programatic and cannot be purchased through the marketplace, learn more .
Subscription: Users can subscribe to continuous data access for a fixed periodic (e.g. monthly) fee, eliminating the PAYG protocol fee. This model is ideal for applications with consistent data consumption, reducing transaction overhead while ensuring predictable expenses. You can manage your subscriptions through the marketplace.
Fair Pricing Model: The protocol ensures pricing transparency by dynamically estimating costs based on gas prices, assets volatility, and cost sharing mechanisms. Users prepay for updates, there is a minimum deposit covering a few updates, and an estimated suggested deposit to cover for about 7 days of updates for the user to be able evaluate costs. Additionally we advise users to also consider the buffer deposit to cove for gas spikes and volatility.
Exclusive Access: On-chain price data is restricted to paying users, ensuring that only those contributing to the ecosystem benefit from updates.
Cost Sharing Mechanism: As more consumers subscribe to a feed, the cost per user decreases, making the model more economical at scale.
Gas prices vary significantly (e.g., Ethereum vs. an L2) and UDF must adapt. Fees vary based on the target chain’s gas costs; higher fees apply to expensive chains, while cheaper chains benefit from lower costs. Pricing is periodically adjusted to reflect changes in network fees, ensuring sustainable operations across all supported chains.
The Pull Model ensures that data updates are available in real-time, with minimal delay since users fetch and verify updates themselves, making it particularly useful for time-sensitive applications where immediate data updates are crucial, such as trading platforms or live dashboards.
In contrast, the Push Model, may introduce some latency since data is pushed periodically on-chain, an expensive process. While it is generally fast, it is better suited for applications where real-time updates are not as critical.
The Pull Model provides excellent scalability for applications requiring frequent updates. It is best suited for scenarios with lower transaction volumes, where the cost impact of update verification remains minimal.
On the other hand, the Push Model scales efficiently across all scenarios by providing much simpler flow for feed value retrieval, saving gas for end-users. It's particularly advantageous for applications with medium-to-high transaction volumes, where gas usage is a significant concern. Push users simply read the latest data from storage without incurring additional gas charges, making this model a more cost-effective solution for handling high transaction volumes.
In this section we will guide you on how to implement the Pull Model using the PAYG method.
In this section we will guide you on how to implement the Push Model.
In this step-by-step guide we'll go over how to manage your subscriptions using the Universal Data Feeds (UDF) . This process is an intuitive and straight forward process, follow the instructions below to get started.
Universal Token Standard (UTS)
Universal Data Feeds (UDF)
Entangle Interoperable Blockchain (EIB)
Security Audits
Join the Entangle Community
FAQ & Troubleshooting
Report an Issue
Become a Partner








Pull Model
Push Model


This page is under construction and will be available soon. We're exploring Solana connectivity as a possible addition in a future release. We appreciate your patience and encourage you to follow us on social media to stay updated.
This page is under construction and will be available soon. We're exploring Solana connectivity as a possible addition in a future release. We appreciate your patience and encourage you to follow us on social media to stay updated.
The diagram above illustrates the architecture of Entangle's Universal Data Feeds (UDF), a system designed to ensure secure, efficient, and scalable delivery of verified data across blockchains. At its core, UDF integrates data Aggregation, Attestation, and Updating with a convenient Data Delivery method, supporting diverse use cases while ensuring reliability and accuracy.
The process begins with data collection, where the raw data is fetched from reliable, high-quality Data Providers at fixed intervals. This data is encapsulated into a structured format containing key attributes such as the feedKey, value, and timestamp. To ensure the integrity and authenticity of the data, it is signed using ECDSA signatures, effectively securing it from tampering. Once packaged, the signed data is sent as an update transaction to the Oracle Chain, where it undergoes aggregation and attestation (validation).
The Oracle Chain serves as the processing hub, consisting of two key modules. It offloads data collection and processing to L2 auxiliary blockchains, enabling fast, distributed data collection and parallel processing for real-time data feeds. The Aggregation module consolidates inputs from multiple publishers into a coherent dataset. Subsequently, the Attestation module creates on-chain verifiable proofs, certifying the accuracy and reliability of the aggregated data on the target chain.
Users can then use the Update Provider Service which is a service to generate snapshots of data which are maintained on the Oracle Chain for consistency and retrieved via a Delivery Method (i.e., an API) for accessibility. It provides a unified reference point, eliminating the need to interact directly with the Oracle Chain.
On the Target Chain, UDF plays a pivotal role in managing delivered data. It enables decentralized applications to access and utilize verified data directly in their logic. It also facilitates verification of data using the VerificationLib which is the smart contract library that handles validating data on target chains. These measures ensure that all signatures are valid, votes are unique, and the data has met consensus thresholds. For numerical feeds, the verification process aggregates the data into a final state, often using statistical methods like medians to derive a reliable result.
UDF integrates EIB with L2 solutions for scalability, performance, and cost optimization. This approach allows relaying aggregated data snapshots to each target chain. The L2 network is parallelized and synced with EIB to handle specific tasks (aggregator rollups) and offload mainchain overhead while enabling unlimited horizontal scaling.
EIB's robust consensus mechanism (Tendermint BFT) that finalizes data more rapidly than many L1 networks is leveraged by the L2 solutions as a trusted layer to periodically commit their state. This lets the L2 solutions inherit EIB’s security while achieving greater compute bandwidth and lower costs, enabling near real-time data updates.
Additionally, unlike traditional L2 processes, UIP's internal utility networks bypass full block finalization for messages that do not involve value transfers, relying solely on source network finalization to speed up delivery.
In short, UDF leverages the EIB, a specialized blockchain, for orchestrating:
Cross-Chain Consensus
Validator Set & Finality
Performance & Lower Costs
A decentralized network of lightweight transmitter nodes retrieves raw data from various sources and cryptographically signs each update before submission to the oracle chain. These signatures collectively form a Byzantine-tolerant proof, ensuring the integrity and validity of the data. Signatures are merged into a final proof, which is used for validation on external blockchains and off-chain applications.
Transmitters must stake the native EIB tokens to participate in the network, aligning incentives with honest operation. Staked assets act as a security deposit, misbehavior, downtime, or incorrect data submissions result in slashing penalties. This mechanism prevents Sybil attacks while ensuring that only committed, well-incentivized nodes contribute to UDF’s data integrity.
Generate signatures on data updates, contributing to a trust-minimized consensus.
Are monitored and rotated, with penalties for downtime or producing inconsistent signatures.
Once finalized, the data snapshot is posted on the EIB/L2 oracle chain, ensuring accuracy through multiple layers of signed votes. The Finalized Data Snap service continuously synchronizes with the oracle chain, providing real-time oracle data for users and push nodes to retrieve validated oracle data efficiently.
The final snapshot is a cryptographically verifiable object that includes:
Aggregated Value (e.g., BTC/USD = 100,000)
Timestamp
Transmitter Signatures
Optional Extra Metadata (standard deviation, volume data, etc.)
UDF supports two primary delivery models, Push and Pull. In the Push Model, updates are actively sent by the Push Publisher (i.e., Push Node) to the target chain, while in the Pull Model, users initiate requests for the latest data snapshots and append verification details to their transactions.
UDF offloads computationally intensive tasks such as aggregation and validation to the Oracle Chain, allowing the system to minimize on-chain costs while ensuring rapid updates. Additionally, a robust verification process guarantees that the data remains tamper-proof throughout its lifecycle, allowing cross-chain deployments without comprising data integrity.
UDF is an essential pillar in infrastructure for modern blockchain ecosystems. It excels in delivering accurate, timely, and secure data to decentralized applications, positioning it as a leading solution for cross-chain data delivery.
This page provides step-by-step instructions on how to delegate tokens to your validators. This is a simple and quick process with our intuitive user interface, see below for more information.
Navigate to the Entangle Hub and click the ‘Connect Wallet’ button. Approve connecting to the platform and adding Entangle Mainnet to your wallet.
UDF is a modular oracle protocol that allows smart contracts to access both Web2 and Web3 data seamlessly, delivering authenticated, customizable data on-demand. It offers developers the flexibility to choose between push and pull data feeds, optimizing cost-efficiency and performance. UDF standardizes data streaming into a universal format, enabling dApps to easily connect with data sources across any EVM and non-EVM network.
Existing dApps or developers planning to build one requiring an oracle for price data, can integrate our data feeds into your smart contracts. Get started by taking a look at one of our guides.
UTS offers three distinct types of token deployments for manually deployed tokens, each with its own characteristics.
Can be launched by anyone
Not officially registered on the Entangle platform
Token and connector contracts can be modified by deployers as needed
Offer maximum flexibility but may lack official recognition
Deployed through Factory contracts
In the case of UTS Connectors deployment:
Underlying tokens remain owned by third parties
Provides a standardized deployment process
Registered and vetted by Entangle
Official recognition on the Entangle platform
Increased trust and credibility for token projects
For more detailed information about how to verify your token on Entangle platform please visit or check out the guide.
In this step-by-step guide we'll go over how to check if a subscription to a data feed is still active using the Universal Data Feeds (UDF) hub. This process is an intuitive and straight forward process, follow the instructions below to get started.
Navigate to https://udf.entangle.fi/ and click on "Connect Wallet" at the top right to connect your wallet.
In this step-by-step guide we'll go over how to connect your wallet and search for feeds to find the one that you'll need using the Universal Data Feeds (UDF) . This process is an intuitive and straight forward process, follow the instructions below to get started.
Universal Tokens are cross-chain native tokens secured by UIP. The Universal Token Standard (UTS) enables token developers to launch new or expand existing tokens across all major blockchains through a within a few clicks.
UTS revolutionizes cross-chain management, giving developers the power to effortlessly expand their assets across popular chains while keeping full control of their smart contracts — UTS requires no special permissions or rights. Take your token to the next level with UTS, the ultimate tool to seamlessly scale and control, and unlock the infinite possibilities of the interconnected web.
UDF’s oracle contracts provide two distinct interfaces, Pull and Push, designed to deliver flexible data access for dApps. Please refer to the section for implementation details.
The Pull interface enables on-demand verification within user transactions, supporting both Pay-As-You-Go (PAYG) and subscription-based models. The Push interface offers subscribed users seamless access to pre-verified, on-chain data updates.
These interfaces, detailed in the table below, leverage VerificationLib to ensure data integrity and adaptability across diverse use cases.
UTS provides a cost-efficient and transparent fee system for cross-chain transactions, ensuring users pay only for the resources required. Its optimized structure minimizes costs while maintaining reliable and secure token transfers.
Cross-chain transactions require gas fees on both source and destination networks, covering computational tasks like minting, burning, locking, and unlocking. Gas limits dynamically adjust to real-time network conditions to prevent overpayment.
Destination Gas Limit (dstGasLimit):
This page is under construction and will be available soon. We're exploring Solana connectivity as a possible addition in a future release. We appreciate your patience and encourage you to follow us on to stay updated.
The UTS Connector is designed to add cross-chain bridging capabilities to your tokens. If you want to add bridging capabilities to an already deployed token, the Connector is the solution. Once deployed, it allows tokens to be locked on one chain and unlocked on another, facilitating the creation or release of UTS tokens. You can use the provided contract as it is or adjust it to suit your needs. To get started, simply deploy the Connector, and you'll be able to bridge tokens seamlessly.
Below are two types of Connectors you can choose from based on your needs:
Connector with
This page provides step-by-step instructions on how to undelegate tokens from your validators. This is a simple and quick process with our intuitive user interface, see below for more information.
If you want your token to be traded against other assets, you'll need to create a liquidity pool. A liquidity pool is a collection of cryptocurrency tokens locked in a smart contract on a decentralized exchange (DEX). It serves as a reserve, enabling users to exchange tokens directly using the pool's locked tokens without requiring a matching buyer or seller. Alternatively, you can watch the video above for a voiced over walkthrough.
EIB is at the core of Entangle's ecosystem. Built on the Cosmos SDK and powered by the Ethermint framework, this highly customised Layer-1 blockchain is designed to function as a central hub, coordinating cross-chain message processing and transaction validation across networks. It employs Merkle tree structures for efficient and secure data storage and retrieval, ensuring data integrity and optimizing storage for high-volume cross-chain applications.
As the ultimate Web3 unification layer, Entangle brings together all blockchains, applications, tokens, and data sources into one seamless ecosystem. It’s built to accelerate scalability and fuel mass adoption, empowering developers and users alike to leverage cutting-edge technology that transcends the limitations of fragmented systems.
We invite everyone to join our growing ecosystem and leverage innovative technology that accelerates scalability and adoption. Please fill up the partnership request here:
If you encountered any issues during the integration process, or noticed a bug while integrating contracts, we’re here to help — simply submit a request on . Please provide as many details as possible, including any error messages or unexpected behavior you observed. Our team will assist you in identifying the problem and guiding you through a solution.
UTS Router
0x5Ac3F749C209f0fdc9E1869Dcb9d109E75e48c61
UTS Registry
0x481d89337aBcb336bdfA422e85af8aa88919b342
UTS Factory
0x123d82338E00AecB432F526a8aEB9442D48e4412
UTS Deployment Router
0x587Bc1BB463F309c4511c36877825D6ec196f6A6
Arbitrum
Mantle
Binance Smart Chain
xLayer
CoreDAO
Base
Optimism
Avalanche
Polygon
Ethereum
Offers a balance between customization and standardization

Developer Guides



UDF Oracle
Core system for data aggregation, signature verification, and on-chain publication.
Pull Model
Data-verification method for each user transaction, containing oracle votes.
Push Model
Data publication method where nodes regularly push updated values on-chain.
Transmitters
Decentralized agents that sign and attest to data, forming a security layer for aggregated values.
Entangle Interoperable Blockchain (EIB)
A specialized oracle chain that coordinates data availability, finalization, and bridging logic.
Layer 2 (L2)
Layer 2 rollups that further boost throughput and reduce latency of the EIB.
Finalized Snapshot
A canonical data set aggregated from various sources & attested by transmitters.
VerificationLib
A smart contract library that validates oracle votes for secure data feeds on target chains.
Byzantine Fault Tolerance (BFT)
An algorithim for fault tolerance used by transmitters to sign data updates.








Custom Data Feeds
Fetch Data via Pull Model (PAYG)
Fetch Data via Pull Model (Subscription)
Fetch Data via Push Model




EVM Smart Contracts
Solana Smart Contracts


Initial Setup
UTS Connector
UTS Token



Ideal for dApps with sporadic data needs.
Pull
verifyAsSubscriber()
Confirms the user has an active pull subscription, verifies votes, and returns the aggregated data value without additional protocol fees.
Suited for protocols (e.g. lending) needing frequent, real-time price checks with predictable costs.
Push
getFeedPrice()
Allows subscribed users to read the latest push feed data stored on-chain without verification overhead.
Suited for DEXs or stablecoin issuers requiring continuous price updates for liquidity pools.
Multiple Chains: Each supported chain has UDF Oracle Push and Pull contract deployments. See the data endpoints page for more information.
Push Node Authorization: Authorization mechanisms manage the push nodes that are permitted to submit updates on each chain, ensuring controlled data propagation through access control mechanisms.
Cross-Chain: For multi-network deployments, EIB supports two sync methods. In the subscription model, chains listen for finalized updates. In the push model, updates are actively sent when values change or a heartbeat is triggered. Both approaches keep data consistent with minimal delay.
UDF supports seamless omnichain data delivery through two models. In the subscription model, transmitter nodes monitor the Entangle Interoperable Blockchain (EIB) and relay oracle updates to subscribed chains as they’re finalized. This ensures efficient propagation with minimal overhead. In contrast, the push model takes a more active approach. Specialised nodes listen to live oracle emissions from L2, compare them with on-chain values, and trigger updates when deviation thresholds or heartbeat timers are met. Each target chain is updated independently, keeping data in sync and available in real time for multi-chain dApps.
Pull
verifyWithFee()
Processes the protocol fee for the PAYG pull model during a transaction, verifies transmitter votes, and returns the aggregated data value.
Destination Gas Price (dstGasPriceInWei): Real-time gas price updated by UTS backend.
Native Token Prices: Prices of source and destination native tokens (e.g., ETH, BNB) in USD are tracked for accurate fee estimation.
Fees for transferring additional data, calculated based on the data size (in bytes) and destination gas price.
Protocol fees for managing token transfers, covering operational costs of the Entangle Bridge.
Source Network Execution: Gas fees for burning or locking tokens.
Destination Network Execution: Gas fees for minting or unlocking tokens.
Protocol Fee: A flat fee deducted during the transaction.
UTS minimizes on-chain workload by performing calculations off-chain. Backend services compute values like dstGasPriceAtCurrentNative and dstPayloadPriceAtCurrentNative before passing them to the contract, reducing unnecessary costs for users.
codebaseFeeAtCurrentNative = dstGasLimit * dstGasPriceInWei * (dstNativePriceInUSDe18 / curNativePriceInUSDe18)codepayloadPriceAtCurrentNative = payloadLength * dstPricePerByteInWei * (dstNativePriceInUSDe18 / curNativePriceInUSDe18)Network in which you are receiving the feed on
Mode type for the particular feed (Push or Pull)
Deviation selected for the feed
Status of the feed (If the feed is active or inactive)
Expiration date of the feed



Contract Sovereignity
Deploy, mint, burn, lock, or unlock tokens while retaining full control over your token.
Logic Customization
Easily add custom logic and cross-chain messaging to your token.
Dual Proof of Stake Mechanics
Transparent, verifiable, and tamper-proof, operating without third parties.
Gas Management
Set custom gas limits per blockchain with auto adjusting fees based on network conditions.
Low Cost Bridging
Simple and cost effective token bridging. Tokens mint or unlock automatically on the target chain.
No Liquidity Pools Required
Tokens move directly cross-chains without locking funds, reducing complexity and costs.
Connector with Mint/Burn Mechanism: Choose this option if you're able to provide mint and burn access to the Connector. It allows the Connector to mint and burn tokens during the bridging process.
Connector Based on Native Currency: This type of Connector is built over the native currency of a blockchain, using the native token as the underlying asset for bridging.


Ensure that you have imported your token into your wallet. If you're using MetaMask, you can follow their official guide. For other wallets, please refer to their respective documentation for transferring tokens.
That’s it! You’ve successfully created a liquidity pool on a DEX, allowing your users to exchange tokens seamlessly.
The fastest way to start exploring and deploying omnichain tokens is through the UTS portal found via the navigation bar on the Entangle website. Deployment through the UTS portal is referred to as automated deployment.
Automated deployment with UTS is specifically designed to simplify the expansion of existing tokens across multiple blockchains while providing the ability to launch new universal tokens. Using the intuitive UTS interface developers can effortlessly configure token details — such as name, ticker, total supply, decimals, and bridging mechanisms. Once the setup is verified, the system handles all deployment tasks, including creating contracts and allocating the token supply across the chosen chains. The UTS interface is designed to minimize errors and streamline processes.
This section provides information on how to launch new tokens, connect existing tokens, and other functionalities related to the UTS infrastructure using the UTS interface.
For voiced over guides with full walk throughs you can check out our page.
Developers who require greater control and flexibility can customize their token via smart contracts, a process referred to as manual deployment. It allows direct interaction with the Entangle framework and involves deploying token smart contracts to individual blockchains, requiring developers to define all token parameters, and manage bridging mechanisms programmatically.
While this approach offers more control, it requires a deeper understanding of blockchain infrastructure and additional effort to ensure consistency and security across networks. Manual deployment is ideal for projects requiring customized token behavior and complete flexibility in management.
This section provides information on how to create, deploy new tokens, or connect existing tokens to the UTS infrastructure manually.
The Universal Token Standard is built for seamless deployment, management, and cross-chain interoperability of omnichain tokens. Integrated with the Entangle Bridge protocol, UTS enables efficient, secure cross-chain transactions using a flexible framework of smart contracts and mechanisms like mint-and-burn and lock-and-unlock.
Each token has its own token pool, which simplifies cross-chain transfers by handling the necessary token actions. Token pools act as a secure layer that limits how often tokens can be transferred, adding extra protection. They are set up to either "lock" or "burn" tokens on the source chain and then "unlock" or "mint" them on the destination chain. This results in four possible combinations: Burn and Mint, Lock and Mint, Burn and Unlock, and Lock and Unlock.
Token bridging is the process of transferring tokens from one blockchain to another, enabling interoperability and seamless interaction across multiple networks. Since blockchains operate independently and cannot directly communicate with one another, token bridging serves as a mechanism to "move" tokens between them.
UTS simplifies token bridging by integrating with the Entangle Bridge Protocol. This system automates the mint and burn or lock and mint processes, enabling developers to focus on token functionality while ensuring secure and efficient cross-chain transfers. With UTS, bridging is decentralized, trustless, and designed to minimize the complexity typically associated with multi-chain operations. UTS offers two modes, expert mode, and simple mode.
When transferring tokens there are several different models users can use to handle tokens on source and destination chains. In this section each type of model is explained.
The burn and mint model maintains token supply consistency by burning tokens on the source chain and minting an equivalent amount on the destination chain. This preserves total supply during cross-chain transfers and supports custom tokenomics.
In the lock and mint model, tokens are locked on the issuing blockchain, and fully collateralized wrapped tokens are minted on the destination blockchain. These wrapped tokens, with burn-and-mint functionality, enable cross-chain transfers and extended functionality.
The burn and unlock model returns tokens to their issuing blockchain by burning them on the non-issuing source chain and releasing an equivalent amount on the issuing destination chain.
Unlock scheme requires to have enough liquidity on destination side to perform bridge operation, make sure to check this before bridging.
The lock-and-unlock model enables cross-chain transfers for non-upgradable tokens by locking tokens on the source chain and releasing an equivalent amount on the destination chain. This method maintains token integrity and requires careful liquidity management to ensure balance across chains.
Unlock scheme requires to have enough liquidity on destination side to perform bridge operation, make sure to check this before bridging.
The UTS Token is designed to be bridgeable by default. Once you create it, you can immediately begin bridging it across networks. You can use the provided implementation as-is or customize it to suit your needs.
In this section, we’ll provide examples of two types of tokens: a simple token and a token that uses cross-chain messages, allowing you to bridge tokens to multiple addresses in a single transaction.
UDF has an easy to use and intuitive hub for quickly managing all of your data feeds. This section walks you through everything you need to know to start using it. Whether you’re exploring data feeds or managing your existing subscriptions, you’ll find step-by-step guides to help you along the way. We’ve kept things clear and practical so you can focus on building without getting lost in the details.
The Universal Token Standard (UTS) is a framework for deploying and managing omnichain tokens across multiple blockchains. It uses mint-and-burn or lock-and-unlock mechanisms to ensure consistent token supply during cross-chain transfers. Integrated with the Entangle Bridge, UTS supports EVM and non-EVM environments, enabling seamless token management and interoperability across diverse blockchains.
On chains where token already exists UTS offers a lock-and-unlock bridging mechanism, similar to mint-and-burn but requiring additional liquidity for bridging. This guide will walk you through how to expand existing tokens using UTS.
First, navigate to UTS via the navigation bar on the Entangle website.
Enter the address of the tokens that are on different chains which you wish to link. You can select the chain by clicking on the icon. In this example we're using the USDT addresses on BNB and Polgygon.
Make sure you read and understand the messages before checking them. Once you're ready click the "Import Tokens" button to move on.
Now you should decide which networks to deploy on. Here you can choose your prefered bridging mechanism but remember the total supply will remain unchanged since the token already exists. If you don't want to add new chains (as in our example) click "Skip & Deploy".
After signing the transaction to confirm deployment of the smart contract you be automatically redirected to the next step.
This section provides information and guides for developers intending to use Entangle's Interoperable Blockchain (EIB). If you have any questions or need assistance, please feel free to use our contact form to connect with an expert on our team.
Each transaction processed on the network requires thorough validation to ensure compliance with established rules and protocols. Validators are responsible for verifying these transactions, ensuring that all operations are accurately checked, recorded, and aligned with the network’s governing principles.
Validators use the native token, to participate in securing the network. Validators stake the native token to gain voting power, with greater stakes or delegated tokens enhancing their influence over the network's security and consensus. This staking mechanism ensures that validators have a vested interest in maintaining the network's security.
Validators actively monitor the network for signs of malicious activity. Their vigilance is crucial in preventing attacks, double-spending, or other behaviors that could compromise the blockchain's security.
Validators are incentivized through rewards, which come from gas fees generated by the transactions they validate. This reward mechanism is designed to acknowledge and compensate validators for their crucial contributions and encouraging active participation.
Validators are held accountable through a robust slashing mechanism that discourages malicious behavior and ensures network security
If a validator signs two conflicting blocks, a percentage of their tokens are slashed.
Validators that sign fewer than 50 of the last 100 blocks will have a portion of their staked tokens slashed and be temporarily "jailed" for a set period.
Creating a new token is a simple process with our streamlined and intuitive interface. This guide will walk you through how to create a new token using UTS. Alternatively, you can watch the video above for a voiced over walkthrough.
Fill in all of the necessary information (Ticker, Name, Total Supply, Mechanism, Decimals, Contract Address) for the token you're launching, and then click "Preview Setup" to proceed to the next step.
The button may be replaced with a connect your wallet button if you have not connected it yet. In which case please connect it first, then the preview button will become available.
Now you should decide networks for deployment and how to initially split the total supply between chosen blockchains. You can decide to split it evenly, or define a custom value. If you decide to define a custom value, make sure that the total value of tokens on each chain equals the initial supply. The total deployment fee will be automatically updated and displayed in the native currency of the connected blockchain network.
When you're ready click "Setup & Deploy" and sign the transaction to confirm deployment of the smart contract. After a few seconds the deployment will be completed and you will be automatically redirected to the next step.
UTS supports launching to multiples chains. The process is similar to the steps described above, for a full breakdown and walk through you can watch the video below.
Entangle's Push Model delivers continuous, automated updates for critical data, such as prices and asset values, across multiple blockchains. These updates are delivered periodically, making them ideal for scenarios requiring data at set intervals or infrequently, without the need for user requests. This is particularly useful for applications like monitoring dashboards or dApps that display market prices or feed metrics, ensuring optimal performance without user-initiated transactions.
The Push Model transmits verified information across blockchains when specified deviations occur and at set heartbeat intervals. All updates undergo a secure, consensus-driven verification process before being published on-chain, ensuring data accuracy and security.
UDF push nodes post the current aggregated feed to a target chain at given price deviations of 0.25%, 0.5%, 1%, 2.5%, or 5% depending on the deviation parameter selected by users, or based on a predefined heartbeat of 24 hours to guarantee sufficient data freshness even when price variations are minimal.
This means users with stricter update frequencies bear a proportionally higher cost, increasing a user's cost share. This ensures that users requiring frequent updates pay more, while those with wider deviation tolerances contribute adequately less, maintaining fairness and economic efficiency.
The UDF push oracle contract holds the verified value so that authorized users’ dApps can read it without paying additional verification overhead. This is managed by the PushMarketplace contract, and whitelisted addresses can read via the getFeedPrice function.
Subscribed users can share the cost of running the same feed which guarantees cost efficiency for users. As more consumers subscribe to a feed, the cost per user decreases, making the model more economical at scale. Consider checking out our for indepth details on the cost sharing formula.
UDF charges a small percentage on deposits to the push marketplace contract. The push oracle updates cost is shared across subscribed readers, making it cheaper for the UDF push oracle user base wanting continuous access.
Gas Efficiency for Consumers: Price reads are restricted to subscribed users and data updates are shared across multiple users, reducing individual verification costs.
Cost Efficiency for Consumers: Cost sharing mechanisms amongst subscribed users makes subscriptions cost efficient.
Slight Delay in Data Freshness: Updates happen periodically (price deviation triggers or time-based heartbeats).
Best for Continuous Data Access
In this step-by-step guide we'll go over how to subscribe to a data feed using the Universal Data Feeds (UDF) . This process is an intuitive and straight forward process, follow the instructions below to get started.
Fee calculation and gas estimation help you understand the costs involved in cross-chain transactions and deployments. These processes account for things like gas limits, payload size, and currency prices on the source and destination chains to ensure your operations run smoothly. Entangle provides to estimate fees for bridging and deploying tokens, making it easier to plan and execute your transactions.
This page explains the logic for calculating transaction fees and estimating gas costs for cross-chain operations, including base fees, payload costs, and optimization strategies handled off-chain by the backend.
Caution, the liquidity you transfer to the UTS Connctor is non-refundable.
Adding liquidity to the UTS connector is simple, this guide will explain how it's done.
Before starting any of the examples, you’ll need to set up your environment. We recommend using the development environment. If you choose to use it, please follow the .
Once your development environment is set up, install the core dependencies using the following commands:
After initializing an empty Hardhat project and installing dependencies, you are ready to write your token or connector. Next, try creating a or a .


Mint & Burn Connector Scheme
Lock & Unlock Connector Scheme
Connector Over Native Currency







Token Deployment Types
Create a Custom Token
Factory Blueprint Deployment
Examples




Simple Token
Token with Messages


Accessing Feeds
Subscribe to a Data Feed
Check Subscription
Manage Subscription
Renew Subscription





Manual Deploy
Bridge SDK
Token Verification
Fees Calculation & Gas Estimation Logic
Estimations






dstGasPriceInWei: Stored in the contract and updated by the backend; represents the gas price on the destination network based on chainId.
dstNativePriceInUSDe18: Stored in the contract and updated by the backend; indicates the USD price of the native currency on the destination network, depending on chainId.
curNativePriceInUSDe18: Stored in the contract and updated by the backend; reflects the USD price of the native currency on the source network.
To calculate the base fee in terms of the current native currency:
This formula determines the amount of native currency required on the source network to execute the transaction (excluding payload), which is then compared to msg.value.
To calculate the cost related to the payload, use these parameters:
payloadLength: User-defined length of the payload in bytes.
dstPricePerByteInWei: Cost per byte of payload in the native currency on the destination network.
dstNativePriceInUSDe18: Represents the USD price of the native currency on the destination network.
curNativePriceInUSDe18: Represents the USD price of the native currency on the source network.
The payload cost calculation in the current native currency is:
This formula calculates how much native currency is needed on the source network to cover the payload cost, which is also compared to msg.value.
To determine the total required amount, sum baseFeeAtCurrentNative and payloadPriceAtCurrentNative, then compare to msg.value. Note that users may incur small losses if dstGasLimit exceeds the actual gas consumed.
To optimize the process, the backend handles calculations off-chain to minimize contract updates across different destination networks:
dstGasPriceInWei
dstPricePerByteInWei
dstNativePriceInUSDe18
curNativePriceInUSDe18
Using these parameters, the backend performs these calculations:
Calculate dstGasPriceAtCurrentNative:
Calculate dstPayloadPriceAtCurrentNative:
The backend then sends only the dstGasPriceAtCurrentNative value to the contract. Optionally, it can also send dstPayloadPriceAtCurrentNative if a dynamic price per byte is preferred; otherwise, a static price is more efficient.
To navigate with HEX addresses you can use Metamask or other EVM wallets, with following parameters.
Network Name
Entangle Mainnet
Default RPC URL
https://json-rpc.entangle.fi/
Chain ID
33033
Currency Symbol
NTGL
Network Name
Entangle Testnet
Default RPC URL
https://34.174.174.102:8545/
Chain ID
33133
Currency Symbol
NTGL
npm i @entangle-labs/uts-contractsnpm i @openzeppelin/contractsdstGasPriceAtCurrentNative = dstGasPriceInWei * dstNativePriceInUSDe18 / curNativePriceInUSDe18dstPayloadPriceAtCurrentNative = dstPricePerByteInWei * dstNativePriceInUSDe18 / curNativePriceInUSDe18baseFeeAtCurrentNative = dstGasLimit * dstGasPriceInWei * dstNativePriceInUSDe18 / curNativePriceInUSDe18payloadPriceAtCurrentNative = payloadLength * dstPricePerByteInWei * dstNativePriceInUSDe18 / curNativePriceInUSDe18entangled debug addr <address>entangled keys add <key_name> --keyring-backend fileentangled keys add <key_name> --keyring-backend file --recoverentangled keys unsafe-export-eth-key <key_name> --keyring-backend fileentangled q account <address> For "Pull mode" choose the desired duration. Your subscription will remain active for the selected duration extension and will end on the expiry date unless extended beforehand by depositing additional funds, allowing you to renew it seamlessly.
You can deposit any amount, as long as it meets the minimum required. The app will automatically calculate your subscription period based on the amount you choose to deposit.
Import your underlying token for UTS Connector in Metamask. You may follow the official guide to import any token.
That's it! Now the connector has liquidity and is ready to be unlocked in bridging transactions. Go to Entangle bridge page to perform your first bridge!
Architecture
Fee Components
Developer Guides
User Guides
Contract Addresses
Frequently Asked Questions













Custom data in data feeds refers to user-defined information that is integrated into a data feed to meet the specific needs of a dApp or smart contract. This enables developers to pull tailored data from both on-chain and off-chain sources, facilitating more accurate and relevant decision-making and operations. With UDF, users can easily expand the data available to their applications by incorporating new data types into the Entangle UDF infrastructure.
Integrating New Crypto Assets: Add new cryptocurrency price feeds to ensure decentralized exchanges, lending platforms, or wallets always have the latest market prices.
Real-World Asset (RWA) Integration: Introduce real-world assets like commodities, real estate, or physical goods into the decentralized ecosystem, enabling their tokenization and trading.
Proof of Reserves Integration: Add external data feeds to verify asset reserves, providing transparency and confidence in the backing of digital assets or financial platforms.
To integrate new data into UDF, and provide the following information:
Initial Data with Examples: Share real sample data that will be processed during the integration.
Data Processing Requirements: Outline any necessary transformations or formatting for the data to be delivered on-chain.
Update Frequency Suggestions: Recommend the desired update frequency to tailor the integration to the protocol’s needs.
This section outlines the process of integrating a new data feed into UDF. The default integration flow ensures smooth onboarding of new data sources and utilizes UDF’s capabilities for fetching, parsing, and distributing data on-chain.
The Rest URL template defines how data is fetched from external sources. You will need to create a URL template that points to the endpoint of the data provider. The template uses Go text syntax, with placeholders such as DataKey and SourceID. Example for Binance API:
This URL template is inserted into the configuration file, allowing Entangle's backend to fetch the necessary data.
The main idea of this connector is to have a possibility to bridge native currency between chains.
Create contract with name UTSConnectorNativeShowcase .
You need to import:
UTSBase is our main contract that should be inherited by any Token or Connector.
In this implementation we also used Ownable
We are starting with defining contract and dependencies. As access control we are choosing Ownable.
Also we need to define NATIVE_TRANSFER_GAS_LIMIT some errors and address converter library.
Then we need do define constructor. Since it is connector, we need _nativeCurrencyDecimals( since our asset for bridging is native currency, we can provide a null address, but meaningful decimals value still required). We need also setup _router address, that can be found .
_allowedChainIds are simply whitelist of chain id's, where you are allowing to bridge tokens.
_chainConfigs is array of settings responsible for bridge settings. We described this config .
In constructor we need to call __UTSBase_init function to initialize UTS Base contract. Also we need to set router and chain config.
_nativeTransferGasLimit serves for defend your and your users gas to avoid attack vector over too complicated or useless receive function at the receiver address.
Lets start with defining 2 view functions that will help us to track balance of this connector and also decimals of native currency and also receive function that will allow our connector to receive native currency.
After this step we need to override 3 functions: _mintTo, _burnFrom and _authorizeCall.
In the outbounding transaction we need to ensure that msg.value we are going to accept is greater than amount, to ensure that we received enough native currency.
In the inbounding transaction we need to ensure that we have enough native currency on connector balance and as a transfer method we will use raw call, since we want also provide restricted gas value.
Don't forget to check success.
Last but not least we need to override one our internal function that is responsible for sending request to router for bridge proposal. Here we need modify value of this message. Main fee asset in our protocol is native currency, and we need to transfer some fee to router to pay fee. If we will leave as it is, it will transfer whole msg.value to router. It is incorrect logic in this case, so we need to substruct our amount that will be locked on the connector balance.
So, our contract is ready, now it can be deployed on networks and we can start bridging tokens between each chain.
Create contract with name UTSConnectorLockUnlockShowcase .
You need to import:
UTSBase is our main contract that should be inherited by any Token or Connector.
In this implementation we also used Ownable, Pausable and IERC20Metadata
We are starting with defining contract and dependencies. As access control we are choosing Ownable.
Then we need do define constructor. Since it is connector, we need underlyingToken_(the token that will be bridged through UTS protocol). We need also setup _router address, that can be found .
_allowedChainIds are simply whitelist of chain id's, where you are allowing to bridge tokens.
_chainConfigs is array of settings responsible for bridge settings. We described this config .
In constructor we need to call __UTSBase_init function to initialize UTS Base contract. Also we need to set router and chain config.
After this step we need to override 3 functions: _mintTo, _burnFrom and _authorizeCall.
So, here is very simple logic, in the outbounding bridge transaction, we need to transfer tokens from spender to connector address, in the inbounding redeem transaction, we need to transfer tokens from connector to receiver to address.
Also we will implement one more function: getter for underlying token decimals.
Also we need to use SafeERC20 library by OpenZeppelin.
So, our contract is ready, now it can be deployed on networks and we can start bridging tokens between each chain.
Create contract with name UTSConnectorMintBurnShowcase .
You need to import:
UTSBase is our main contract that should be inherited by any Token or Connector.
In this implementation we also used Ownable, Pausable and IERC20Metadata
We are starting with defining contract and dependencies. As access control we are choosing Ownable and we also want make this contract as Pausable, because in bridge flow it is also nice to have an option to pause all interactions to make some changes for example.
Then we need do define constructor. Since it is connector, we need underlyingToken_(the token that will be bridged through UTS protocol). We need also setup _router address, that can be found .
_allowedChainIds are simply whitelist of chain id's, where you are allowing to bridge tokens.
_chainConfigs is array of settings responsible for bridge settings. We described this config .
In constructor we need to call __UTSBase_init function to initialize UTS Base contract. Also we need to set router and chain config.
After this step we need to override 3 functions: _mintTo, _burnFrom and _authorizeCall. We need to do it, because this connector is mint/burn, so, internal logic should be of main bridging function should be done via mint/burn of underlying token.
So, here is very simple logic, in the inbounding redeem transaction, we need to mint tokens, in the outbounding bridge transaction, we need burn tokens.
Also we will implement 3 more functions to controll pause state and getter for underlying token decimals.
Also we need extended ERC20 interface to call mint and burn functions. In this example, we use standard mint and burnFrom token functions, but you can use any interface and logic supported by your underlyingToken contract.
So, our contract is ready, now it can be deployed on networks and we can start bridging tokens between each chain.
Token verification is an optional process that enhances the visibility and usability of your token in Entangle’s bridge and dApp ecosystem. By verifying your token, it becomes searchable by name rather than by address, improving user accessibility and integration within the platform.
After deploying your token and setting up its cluster, you can use the bridge and associated dApps. By default, users can search for your token using its contract address. Token verification ensures your token is indexed, allowing users to find it quickly by name in search fields.
After deploying and setting up your cluster you can start bridging and using our dApp for this purpose, but by default, you can find your token using address only. This guide will help you to verify your token and after verification, we will index your token and you will have a possibility just to write the name of your token in the search field.
Prepare Your Repository
Fork the token verification .
Create a project folder in the projects directory and add a CLUSTER_INFO.json file based on the provided .
Expand Existing Clusters
If you’ve already verified your token but added new deployments to its cluster, create a new pull request mentioning the expansion and update the CLUSTER_INFO.json.
Submit Your Request
Fill out the required details in the repository’s README and corresponding .
Submit a pull request using the provided PULL_REQUEST_TEMPLATE.md .
Create contract with name UTSTokenShowcase .
You need to import:
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@entangle-labs/uts-contracts/contracts/ERC20/UTSBase";We are starting with defining contract and dependencies. As access control we are choosing Ownable and also since it is UTS Token we need to import ERC20 implementation, here we are using one by OpenZeppelin.
UTSBase is our main contract that should be inherited by any Token or Connector.
In this implementation we also used Ownable and ERC20
Then we need do define constructor. Since it is Token, we don't need a lot, just general UTS settings and ERC20 metadata.
We need setup _router address, that can be found .
_allowedChainIds are simply whitelist of chain id's, where you are allowing to bridge tokens.
_chainConfigs is array of settings responsible for bridge settings. We described this config .
In constructor we need to call __UTSBase_init function to initialize UTS Base contract. Also we need to set router and chain config.
After this step we need to override 3 functions: _mintTo, _burnFrom and _authorizeCall.
So, here is very simple logic, in the outbounding bridge transaction, we need to burn tokens from from address (do not forget about ERC20 allowance check), in the inbounding redeem transaction, we need to mint tokens to receiver to address.
So, our contract is ready, now it can be deployed on networks and we can start bridging tokens between each chain.
Users can renew their previously subscribed data feeds without the need to set the parameters again. In this step-by-step guide we'll go over how to renew your subscriptions using the Universal Data Feeds (UDF) . This process is an intuitive and straight forward process, follow the instructions below to get started.
Users can renew their inactive subscriptions from the "" section or from the "" section of the dashboard.
The Entangle Interoperable Blockchain consists of 3 Layers:
Application Layer
Modules Layer
Tendermint Consensus Layer
import "@openzeppelin/contracts/access/Ownable.sol";
import "@entangle-labs/uts-contracts/contracts/ERC20/UTSBase";contract UTSConnectorNativeShowcase is UTSBase, Ownable {}import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@entangle-labs/uts-contracts/contracts/ERC20/UTSBase";contract UTSConnectorLockUnlockShowcase is UTSBase, Ownable {}constructor(
address underlyingToken_,
address _router,
uint256[] memory _allowedChainIds,
ChainConfig[] memory _chainConfigs
) Ownable(msg.sender) {
__UTSBase_init(underlyingToken_, IERC20Metadata(underlyingToken_).decimals());
_setRouter(_router);
_setChainConfig(_allowedChainIds, _chainConfigs);
}import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Pausable.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@entangle-labs/uts-contracts/contracts/ERC20/UTSBase";contract UTSConnectorMintBurnShowcase is UTSBase, Ownable, Pausable {}constructor(
address underlyingToken_,
address _router,
uint256[] memory _allowedChainIds,
ChainConfig[] memory _chainConfigs
) Ownable(msg.sender) {
__UTSBase_init(underlyingToken_, IERC20Metadata(underlyingToken_).decimals());
_setRouter(_router);
_setChainConfig(_allowedChainIds, _chainConfigs);
}contract UTSTokenShowcase is UTSBase, Ownable, ERC20 {}constructor(
address _router,
uint256[] memory _allowedChainIds,
ChainConfig[] memory _chainConfigs
) Ownable(msg.sender) ERC20("UTS Token Showcase", "UTSTS") {
__UTSBase_init(address(this), decimals());
_setRouter(_router);
_setChainConfig(_allowedChainIds, _chainConfigs);
_mint(msg.sender, 1_000_000 * 10 ** decimals());
}









In the configuration file, set up pipelines to parse data from the response JSON. Below is an example JSON response structure:
Pipeline for Price: asset.price
Pipeline for Timestamp: asset.timestamp
Pipeline for Volume: asset.volume
These pipelines should be defined using GJSON syntax to extract the required fields.
To retrieve data updates, use the Finalized Data API. For example, to fetch the BTC/USD price feed, execute the following curl command:
Below is a sample response containing publisher votes along with their signatures, and data encoded as bytes in the update_call_data property which is ready to be used on-chain.
100004503599627370496{
"asset": {
"price": 1000000000000000000,
"timestamp": 1721636387,
"volume": 10000000000
}
}urlTemplate: "https://api.binance.com/api/v3/avgPrice?symbol={{.DataKey}}"
pipelines:
- value: "asset.price"
- timestamp: "asset.timestamp"
- volume: "asset.volume"curl https://udfsnap.ent-dx.com/last_votes?feedKeys=BTC/USD{
"update_call_data": "0x..",
"feeds": [
{
"feed_key": "BTC/USD",
"votes": [
{
"Value": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS4fbe+zAAA=",
"Timestamp": 1737093395,
"Signature": {
"R": "0xfb2e89fb52f1d832d5437f4b773c0f67749b914b25c592af9cbc8ffe58e9e928",
"S": "0x3c7e717a67b8a0c072f0271f35f3994c865c12b930f20b57149b623cade43d43",
"V": 27
}
},
{
"Value": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS4fbe+zAAA=",
"Timestamp": 1737093402,
"Signature": {
"R": "0xe6a1448feac17a219c63a426f978cd395f8be55a74ebc717a88385033c7c5432",
"S": "0x1a16ba5138e5293b03c093097c8564c55a0f054537a6c665d3b4bc9cf716d46f",
"V": 28
}
},
{
"Value": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS4fbe+zAAA=",
"Timestamp": 1737093400,
"Signature": {
"R": "0x8308dc0d3b5a32a12374fbc8a2852cb14af4e31cc015b4d463e4cfb41d5aa469",
"S": "0x12d5746c9499e7dc5edbbc436f696478ab4ad5a96247fb357d3d0c418769778d",
"V": 28
}
}
]
}
]
}https://api.binance.com/api/v3/avgPrice?symbol={{.DataKey}}using AddressConverter for address;
uint256 public immutable NATIVE_TRANSFER_GAS_LIMIT;
error UTSConnectorNativeShowcase__E0();
error UTSConnectorNativeShowcase__E1();
error UTSConnectorNativeShowcase__E2(bytes);constructor(
address _router,
uint256[] memory _allowedChainIds,
ChainConfig[] memory _chainConfigs,
uint8 _nativeCurrencyDecimals,
uint256 _nativeTransferGasLimit
) Ownable(msg.sender) {
__UTSBase_init(address(0), _nativeCurrencyDecimals);
_setRouter(_router);
_setChainConfig(_allowedChainIds, _chainConfigs);
NATIVE_TRANSFER_GAS_LIMIT = _nativeTransferGasLimit;
}receive() external payable {}
function underlyingDecimals() external view returns(uint8) {
return _decimals;
}
function underlyingBalance() external view returns(uint256) {
return address(this).balance;
}function _authorizeCall() internal override onlyOwner() {}function _burnFrom(
address /* spender */,
address /* from */,
bytes memory /* to */,
uint256 amount,
uint256 /* dstChainId */,
bytes memory /* customPayload */
) internal override returns(uint256 bridgedNativeAmount) {
if (amount >= msg.value) revert UTSConnectorNativeShowcase__E0();
return amount;
}function _mintTo(
address to,
uint256 amount,
bytes memory /* customPayload */,
Origin memory /* origin */
) internal override returns(uint256 receivedNativeAmount) {
if (amount > address(this).balance) revert UTSConnectorNativeShowcase__E1();
(bool _success, bytes memory _response) = to.call{value: amount, gas: NATIVE_TRANSFER_GAS_LIMIT}("");
if (!_success) revert UTSConnectorNativeShowcase__E2(_response);
return amount;
}function _sendRequest(
uint256 payment,
bytes memory dstToken,
bytes memory to,
uint256 amount,
uint8 srcDecimals,
uint256 dstChainId,
uint64 dstGasLimit,
bytes memory customPayload,
bytes memory protocolPayload
) internal override returns(bool success) {
return IUTSRouter(router()).bridge{value: payment - amount}(
dstToken,
msg.sender.toBytes(),
to,
amount,
srcDecimals,
dstChainId,
dstGasLimit,
customPayload,
protocolPayload
);
}function _authorizeCall() internal override onlyOwner() {}function _burnFrom(
address spender,
address /* from */,
bytes memory /* to */,
uint256 amount,
uint256 /* dstChainId */,
bytes memory /* customPayload */
) internal override returns(uint256 bridgedAmount) {
IERC20(_underlyingToken).safeTransferFrom(spender, address(this), amount);
return amount;
}function _mintTo(
address to,
uint256 amount,
bytes memory /* customPayload */,
Origin memory /* origin */
) internal override returns(uint256 receivedAmount) {
IERC20(_underlyingToken).safeTransfer(to, amount);
return amount;
}function underlyingDecimals() external view returns(uint8) {
return _decimals;
}
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
using SafeERC20 for IERC20;function _authorizeCall() internal override onlyOwner() {}function _burnFrom(
address spender,
address /* from */,
bytes memory /* to */,
uint256 amount,
uint256 /* dstChainId */,
bytes memory /* customPayload */
) internal override whenNotPaused() returns(uint256 bridgedAmount) {
IERC20Extended(_underlyingToken).burnFrom(spender, amount);
return amount;
}function _mintTo(
address to,
uint256 amount,
bytes memory /* customPayload */,
Origin memory /* origin */
) internal override whenNotPaused() returns(uint256 receivedAmount) {
IERC20Extended(_underlyingToken).mint(to, amount);
return amount;
}function unpause() external {
_authorizeCall();
_unpause();
}
function pause() external onlyOwner() {
_pause();
}
function underlyingDecimals() external view returns(uint8) {
return _decimals;
}interface IERC20Extended {
function mint(address to, uint256 amount) external;
function burnFrom(address from, uint256 amount) external;
}{
"General information":{
"TokenName":"Mytoken",
"TokenSymbol":"MTK",
"DeployedViaFactory": "false",
"ChainIDs":[1, 42161]
},
"Deployments": [
{
"ChainId": 1,
"Address": "0x",
"Type": "Token",
"Decimals": 18
},
{
"ChainId": 42161,
"Address": "0x",
"Type": "Connector",
"Decimals": 6
}
]
}function _authorizeCall() internal override onlyOwner() {}function _burnFrom(
address spender,
address from,
bytes memory /* to */,
uint256 amount,
uint256 /* dstChainId */,
bytes memory /* customPayload */
) internal override returns(uint256 bridgedAmount) {
if (from != spender) _spendAllowance(from, spender, amount);
_update(from, address(0), amount);
return amount;
}function _mintTo(
address to,
uint256 amount,
bytes memory /* customPayload */,
Origin memory /* origin */
) internal override returns(uint256 receivedAmount) {
_update(address(0), to, amount);
return amount;
}Next, you'll need to import the UTS Base smart contract dependency. The UTS Base contract is the core component in the creation of a UTS Token or UTS Connector. To implement any token or connector, you’ll need to inherit from the UTS Base smart contract. Simply put, it acts as a wrapper around the cross-chain communication logic.
To learn more, check out our examples, which include code with detailed technical documentation.
That's it! You've successfully created a custom token, and your cluster is now set up and ready to go. For additional important information, please refer to the section below. We also provide clear examples of custom tokens to guide you further.
Destination chain Id
Destination gas limit
Length of your custom payload
UTS protocol payload
The UTS Router provides a function called dstMinGasLimit which returns the amount of gas required to execute the {redeem} function on the destination chain.
If you want to perform a cross-chain deploying using our UTSDeploymentRouter, you can use the estimateDeploy function to estimate the separated payments required for sending crosschain deployment requests in the payment token. The function will return the fees you need to pay in PAYMENT_TOKEN.
Array of chain ids where UTS Tokens will be deployed
Array of chain ids where UTS Connectors will be deployed
If you want to perform a cross-chain deploying using our UTSDeploymentRouter, you can use the estimateDeployNative function to estimate the separated payments required for sending crosschain deployment requests in the payment token. The function will return the fees you need to pay in native currency.
Navigate to https://udf.entangle.fi/ and click on "Connect Wallet" at the top right to connect your wallet.
Navigate to https://udf.entangle.fi/ and click on "Connect Wallet" at the top right to connect your wallet.
You can configure your blockchain providers like so:
Navigate to /blockchain/EVM/provider.ts.
Set up your wagmi configuration and provide your project ID (Learn more here:):
Update the transports variable to use your custom RPC endpoints for different chains. By default, they are set to public RPCs, which may not be reliable:
Navigate to /constants/tokens.ts:
Add UTS tokens or connectors you want to bridge.
Follow the TokenOption type to ensure compatibility.
The bridge SDK supports 10 default chains, compatible with UTS:
Ethereum, Mantle, BSC, Base, Arbitrum, Avalanche, Optimism, Polygon, CoreDAO, and XLayer.
These chains are defined in SelectNetworkModal.tsx and filtered based on tokens in the TOKENS file.
To integrate a new chain, follow these steps:
Add the network to Web3Manager (e.g., EVMManager for EVM chains).
Add the network configuration to tokens.ts.
.husky – Git hooks for the repository.
.next – Next.js build folder.
api – GraphQL API calls.
app – Next.js app router directory for pages.
blockchain – Blockchain logic, including providers, hooks, and ABIs.
containers – Reusable container components.
core – Redux layers (actively uses RTK; transitioning to RTK-query).
helpers – Utility functions and helpers.
hooks – Common application hooks.
lib – Third-party libraries used in the project.
providers – React contexts and providers.
public – Public assets folder.
scripts – Service scripts (e.g., Cosmos blockchain interactions).
ui–components – Reusable components that are used in the whole app (like tabs and tables).
utils – Common utilities.
Contributions are welcome! If you’d like to contribute, please follow the repository guidelines and submit a pull request.
The Application Layer is the user-facing component of EIB, bridging consumer devices and the blockchain. It validates, decodes, and routes messages to enable smart contracts, dApps, and other blockchain-based applications, ensuring seamless user interaction and efficient execution.
Sub-Layers
Execution Layer: Validates and executes smart contracts, chaincode, and foundational rules, ensuring accurate transaction processing.
Application Sub-Layer: Provides end-user services like wallets, lending, and staking. Interfaces such as Web3 applications or APIs facilitate user interactions with blockchain-based services.
This comprises a variety of modules that provide specialized functions to the blockchain. These modules are responsible for handling core operations such as asset management, governance, and network security.
See the list of modules and their description in the table below.
Delegated Proof of Stake (DPoS)
Facilitates staking for Validators and Delegators.
Banking
Sending and receiving assets on Entangle Blockchain.
GOV Module
Changes to blockchain parameters, addition of new Validators, Distributors or Agents.
Auth Module
Allows authorized entities to launch smart contracts on Entangle Blockchain.
IBC Module
Facilitates communication with Cosmos-based Chains.
EIB ensures reliability and security with a Byzantine Fault Tolerant (BFT) consensus mechanism, allowing the network to function even if up to one-third of nodes are faulty or compromised. This ensures consistent validation of cross-chain transactions and prevents disruptions from malicious actors.
Participants:
Validators: Propose and vote on transaction blocks, using round-robin scheduling based on their stake proportion.
Delegators: Delegate their tokens to validators, who in turn earn a commission from the delegated stake.
The consensus process in Tendermint involves several key steps to ensure secure and consistent block creation:
Propose: A designated validator compiles transactions into a block and broadcasts it to peers. This ensures that all validators have a common starting point for considering new transactions.
Pre-Vote: Each validator decides whether to support the proposed block. If a validator is already locked on a previous block, or if it receives a valid new proposal, it broadcasts its prevote for that block. If no valid proposal is received, it signs a nil prevote. This step is crucial to gauge initial support for the block, ensuring that validators are aligned before moving forward.
Pre-Commit: Validators broadcast a precommit if they receive more than two-thirds of prevotes for a proposed block, effectively locking onto that block. Locking onto a block prevents validators from equivocating, which is essential for ensuring consensus consistency and security.
Commit: During the commit step, validators wait to receive at least two-thirds of commits for a block. Once this condition is satisfied, the validator sets the CommitTime and moves to the next phase. This step finalizes the block and adds it to the blockchain, ensuring that all validators have reached agreement.
NewHeight: After a block has been committed, the NewHeight step allows the network to gather additional commitments for the committed block from validators that may have been delayed. This step helps to maintain overall network integrity by ensuring that slower validators are included, promoting fairness and cohesion across the network.
Locks play a crucial role in maintaining consistency during voting stages by preventing validators from changing their support within the same round. Once a validator locks onto a block, they remain committed to it until consensus is achieved. This mechanism ensures that validators do not equivocate, thereby maintaining the reliability of the consensus process.
New rounds may be initiated in cases such as proposal failures, invalid blocks, untimely propagation, or insufficient pre-vote or pre-commit consensus. These new rounds are essential to the robustness of the Tendermint consensus process, allowing the network to overcome temporary challenges and maintain consistent block production. This flexibility is key to maintaining the integrity of the network even when disruptions occur.

Becoming a validator on the EIB involves setting up your node, creating a wallet, configuring your system, and finally launching your validator node. This guide will walk you through these steps in detail to help you get started.
Before proceeding, ensure that your hardware meets the recommended specifications to run a validator node efficiently.
Before you begin, make sure your system has the following software installed:
Git
Golang (minimum version 1.21)
Make
jq
Clone the Entangle Blockchain Repository: Start by cloning the and navigating into the project directory:
Build the Project: Compile the project using the make command, which will build the necessary binaries:
After installation, you can verify the installation by running:
Initialize Your Node: Create your node by initializing it with a unique moniker and specifying the chain ID:
Create a New Key: Generate a new key for your wallet. You'll need to provide a key name and select the keyring backend and algorithm:
Retrieve Your Address: Extract your wallet address and store for later use:
Update Configuration Files: Download and . You'll need to replace the existing files in your .entangle/config/ directory with these.
Set Persistent Peers: In your .entangle/config/config.toml file, update the field with the provided list of nodes:
Start Your Node: With the configurations set, you can start your node. Ensure to specify the chain ID and set the gas cap for JSON-RPC:
Verify Node Synchronization: Check if your node is fully synced with the network:
A response of false indicates successful synchronization.
Ensure You Have NTGL Tokens: To create a validator, you must have a sufficient amount of NTGL tokens for staking and transaction fees.
Create Validator: Use the following command to create your validator. Replace <key_name> with your key name and adjust other parameters as necessary:
Verify Your Validator Node: Check if your validator has been successfully added to the validator set:
By following these steps, you can set up and launch a validator node on the Entangle blockchain. Remember to keep your node online and perform regular maintenance to ensure its smooth operation and contribution to the network's security and efficiency.
Create contract with name UTSTokenWithLogicShowcase .
You need to import:
We start with defining the contract and the dependencies. For the access control we choose Ownable and also since it is UTS Token we need to import ERC20 implementation, here we are using one by openzeppelin.
UTSBase is our main contract that should be inherited by any Token or Connector.
In this implementation we also used Ownable and ERC20.
Then we need do define constructor. Since it is Token, we don't need a lot, just general UTS settings and ERC20 metadata.
We need setup _router
import "@entangle-labs/uts-contracts/contracts/ERC20/UTSBase";npm i @entangle-labs/uts-contracts/**
* @notice Returns estimated minimal amount to pay for bridging and minimal gas limit.
* @param dstChainId destination chain Id.
* @param dstGasLimit {redeem} call gas limit on the destination chain.
* @param customPayloadLength user's additional data length.
* @param protocolPayload UTS protocol's additional data.
* @return paymentAmount source chain native currency amount to pay for bridging.
* @return dstMinGasLimit destination chain minimal {redeem} call gas limit.
*/
function estimateBridgeFee(
uint256 dstChainId,
uint64 dstGasLimit,
uint16 customPayloadLength,
bytes calldata protocolPayload
) public view virtual returns(uint256 paymentAmount, uint64 dstMinGasLimit)/**
* @notice Returns the amount of gas required to execute {redeem} function on the destination chain.
* @param dstChainId destination chain Id.
* @return dstMinGasLimitAmount the amount of gas required to execute {redeem} function on the provided {dstChainId}.
*/
function dstMinGasLimit(
uint256 dstChainId
) public view returns(uint64 dstMinGasLimitAmount)/**
* @notice Estimates the separated payments required for send crosschain deployment requests in the {PAYMENT_TOKEN}.
* @param dstTokenChainIds destination chain Ids for {UTSToken} deployments.
* @param dstConnectorChainIds destination chain Ids for {UTSConnector} deployments.
* @return tokenPaymentAmount array of estimated payment amount in the {PAYMENT_TOKEN} for each {dstChainId}.
* @return connectorPaymentAmount array of estimated payment amount in the {PAYMENT_TOKEN} for each {dstChainId}.
* @return totalPaymentAmount estimated total payment amount in the {PAYMENT_TOKEN}.
*/
function estimateDeploy(
uint256[] calldata dstTokenChainIds,
uint256[] calldata dstConnectorChainIds
) external view returns(
uint256[] memory tokenPaymentAmount,
uint256[] memory connectorPaymentAmount,
uint256 totalPaymentAmount
)/**
* @notice Estimates the separated payments required for send crosschain deployment requests in native currency.
* @param dstTokenChainIds destination chain Ids for {UTSToken} deployments.
* @param dstConnectorChainIds destination chain Ids for {UTSConnector} deployments.
* @return tokenPaymentAmountNative array of estimated payment amount in native currency for each {dstChainId}.
* @return connectorPaymentAmountNative array of estimated payment amount in native currency for each {dstChainId}.
* @return totalPaymentAmountNative estimated total payment amount in native currency.
*/
function estimateDeployNative(
uint256[] calldata dstTokenChainIds,
uint256[] calldata dstConnectorChainIds
) external view returns(
uint256[] memory tokenPaymentAmountNative,
uint256[] memory connectorPaymentAmountNative,
uint256 totalPaymentAmountNative
)npm i












Distributor Module
A database of authorized Distributors added through the GOV Module.
EVM Module
Hosts smart contracts deployed on Entangle Blockchain such as the Controller. Provides composability between Entangle Blockchain and EVM Infrastructure such as Metamask, Hardhat, and Truffle.
Vesting Module
Facilitates vesting of native tokens.













NEXT_PUBLIC_BASE_MESSAGES_URL: GraphQL endpoint for tracking the history of bridge operations.
golangci-lint
Solc-JS
CPU
4 or more physical CPU cores
2 or more physical CPU cores
RAM
16GB+
8GB+
Storage
500GB+ SSD
250GB+ SSD
Network
100mbps+
10mbps+
OS
Ubuntu 22.04
Ubuntu 22.04 or macOS
_allowedChainIds are simply whitelist of chain id's, where you are allowing to bridge tokens.
_chainConfigs is array of settings responsible for bridge settings. We described this config here.
In constructor we need to call __UTSBase_init function to initialize UTS Base contract. Also we need to set router and chain config.
Also we added mint function to premint some tokens for deployer address.
Also let's define some events to have a possibility to debug and track transactions and also some errors and library.
After this step we need to override 3 functions: _mintTo, _burnFrom and _authorizeCall. We need to do it, because this token is mint/burn, so, internal logic should be of main bridging function should be done via mint/burn of underlying token.
In this implementation, we use customPayload as an option to transfer tokens to multiple receivers in different amounts just by one transaction. To do that, we define rules for how the raw bytes passed in customPayload should be encoded. In the _burnFrom function, we burn the tokens from the token holder in the provided amount and implement basic validations of the customPayload, if it is provided.
Also, it is necessary to implement tokens minting to the receiver or multiple receivers in the _mintTo function. Both possible scenarios need to be handled depending on whether a meaningful customPayload was provided. In this case, the raw bytes should be decoded, and the corresponding token _amounts should be minted to _receivers.
So, our contract is ready, now it can be deployed on networks and we can start bridging tokens between each chain.
/**
* @notice Sets the destination chains settings.
* @param allowedChainIds chains Ids available for bridging in both directions.
* @param chainConfigs array of {ChainConfig} settings for provided {allowedChainIds}, containing:
* peerAddress: connected {UTSToken} or {UTSConnector} contract address on the destination chain
* minGasLimit: the amount of gas required to execute {redeem} function on the destination chain
* decimals: connected {peerAddress} decimals on the destination chain
* paused: flag indicating whether current contract is paused for sending/receiving messages from the connected {peerAddress}
*
* @return success call result.
*/
function setChainConfig(
uint256[] calldata allowedChainIds,
ChainConfig[] calldata chainConfigs
) external virtual returns(bool success)const projectId = 'your project id';export const transports = {
[mainnet.id]: http('https://ethereum-rpc.publicnode.com'),
[mantle.id]: http('https://mantle-rpc.publicnode.com'),
[bsc.id]: http('https://bsc-rpc.publicnode.com'),
...
};NEXT_PUBLIC_COINGECKO_API_KEY=your API KEY
NEXT_PUBLIC_BASE_MESSAGES_URL=your GraphQL endpointgit clone https://github.com/Entangle-Protocol/entangle-blockchain
cd entangle-blockchain
git checkout mainmake installentangled --helpentangled init <moniker> --chain-id entangle_33033-1entangled keys add <key_name> --keyring-backend file --algo eth_secp256k1MY_ADDRESS=$(entangled keys show <key_name> -a --keyring-backend file)
echo $MY_ADDRESSpersistent_peers = "node1@ip:port, node2@ip:port"entangled start --chain-id entangle_33033-1 --json-rpc.gas-cap 200000000curl -s 127.0.0.1:26657/status | jq '.result.sync_info.catching_up'entangled tx staking create-validator \
--amount="5000000000000000000aNGL" \
--pubkey=$(entangled tendermint show-validator) \
--moniker="validator" \
--chain-id=entangle_33033-1 \
--commission-rate="0.10" \
--commission-max-rate="0.20" \
--commission-max-change-rate="0.01" \
--min-self-delegation="1" \
--gas=500000 \
--gas-prices="10aNGL" \
--from=<key_name> \
--keyring-backend fileentangled query tendermint-validator-setdstChainIddstGasLimitcustomPayloadLengthprotocolPayload dstTokenChainIdsdstConnectorChainIdsimport "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@entangle-labs/uts-contracts/contracts/ERC20/UTSBase";contract UTSTokenWithLogicShowcase is UTSBase, Ownable, ERC20 {}constructor(
address _router,
uint256[] memory _allowedChainIds,
ChainConfig[] memory _chainConfigs
) Ownable(msg.sender) ERC20("UTS Token With Logic Showcase", "TWL") {
__UTSBase_init(address(this), decimals());
_setRouter(_router);
_setChainConfig(_allowedChainIds, _chainConfigs);
_mint(msg.sender, 1_000_000 * 10 ** decimals());
}using AddressConverter for bytes;
error UTSTokenWithLogicShowcase__E0();
error UTSTokenWithLogicShowcase__E1();
error UTSTokenWithLogicShowcase__E2();
event MultiBridged(
address indexed spender,
address from,
bytes indexed dstPeerAddressIndexed,
bytes dstPeerAddress,
bytes[] receivers,
uint256[] amounts,
uint256 indexed dstChainId
);
event MultiRedeemed(
address[] indexed receiversIndexed,
address[] receivers,
uint256[] amounts,
bytes indexed srcPeerAddressIndexed,
bytes srcPeerAddress,
uint256 indexed srcChainId,
bytes sender
);
function _authorizeCall() internal override onlyOwner() {}function _burnFrom(
address spender,
address from,
bytes memory /* to */,
uint256 amount,
uint256 dstChainId,
bytes memory customPayload
) internal override returns(uint256 bridgedAmount) {
if (from != spender) _spendAllowance(from, spender, amount);
_update(from, address(0), amount);
// This is just an example of how multi-bridge can be implemented.
if (customPayload.length > 0) {
// {address} type array can be used here if the token will exist only in EVM-compatible chains.
(bytes[] memory _receivers, uint256[] memory _amounts) = abi.decode(customPayload, (bytes[], uint256[]));
// Some basic checks are added here:
// 1. the {_receivers} and {_amounts} arrays lengths are the same
// 2. the {_receivers} addresses are not empty
// 3. the sum of {_amounts} is equal to {amount}
if (_receivers.length != _amounts.length) revert UTSTokenWithLogicShowcase__E0();
uint256 _amountsSum;
for (uint256 i; _receivers.length > i; ++i) {
if (_receivers[i].length == 0) revert UTSTokenWithLogicShowcase__E1();
_amountsSum += _amounts[i];
}
if (amount != _amountsSum) revert UTSTokenWithLogicShowcase__E2();
ChainConfig memory config = _chainConfig[dstChainId];
emit MultiBridged(spender, from, config.peerAddress, config.peerAddress, _receivers, _amounts, dstChainId);
}
return amount;
}function _mintTo(
address to,
uint256 amount,
bytes memory customPayload,
Origin memory origin
) internal override returns(uint256 receivedAmount) {
if (customPayload.length > 0) {
(bytes[] memory _receiversInBytes, uint256[] memory _amounts) = abi.decode(customPayload, (bytes[], uint256[]));
address[] memory _receivers = new address[](_receiversInBytes.length);
for (uint256 i; _receiversInBytes.length > i; ++i) {
_receivers[i] = _receiversInBytes[i].toAddress();
_update(address(0), _receivers[i], _amounts[i]);
}
emit MultiRedeemed(
_receivers,
_receivers,
_amounts,
origin.peerAddress,
origin.peerAddress,
origin.chainId,
origin.sender
);
} else {
_update(address(0), to, amount);
}
return amount;
}Using the Push model means retrieving data from on-chain contract storage. This requires the data to exist in the on-chain contract storage, uploaded by a publisher or node operator beforehand. UDF maintains a node that continuously publishes updates for supported assets, but anyone is free to run their own node instance to ensure redundancy or customize their setup.
To utilize an update in your contract, you can read the value of the asset of interest from the PushMarketplace contract deployed on each supported chain. For demonstration purposes, in this guide we will create a consumer contract whose logic depends on the BTC/USD price.
First of all, let's create new project and initialize hardhat in it.
Next, let's create our consumer contract. The consumer contract, referred to as the PushReader, will retrieve data stored in PushMarketplace, which assumes the availability of verified updates pushed on-chain beforehand.
In this example, the contract will read the latest stored price of the BTC/USD asset and store it. To achieve this, we'll use the getFeedPrice function, a free data retrieval method for previously subscribed readers.
This contract should already be in subscription so it can have access for verification. The subscription can be purchased from the marketplace and the consumer contract needs to be added as an authorized address for the subscription.
For convenience, this function returns both the price and the timestamp in a single call. Providing both values ensures that users can verify the freshness of the data, which is critical for applications where price accuracy and timing are essential for decision-making.
Next, create an ignition script to help with deployment of the new PushReader contract.
Now let's modify the hardhat configuration file to add the eth_sepolia network.
Now we can deploy the PushReader contract by running the command below.
Now we should add deployed PushConsumer(PushReader) contract as allowed address to our subscribtion.
for desired feed and with desired parameters and put deployed PushConsumer contract as address that can read the contract.
To validate that we have our price, we will write an example backend script that send transaction to execute PushConsumer.consumePrice.
Let's create a separate directory called "scripts" and create a new script consumePrice.ts in it.
Change the PushReaderAddress to the address you got from deployment and execute the script to send the transaction to the blockchain.
You can use cast to examine the transaction call trace and logs. The emitted event shows the latest update that we read. Note that gas usage may vary depending on the result and the version of the library in use.
Take care of the below points while using the logo
We are pleased to share Entangle’s media kit, which contains essential brand assets, key messaging, and valuable resources to support our shared efforts. As we continue to drive innovation in Web3, this kit will provide you with everything needed to accurately represent Entangle and strengthen our partnership. Together, we are building the future of decentralized technology.
Do not crop the logo, rotate it, or place it with other colors
Do not re-create using any other typeface
Do not use shadows, transparency, or any other effects
Refrain from altering the shape, and proportion of the logos
Keep padding around the logo.
mkdir push-consumer && cd push-consumer
yarn init -y
# Yarn initialization
yarn add --dev hardhat
yarn hardhat init
# Hardhat initialization flow(select TypeScript project)

// SPDX-License-Identifier: BSL1.1
pragma solidity ^0.8.20;
interface IPushMarketplace {
function getFeedPrice(
bytes32 feed
) external returns (uint256 price, uint256 timestamp);
}
contract PushReader {
IPushMarketplace public pushMarketplace;
uint public latestPrice;
uint public latestTimestamp;
constructor(address _pushMarketplace){
pushMarketplace = IPushMarketplace(_pushMarketplace);
}
function getData(bytes32 feedKey) external{
(latestPrice, latestTimestamp) = pushMarketplace.getFeedPrice(feedKey);
}
}import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
// PushMarketplace address on Eth Sepolia network
const PushMarketplaceAddress = "0x48A10FF4ea77A099Cc966b537b5efC5b15c1060A"
const PushReaderModule = buildModule("PushReaderModule", (m) => {
const consumer = m.contract("PushReader", [PushMarketplaceAddress]);
return { consumer };
});
export default PushReaderModule;import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";
const config: HardhatUserConfig = {
solidity: "0.8.28",
networks: {
eth_sepolia: {
chainId: 11155111,
// or http://127.0.0.1:8545, if you forked it locally
url: "https://ethereum-sepolia-rpc.publicnode.com",
accounts: [ "0x" ], // TODO: Set deployer private key
},
},
};
export default config;yarn hardhat ignition deploy ./ignition/modules/PushReader.ts --network eth_sepolia✔ Confirm deploy to network eth_sepolia (11155111)? … yes
Compiled 1 Solidity file successfully (evm target: paris).
Hardhat Ignition 🚀
Deploying [ PushReaderModule]
Batch #1
Executed PushReaderModule#PushReader
[ PushReaderModule] successfully deployed 🚀
Deployed Addresses
PushConsumerModule#PushConsumer - 0xe23650424625B602c0f996F25b287203632A3f16mkdir scripts && touch ./scripts/consumePrice.tsimport {
PushReader,
} from "../typechain-types";
import { ethers } from "hardhat";
// PushConsumer address we got from the deployment
const PushReaderAddress = "0xe23650424625B602c0f996F25b287203632A3f16";
async function main() {
const pushConsumer = await ethers.getContractAt(
"PushReader",
PushReaderAddress
) as PushReader
;
const feedKey = ethers.encodeBytes32String("BTC/USD");
let tx = await pushReader.getFeedPrice(feedKey);
await tx.wait();
console.log("sent PushReader.getData tx", tx.hash);
let price = await pushReader.latestPrice();
let timestamp = await pushReader.latestTimestamp();
console.log("price:", price );
console.log("timestamp:", timestamp );
}
main()
.then(() => process.exit(0))
.catch(error => {
console.error(error);
process.exit(1);
});
yarn hardhat run ./scripts/consumePrice.ts --network eth_sepoliasent PushReader.getData tx 0x8c9c7abdfc243500d8f20dafd69cb6b2f9b5c41a1bb5f8a374bb04a31e43dc94
price: 76660817492475224330633n
timestamp: 1744027000ncast run 0x8c9c7abdfc243500d8f20dafd69cb6b2f9b5c41a1bb5f8a374bb04a31e43dc94 -r https://eth-sepolia.public.blastapi.ioExecuting previous transactions from the block.
Traces:
[70570] 0xe23650424625B602c0f996F25b287203632A3f16::getData(0x4254432f55534400000000000000000000000000000000000000000000000000)
├─ [20482] 0x48A10FF4ea77A099Cc966b537b5efC5b15c1060A::getFeedPrice(0x4254432f55534400000000000000000000000000000000000000000000000000)
│ ├─ [15586] 0x9E5953C651932937e5a9185404B925030F5cCF3a::getFeedPrice(0x4254432f55534400000000000000000000000000000000000000000000000000) [delegatecall]
│ │ ├─ emit topic 0: 0x3aa8c2e667a2fabc8ac13b12611dabfd9bdd2f66210ad437122a7ab032ddf011
│ │ │ data: 0x000000000000000000000000e23650424625b602c0f996f25b287203632a3f164254432f55534400000000000000000000000000000000000000000000000000
│ │ └─ ← [Return] 0x00000000000000000000000000000000000000000000103bca8eeefcd59d11890000000000000000000000000000000000000000000000000000000067f3bd78
│ └─ ← [Return] 0x00000000000000000000000000000000000000000000103bca8eeefcd59d11890000000000000000000000000000000000000000000000000000000067f3bd78
└─ ← [Stop]
Transaction successfully executed.
Gas used: 91846Purple
A527E1
Dark Grey
1B1B1B
Light Grey
6E6A6A
Grey to White
F0F0F0 - FFFFFF
White
FFFFFF
Name
PNG
SVG
Circle White
Circle Black
Circle Gray
Circle Purple
Сhamfer rectangle White
Сhamfer rectangle Black
Сhamfer rectangle Gray
Сhamfer rectangle Purple
Background White
Background Gray
Background Purple
Background White Without Logo
Background Gray Without Logo
Background Purple Without Logo
Background White Without Logo
Background Gray Without Logo
Background Purple Without Logo
In the Pull model, consumers include verification data about the most recent update directly in their transaction. This data is fetched from the oracle and verified within the same transaction before being used.
This scheme comes with the following properties:
Consumers always use the latest update in their transaction, so the price used in the transaction is as close to real time as protocol update latency.
The cost for maintaining update relevance on-chain relies on end-consumers. This allows the oracle protocol to have much larger set of assets.
This guide will help developers who want to use UDF Pull model (with subscription) within their contracts. It uses Hardhat for contract compilation, although similar methods apply with other frameworks.
In the first step, we are establishing a basic solidity development setup.
Now create a new PullVerifierSub contract, which utilizes the pullMarketplace.verifyAsSubscriber function to verify the update information provided through calldata. The update contains oracle votes along with their signatures.
This contract should already be in subscription so it can have access for verification. The subscription can be purchased from the marketplace and the consumer contract needs to be added as an authorized address for the subscription.
Once the PullVerifierSub contract verifies the update against these inputs, it stores them on-chain, to later be accessed. If the verification fails (e.g., due to mismatched data, invalid signatures, or other inconsistencies) the contract reverts the entire transaction, ensuring that only valid data is accepted.
Next, create an ignition script to help with deployment of the new PullVerifierSub contract.
Now let's modify the hardhat configuration file to add the eth_sepolia network.
Now we can deploy the PullVerifierSub contract by running the command below.
Now we should add the deployed PullVerifierSub contract as an allowed address to our subscription, so that it can verify Pull updates.
To do that, go to the BTC/USD and select Pull Mode. Then add the address of the deployed PullVerifierSub contract under "Which addresses can read the contract?" and click Subscribe.
You may read more about subscribing to feeds at .
First, add the UDF SDK library. We will use it to fetch the update using the REST API and encode it for on-chain usage.
To validate that we have our price, we will write an example backend script that send transaction to execute PullVerifierSub.verifyAsSubscriber. Let's create separate directory scripts and our new script verifyAsSub.ts to do just that.
Change the PullVerifierAddress to the address you got from deployment and execute the script.
Now let's run the script using the command below to ensure it's working.
You can use cast to examine the transaction call trace and logs. The emitted event shows the latest update that we verified. Note that gas usage may vary depending on the result and the version of the library in use.
If our blueprint Token or Connector meets your requirements, you can deploy the token using our Factory. You may also deploy the Token or Connector contracts on all desired chains (ensure that an existing UTS Router is available on these chains). See below for more information.
If you want to deploy UTS Token using our blueprint, you can call the function below on UTS Factory contract:
Use DeployTokenData :
If you want to deploy UTS Connector, you should use function below:
Using struct DeployConnectorData
Chain Config struct should be built using struct below:
Ensure the chain configurations are properly set up to allow all your smart contracts to communicate with each other.
You can use our Deployment Router for cross-chain deployment, allowing you to deploy and set up the entire cluster with a single function call. To do this, simply call the function below on the UTSDeploymentRouter.
To do this you need to provide deployMetadata:
That's it, now you are ready to make your first cross-chain token transfer!

yarn init -y
# yarn initialization flow
yarn add --dev hardhat
yarn hardhat init
# Hardhat initialization flow(select TypeScript project)// SPDX-License-Identifier: BSL1.1
pragma solidity ^0.8.20;
interface IPullMarketplace {
struct PriceUpdate {
uint256 price;
uint256 timestamp;
}
function verifyAsSubscriber(
bytes calldata updateData,
bytes32 feedKey
)
external
returns (PriceUpdate memory);
}
contract PullVerifierSub {
IPullMarketplace public pullMarketplace;
uint public latestPrice;
uint public latestTimestamp;
constructor(address _pullMarketplace){
pullMarketplace = IPullMarketplace(_pullMarketplace);
}
// @notice function that verifies feed as subscriber from provided update through UDFOracle and stores update data
function verify(bytes calldata updateData, bytes32 feedKey) external{
IPullMarketplace.PriceUpdate memory priceUpdate = pullMarketplace.verifyAsSubscriber(updateData,feedKey);
latestPrice = priceUpdate.price;
latestTimestamp = priceUpdate.timestamp;
}
}
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
// PullMarketplace address on Eth Sepolia network
const PullMarketplaceAddress = "0xC6670092056e7bf56c3149FDE52A187b3e9f2e63";
const PullVerifierSubModule = buildModule("PullVerifierSubModule", (m) => {
const consumer = m.contract("PullVerifierSub", [PullMarketplaceAddress ]);
return { consumer };
});
export default PullVerifierSubModule;import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";
const config: HardhatUserConfig = {
solidity: "0.8.28",
networks: {
eth_sepolia: {
chainId: 11155111,
// or http://127.0.0.1:8545, if you forked it locally
url: "https://ethereum-sepolia-rpc.publicnode.com",
accounts: [ "0x" ], // TODO: Set deployer private key
},
},
};
export default config;yarn hardhat ignition deploy ./ignition/modules/PullVerifierSub.ts --network eth_sepolia✔ Confirm deploy to network eth_sepolia (11155111)? … yes
Compiled 1 Solidity file successfully (evm target: paris).
Hardhat Ignition 🚀
Deploying [ PullVerifierSubModule ]
Batch #1
Executed PullVerifierSubModule#PullVerifierSub
[ PullVerifierSubModule] successfully deployed 🚀
Deployed Addresses
PullVerifierSubModule#PullVerifierSub - 0x7c0ad1Bb6c6dD48CA70C190B400aD56DeF61F43C
yarn add --dev @entangle-labs/udf-sdkmkdir scripts && touch ./scripts/verifyAsSub.tsimport {
PullVerifierSub,
} from "../typechain-types";
import { ethers } from "hardhat";
import { UdfSdk } from '@entangle-labs/udf-sdk';
// PullVerifierSub address we got from the deployment
const PullVerifierAddress = "0x7c0ad1Bb6c6dD48CA70C190B400aD56DeF61F43C";
async function main() {
const pullVerifier = await ethers.getContractAt(
"PullVerifierSub",
PullVerifierAddress
) as PullVerifierSub;
// Fetch the update data from finalized-data-snap
const sdk = new UdfSdk();
const updateData = await sdk.getCallData(["BTC/USD"]);
const feedKey = ethers.encodeBytes32String("BTC/USD");
let tx = await pullVerifier.verify(updateData,feedKey);
await tx.wait();
console.log("sent PullVerifierSub.verify tx", tx.hash);
let price = await pullVerifier.latestPrice();
let timestamp = await pullVerifier.latestTimestamp();
console.log("price:", price );
console.log("timestamp:", timestamp );
}
main()
.then(() => process.exit(0))
.catch(error => {
console.error(error);
process.exit(1);
});yarn hardhat run ./scripts/verifyAsSub.ts --network eth_sepoliasent PullVerifierSub.verify tx 0x7d202c67c1d3f04606743a406979eb4dd4a435be37eb0490c502895c28ecc0ec
price: 84087090417798546633122n
timestamp: 1743492361ncast run 0x7d202c67c1d3f04606743a406979eb4dd4a435be37eb0490c502895c28ecc0ec -r https://ethereum-sepolia-rpc.publicnode.comExecuting previous transactions from the block.
Traces:
[99231] PullVerifierSub::verify(0x55444646014254432f55534400000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000011ce4ebc2338797454e80000000000000000000000000000000000000000000000000000000067eba567c157813327ab23012c0c51d803a60fdeb2db3460e19840849b99b5e4996ee90853a0d1ad5e5be5304cb76990ffd1e04680f7f2f85680ac62f529b5614031062e1c0000000000000000000000000000000000000000000011ce5ecc567d2b3ab1a20000000000000000000000000000000000000000000000000000000067eba567cd0d22ac7bc0820ebcbc11b087c059c2d36b207d020a3987b21b56b11c2ca0982796fc953c1ebf14434ee0dffc225d87e12ca4506f07f0971e4fcd72bcbab2241c0000000000000000000000000000000000000000000011ce6ed6df09a64f08420000000000000000000000000000000000000000000000000000000067eba567c82350a2e16b70134ec057193550fd49b18e793604b1e4743fc01c2cae01f9b7631cdfa9033e2c414cf29451f916c482316673fd8be956a1d5ea5dc1791115c01b, 0x4254432f55534400000000000000000000000000000000000000000000000000)
├─ [82339] ERC1967Proxy::fallback(0x55444646014254432f55534400000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000011ce4ebc2338797454e80000000000000000000000000000000000000000000000000000000067eba567c157813327ab23012c0c51d803a60fdeb2db3460e19840849b99b5e4996ee90853a0d1ad5e5be5304cb76990ffd1e04680f7f2f85680ac62f529b5614031062e1c0000000000000000000000000000000000000000000011ce5ecc567d2b3ab1a20000000000000000000000000000000000000000000000000000000067eba567cd0d22ac7bc0820ebcbc11b087c059c2d36b207d020a3987b21b56b11c2ca0982796fc953c1ebf14434ee0dffc225d87e12ca4506f07f0971e4fcd72bcbab2241c0000000000000000000000000000000000000000000011ce6ed6df09a64f08420000000000000000000000000000000000000000000000000000000067eba567c82350a2e16b70134ec057193550fd49b18e793604b1e4743fc01c2cae01f9b7631cdfa9033e2c414cf29451f916c482316673fd8be956a1d5ea5dc1791115c01b, 0x4254432f55534400000000000000000000000000000000000000000000000000)
│ ├─ [77350] PullMarketplace::verifyAsSubscriber(0x55444646014254432f55534400000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000011ce4ebc2338797454e80000000000000000000000000000000000000000000000000000000067eba567c157813327ab23012c0c51d803a60fdeb2db3460e19840849b99b5e4996ee90853a0d1ad5e5be5304cb76990ffd1e04680f7f2f85680ac62f529b5614031062e1c0000000000000000000000000000000000000000000011ce5ecc567d2b3ab1a20000000000000000000000000000000000000000000000000000000067eba567cd0d22ac7bc0820ebcbc11b087c059c2d36b207d020a3987b21b56b11c2ca0982796fc953c1ebf14434ee0dffc225d87e12ca4506f07f0971e4fcd72bcbab2241c0000000000000000000000000000000000000000000011ce6ed6df09a64f08420000000000000000000000000000000000000000000000000000000067eba567c82350a2e16b70134ec057193550fd49b18e793604b1e4743fc01c2cae01f9b7631cdfa9033e2c414cf29451f916c482316673fd8be956a1d5ea5dc1791115c01b, 0x4254432f55534400000000000000000000000000000000000000000000000000) [delegatecall]
│ │ ├─ [60369] 0xc0931aEE1064BD5245fEe76A2d740eab8436621e::40366475(00000000000000000000000000000000000000000000000000000000000000404254432f5553440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a955444646014254432f55534400000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000011ce4ebc2338797454e80000000000000000000000000000000000000000000000000000000067eba567c157813327ab23012c0c51d803a60fdeb2db3460e19840849b99b5e4996ee90853a0d1ad5e5be5304cb76990ffd1e04680f7f2f85680ac62f529b5614031062e1c0000000000000000000000000000000000000000000011ce5ecc567d2b3ab1a20000000000000000000000000000000000000000000000000000000067eba567cd0d22ac7bc0820ebcbc11b087c059c2d36b207d020a3987b21b56b11c2ca0982796fc953c1ebf14434ee0dffc225d87e12ca4506f07f0971e4fcd72bcbab2241c0000000000000000000000000000000000000000000011ce6ed6df09a64f08420000000000000000000000000000000000000000000000000000000067eba567c82350a2e16b70134ec057193550fd49b18e793604b1e4743fc01c2cae01f9b7631cdfa9033e2c414cf29451f916c482316673fd8be956a1d5ea5dc1791115c01b0000000000000000000000000000000000000000000000) [staticcall]
│ │ │ ├─ [55380] 0xB2F863B68d85b198DDe2fE7da1D8baFdCFf199c0::40366475(00000000000000000000000000000000000000000000000000000000000000404254432f5553440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a955444646014254432f55534400000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000011ce4ebc2338797454e80000000000000000000000000000000000000000000000000000000067eba567c157813327ab23012c0c51d803a60fdeb2db3460e19840849b99b5e4996ee90853a0d1ad5e5be5304cb76990ffd1e04680f7f2f85680ac62f529b5614031062e1c0000000000000000000000000000000000000000000011ce5ecc567d2b3ab1a20000000000000000000000000000000000000000000000000000000067eba567cd0d22ac7bc0820ebcbc11b087c059c2d36b207d020a3987b21b56b11c2ca0982796fc953c1ebf14434ee0dffc225d87e12ca4506f07f0971e4fcd72bcbab2241c0000000000000000000000000000000000000000000011ce6ed6df09a64f08420000000000000000000000000000000000000000000000000000000067eba567c82350a2e16b70134ec057193550fd49b18e793604b1e4743fc01c2cae01f9b7631cdfa9033e2c414cf29451f916c482316673fd8be956a1d5ea5dc1791115c01b0000000000000000000000000000000000000000000000) [delegatecall]
│ │ │ │ ├─ [3000] PRECOMPILES::ecrecover(0x0a68e8365be00d0cd98a1171d18af4e3e1566fd09a59d61fe23743c2dbe50c12, 28, 87450987175619729345224437904190378668378471047311994984055329008011973683464, 37826109101770063310129024824192616189507182989479589014137121493233374922286) [staticcall]
│ │ │ │ │ └─ ← [Return] 0x0000000000000000000000006733d110a59dc160b5c8093066a7f5103885196f
│ │ │ │ ├─ [8142] ERC1967Proxy::55b5190b(7564662d76312e310000000000000000000000000000000000000000000000000000000000000000000000006733d110a59dc160b5c8093066a7f5103885196f) [staticcall]
│ │ │ │ │ ├─ [3246] 0xA7a8eAAA131dc4D2f70636F5F8796e640B119926::55b5190b(7564662d76312e310000000000000000000000000000000000000000000000000000000000000000000000006733d110a59dc160b5c8093066a7f5103885196f) [delegatecall]
│ │ │ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000001
│ │ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000001
│ │ │ │ ├─ [3000] PRECOMPILES::ecrecover(0x14e205399af2fdf39cd5ba0d6d33d50ffbb0007a622f772edc16663e7de9cd57, 28, 92747342280930951330331743016924826142985862405571157083665267571049381011608, 17906971417906977260169118529732908076796855624976136117544900421598137987620) [staticcall]
│ │ │ │ │ └─ ← [Return] 0x000000000000000000000000be524616e96bb4b62cce8034ab6bea8f2505b55a
│ │ │ │ ├─ [3642] ERC1967Proxy::55b5190b(7564662d76312e31000000000000000000000000000000000000000000000000000000000000000000000000be524616e96bb4b62cce8034ab6bea8f2505b55a) [staticcall]
│ │ │ │ │ ├─ [3246] 0xA7a8eAAA131dc4D2f70636F5F8796e640B119926::55b5190b(7564662d76312e31000000000000000000000000000000000000000000000000000000000000000000000000be524616e96bb4b62cce8034ab6bea8f2505b55a) [delegatecall]
│ │ │ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000001
│ │ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000001
│ │ │ │ ├─ [3000] PRECOMPILES::ecrecover(0xca613c8b1a20618b010d94a62a700c6e09917c2a8778a82c881f2be28d25a2b3, 27, 90524965894879087420297465183903263448359497350110207873450031945407891896759, 44829987373565001565186606953511927342046667061064660196284399196770407617984) [staticcall]
│ │ │ │ │ └─ ← [Return] 0x00000000000000000000000093b502d3eb45b9eae948f8fca01e64d9c0ba538a
│ │ │ │ ├─ [3642] ERC1967Proxy::55b5190b(7564662d76312e3100000000000000000000000000000000000000000000000000000000000000000000000093b502d3eb45b9eae948f8fca01e64d9c0ba538a) [staticcall]
│ │ │ │ │ ├─ [3246] 0xA7a8eAAA131dc4D2f70636F5F8796e640B119926::55b5190b(7564662d76312e3100000000000000000000000000000000000000000000000000000000000000000000000093b502d3eb45b9eae948f8fca01e64d9c0ba538a) [delegatecall]
│ │ │ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000001
│ │ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000001
│ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000011ce5ecc567d2b3ab1a20000000000000000000000000000000000000000000000000000000067eba567
│ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000011ce5ecc567d2b3ab1a20000000000000000000000000000000000000000000000000000000067eba567
│ │ ├─ emit VerifyAsSubscriberCalled(verifier: PullVerifierSub: [0x9Cd59BEbE8daBbb4a79739619a99DF890498305e], feedKey: 0x4254432f55534400000000000000000000000000000000000000000000000000)
│ │ └─ ← [Return] PriceUpdate({ price: 84087090417798546633122 [8.408e22], timestamp: 1743496551 [1.743e9] })
│ └─ ← [Return] PriceUpdate({ price: 84087090417798546633122 [8.408e22], timestamp: 1743496551 [1.743e9] })
└─ ← [Stop]
Transaction successfully executed.
Gas used: 125591
True/false flag is token should be without any extensions (non-mintable, non-burnable, without fee, working by lock/unlock scheme). If true, other flags should be false
True/false flag is token mintable by owner (owner can mint arbitrary token amount)
True/false flag is token burnable (everyone can burn his tokens)
True/false flag is token burnable by BURNER_ROLE role only
True/false flag is token will supports fee extracting for bridging(currently unsupported, should be false by default)
UTSRouter address on the deployment chain
Array of initial destination chain IDs that token binded to
Array of ChainConfig structs, initial settings
Salt to deploy new token to specified address, you can specify it, if you want specific address
Provided salt to deploy new connector to specified address
Address encoded here will be setted as the owner of the token
The name of new token
The symbol of new token
The decimals of new token
Token initial supply, that will be minted
Token supply, that will be minted to owner (must be less than or equal initialSupply)
owner
UTSConnector owner's address, encoded in bytes
underlyingToken
Address of the ERC20 token on top of which is UTSConnector built(you can also use a placeholder address 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, if the desired connector have to interact with network native currency, e.g. Ether)
feeModule
True/false flag is connector will supports fee extracting for bridging(currently unsupported, should be false by default)
router
UTSRouter address
allowedChainIds
Array of initial destination chain IDs that connector binded to
chainConfigs
Array of ChainConfig structs, initial settings
peerAddress
Destination peer address, that can bridge or receive tokens to/from source chain. Normally it should be address of token/connector on destination chain
minGasLimit
Minimal gas amount that can be provided as function's input and will be used at redeem function
decimals
Destination peer token decimals
paused
True/false flag is destination peer paused
dstChainId
Destination chain Id (or current)
isConnector
True/false flag is specified params for deploy UTSConnector
params
encoded deployData (encoded DeployTokenData struct or encoded DeployConnectorData struct)
salt
/**
* @notice Deploys a new {UTSToken} using the provided deployment parameters.
* @param deployData the {DeployTokenData} struct containing deployment parameters.
* @dev See the {UTSERC20DataTypes.DeployTokenData} for details.
*
* @return success call result.
* @return newToken a newly deployed {UTSToken} contract address.
*/
function deployToken(DeployTokenData calldata deployData)struct DeployTokenData {
bytes owner;
string name;
string symbol;
uint8 decimals;
uint256 initialSupply;
uint256 mintedAmountToOwner;
bool pureToken;
bool mintable;
bool globalBurnable;
bool onlyRoleBurnable;
bool feeModule;
bytes router;
uint256[] allowedChainIds;
ChainConfig[] chainConfigs;
bytes32 salt;
}ownernamesymboldecimalsinitialSupplymintedAmountToOwner/**
* @notice Deploys a new {UTSConnector} using the provided deployment parameters.
* @param deployData the {DeployConnectorData} struct containing deployment parameters.
* @dev See the {UTSERC20DataTypes.DeployConnectorData} for details.
*
* @return success call result.
* @return newConnector a newly deployed {UTSConnector} contract address.
*/
function deployConnector(DeployConnectorData calldata deployData)struct DeployConnectorData {
bytes owner;
bytes underlyingToken;
bool feeModule;
bytes router;
uint256[] allowedChainIds;
ChainConfig[] chainConfigs;
bytes32 salt;
}struct ChainConfig {
bytes peerAddress;
uint32 minGasLimit;
uint8 decimals;
bool paused;
}/**
* @notice Sends UTSToken and UTSConnector deployment crosschain requests via UTS protocol V1.
* @param deployMetadata array of {DeployMetadata} structs, containing destination chain Ids and deploy parameters:
* dstChainId: destination chain Id
* isConnector: flag indicating whether is request for deploy connector(true) or token(false)
* params: abi.encoded {DeployTokenData} struct or abi.encoded {DeployConnectorData} struct
* @dev See the {UTSERC20DataTypes.DeployTokenData} and {UTSERC20DataTypes.DeployConnectorData} for details.
*
* @param paymentToken address of the token used for payment.
* @dev Any {paymentToken} address different from the {PAYMENT_TOKEN} is identified as a payment in native currency.
*
* @return paymentAmount total payment required for send deployment requests.
* @return currentChainDeployment deployment address on the current chain (if a relevant request was provided).
*/
function sendDeployRequest(
DeployMetadata[] calldata deployMetadata,
address paymentToken
) external payable whenNotPaused() returns(
uint256 paymentAmount,
address currentChainDeployment
)pureTokenmintableglobalBurnableonlyRoleBurnablefeeModulerouterallowedChainIdschainConfigssalt













In the Pull model, consumers include verification data about the most recent update directly in their transaction. This data is fetched from the oracle and verified within the same transaction before being used.
This scheme comes with the following properties:
Consumers always use the latest update in their transaction, so the price used in the transaction is as close to real time as protocol update frequency.
The cost for maintaining update relevance on-chain relies on end-consumers. This allows the oracle protocol to have much larger set of assets.
This guide will help developers who want to use Pull model (PAYG) within their contracts. It uses Hardhat for contract compilation, although similar methods apply with other frameworks.
In the first step, we are establishing a basic solidity development setup.
Now create a new PullVerifier contract, which utilizes the pullMarketplace.verifyWithFee function to verify the update information provided through calldata. The update contains oracle votes along with their signatures.
Once the PullVerifier contract verifies the update against these inputs, it returns the verified value to the caller contract. If the verification fails (e.g., due to mismatched data, invalid signatures, or other inconsistencies) the contract reverts the entire transaction, ensuring that only valid data is accepted.
Now that we have a contract, let's write a hardhat ignition script to deploy it.
Now let's modify the hardhat configuration file to add the eth_sepolia network.
Now we can deploy the PullVerifier contract by running the command below.
At this point, the PullConsumer contract is ready to verify updates that we send it. But before doing that, let's quickly go through the update format and the data that it expects.
As an example, let’s imagine you need BTC/USD price in your contract. First, we need to fetch the latest update from the Finalized Data backend service. This service constantly synchronizes its state with oracle's state and provides oracle data via convenient APIs (REST, WebSocket).
This response provides all necessary details to verify the BTC/USD update, specifically:
feeds: An array containing parsed update data for requested feeds.
votes: Publisher's latest votes on the specific feed. This aggregated array of votes is necessary to obtain agreed-upon value on-chain.
Value: The data that Publisher observed.
Timestamp:
This data enables executing the transaction on the PullVerifier contract. To begin, fetch the latest update from the Finalized Data API for the BTC/USD price.
Now that we understand the Finalized Data API, we can create scripts to demonstrate update verification.
First of all, we need to fund the PullVerifier contract, so it will be able to pay the verification fee. Copy the following code to the scripts/fundVerifier.ts file.
Change the PullConsumerAddress to the address you got from deployment and execute the script to fund the deployed PullConsumer contract.
Now we will need to create a script that retrieves an update from the API, parses the response, encodes it to EVM calldata, and sends a transaction to verify the update. The contract can then validate the update and incorporate the verified price into its on-chain logic.
First, add the UDF SDK library. We will use it to fetch the update using the REST API and encode it for on-chain usage.
Copy the script below to the scripts/fetchAndVerify.ts file.
Now let’s execute the script using the command below.
You can use cast to examine the transaction call trace and logs. The emitted event shows the latest update that we verified.
Note that gas usage may vary depending on the result and the version of the library in use.
Publisher: Publisher's public key.
Signature: ECDSA signature for the observed value. This signature represents commitment from publisher that this data is what he observed.
yarn init -y
# yarn initialization flow
yarn add --dev hardhat
yarn hardhat init
# Hardhat initialization flow (select TypeScript project)// SPDX-License-Identifier: BSL1.1
pragma solidity ^0.8.20;
interface IPullMarketplace {
struct PriceUpdate {
uint256 price;
uint256 timestamp;
}
function verifyWithFee(
bytes calldata updateData,
bytes32 feedKey
) external payable returns (PriceUpdate memory);
function getUpdateFee(uint256 nUpdates) external view returns (uint256);
}
contract PullVerifier {
IPullMarketplace public pullMarketplace;
uint public latestPrice;
uint public latestTimestamp;
constructor(address _pullMarketplace){
pullMarketplace = IPullMarketplace(_pullMarketplace);
}
// @notice function that verifies feed from provided update PAYG through PullMarketplace and stores update data
function VerifyPAYG(bytes calldata updateData, bytes32 feedKey) external{
// Calculate required fee
uint256 fee = pullMarketplace.getUpdateFee(1);
// Verify the update
IPullMarketplace.PriceUpdate memory priceUpdate = pullMarketplace.verifyWithFee{value: fee}(updateData, feedKey);
// store update data
latestPrice = priceUpdate.price;
latestTimestamp = priceUpdate.timestamp;
}
// @notice deposit native tokens for fee coverage
receive() payable external {}
}
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
// PullMarketplace address on Eth Sepolia network
const PullMarketplaceAddress = "0xC6670092056e7bf56c3149FDE52A187b3e9f2e63";
const PullVerifierModule = buildModule("PullVerifierModule", (m) => {
const consumer = m.contract("PullVerifier", [PullMarketplaceAddress ]);
return { consumer };
});
export default PullVerifierModule;import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";
const config: HardhatUserConfig = {
solidity: "0.8.28",
networks: {
eth_sepolia: {
chainId: 11155111,
// or http://127.0.0.1:8545, if you forked it locally
url: "https://ethereum-sepolia-rpc.publicnode.com",
accounts: [ "0x" ], // TODO: Set deployer private key
},
},
};
export default config;yarn hardhat ignition deploy ./ignition/modules/PullVerifier.ts --network eth_sepolia✔ Confirm deploy to network eth_sepolia (11155111)? … yes
Hardhat Ignition 🚀
Resuming existing deployment from ./ignition/deployments/chain-11155111
Deploying [ PullVerifierModule ]
Batch #1
Executed PullVerifierModule#PullVerifier
[ PullVerifierModule] successfully deployed 🚀
Deployed Addresses
PullVerifierModule#PullVerifier - 0xBd0AFEE88B7FcEE6139467700D6f40F13FEDa739curl https://udfsnap.ent-dx.com/last_votes?feedKeys=BTC/USD{
"update_call_data": "0x55444646014254432f555344000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000103c693478ddd1a5ff8b0000000000000000000000000000000000000000000000000000000067f3bd9da5c05745f7651d2c2c9f8771ff435193f2b0c719ca149e0b20ef4ecf0b0050c07d76ac34ec08947a026ad849ccce7f324c1d206140c0cf23411ae13f8e5b60121b00000000000000000000000000000000000000000000103c8289badcc4b240340000000000000000000000000000000000000000000000000000000067f3bd9de20d0d68fe93309786b18387c48bbfdd6d2ff03da78bc31294d25b3363a988491f75e29da2c99e3a39bc9afd8bf3072f1c39fe1bfb00033a73af48ba7ad7bf291b00000000000000000000000000000000000000000000103c84ecee2483ad3be70000000000000000000000000000000000000000000000000000000067f3bd9d9ed7b8ba15718887607d52853a12de16bd79d6bf8df3c29a051561d42fe6b160208c1cc24ac7c186fad1f70828be7bbbd66fe7c090193223f788c652ebc127071b",
"feeds": [
{
"feed_key": "BTC/USD",
"votes": [
{
"Value": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAABA8aTR43dGl/4s=",
"Timestamp": 1744027037,
"Publisher": "0x6733D110A59Dc160b5C8093066A7f5103885196F",
"Signature": {
"R": "0xa5c05745f7651d2c2c9f8771ff435193f2b0c719ca149e0b20ef4ecf0b0050c0",
"S": "0x7d76ac34ec08947a026ad849ccce7f324c1d206140c0cf23411ae13f8e5b6012",
"V": 27
}
},
{
"Value": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAABA8gom63MSyQDQ=",
"Timestamp": 1744027037,
"Publisher": "0xbe524616e96bb4b62ccE8034AB6beA8F2505B55a",
"Signature": {
"R": "0xe20d0d68fe93309786b18387c48bbfdd6d2ff03da78bc31294d25b3363a98849",
"S": "0x1f75e29da2c99e3a39bc9afd8bf3072f1c39fe1bfb00033a73af48ba7ad7bf29",
"V": 27
}
},
{
"Value": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAABA8hOzuJIOtO+c=",
"Timestamp": 1744027037,
"Publisher": "0x93b502d3eb45b9EAE948F8Fca01E64D9c0BA538a",
"Signature": {
"R": "0x9ed7b8ba15718887607d52853a12de16bd79d6bf8df3c29a051561d42fe6b160",
"S": "0x208c1cc24ac7c186fad1f70828be7bbbd66fe7c090193223f788c652ebc12707",
"V": 27
}
}
]
}
]
}import { ethers } from "hardhat";
// PullVerifier contract address on ETH Sepolia
const PullVerifierAddress = "0xBd0AFEE88B7FcEE6139467700D6f40F13FEDa739";
async function main() {
// Use default hardhat signer to fund the PullConsumer contract
const [funder] = await ethers.getSigners();
// 0.0003 is the exact cost for 10 updates on eth_seoplia
const depositAmount = ethers.parseEther("0.0003");
// Send funding transaction
const tx = await funder.sendTransaction({
to: PullVerifierAddress ,
value: depositAmount
});
await tx.wait();
console.log(`sent ${depositAmount} to PullVerifier, tx: ${tx.hash}`);
}
main()
.then(() => process.exit(0))
.catch(error => {
console.error(error);
process.exit(1);
});
yarn hardhat run ./scripts/fundVerifier.ts --network eth_sepoliayarn add --dev @entangle-labs/udf-sdkimport { ethers } from "hardhat";
import {
PullVerifier,
} from "../typechain-types";
import { UdfSdk } from '@entangle-labs/udf-sdk';
// PullVerifier contract address on ETH Sepolia
const PullVerifierAddress = "0xBd0AFEE88B7FcEE6139467700D6f40F13FEDa739";
async function main() {
// Fetch the update data from finalized-data-snap
const sdk = new UdfSdk();
const updateData = await sdk.getCallData(["BTC/USD"]);
const feedKey = ethers.encodeBytes32String("BTC/USD");
// Bind PullVerifier contract on the network
const verifier = await ethers.getContractAt(
"PullVerifier",
PullVerifierAddress
);
// Send verify transaction
let tx = await verifier.VerifyPAYG(
updateData,feedKey
);
await tx.wait();
console.log("sent tx:", tx.hash);
let price = await verifier.latestPrice();
let timestamp = await verifier.latestTimestamp();
console.log("price:", price );
console.log("timestamp:", timestamp );
}
main()
.then(() => process.exit(0))
.catch(error => {
console.error(error);
process.exit(1);
});yarn hardhat run ./scripts/fetchAndVerify.ts --network eth_sepoliasent tx: 0xb66fb4112b7fdf72211c2238b9b0a609a8321e2694ddad9b69ec7dcc953fdb18
price: 76683771643884818373141n
timestamp: 1744027087ncast run 0xb66fb4112b7fdf72211c2238b9b0a609a8321e2694ddad9b69ec7dcc953fdb18 -r https://eth-sepolia.public.blastapi.ioExecuting previous transactions from the block.
Traces:
[137017] 0xBd0AFEE88B7FcEE6139467700D6f40F13FEDa739::2e37a79c(00000000000000000000000000000000000000000000000000000000000000404254432f5553440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a955444646014254432f555344000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000103d081c72d9f475f0bd0000000000000000000000000000000000000000000000000000000067f3bdcf2e2ffc130ee5e218e62e5babe2b2c6d0fa30f07c44b3eb131bf32e8dbda99b1e419e4016c33066e7f736958fb849a20edf1f84ecc71acd6ebe1a822142d7276a1c00000000000000000000000000000000000000000000103d091c76140eff9a150000000000000000000000000000000000000000000000000000000067f3bdcf27ef18a4bb82a9d25c7384e5dc40a992a8f6c85d4099d9f77c2833641e91bef96f8c7f3e4296ad701dbd2d93ed345fde78bcbd6c430b5294b81be0eccce83efa1b00000000000000000000000000000000000000000000103d09c19608a94bf2d10000000000000000000000000000000000000000000000000000000067f3bdcf9240ac502823b0d89ec923abcd895508c38ff00f763332d89a5b5acb292600632af57fc4382aa10b59e95d963dd2f39072ba9133186af6c73b939a1a5b6afd371b0000000000000000000000000000000000000000000000)
├─ [8177] 0xC6670092056e7bf56c3149FDE52A187b3e9f2e63::getUpdateFee(1) [staticcall]
│ ├─ [3284] 0xF9E8AEE871D1e9cED62702050F16E0660a88645a::getUpdateFee(1) [delegatecall]
│ │ └─ ← [Return] 0x00000000000000000000000000000000000000000000000000001b48eb57e000
│ └─ ← [Return] 0x00000000000000000000000000000000000000000000000000001b48eb57e000
├─ [70181] 0xC6670092056e7bf56c3149FDE52A187b3e9f2e63::b90ffe1f{value: 30000000000000}(00000000000000000000000000000000000000000000000000000000000000404254432f5553440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a955444646014254432f555344000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000103d081c72d9f475f0bd0000000000000000000000000000000000000000000000000000000067f3bdcf2e2ffc130ee5e218e62e5babe2b2c6d0fa30f07c44b3eb131bf32e8dbda99b1e419e4016c33066e7f736958fb849a20edf1f84ecc71acd6ebe1a822142d7276a1c00000000000000000000000000000000000000000000103d091c76140eff9a150000000000000000000000000000000000000000000000000000000067f3bdcf27ef18a4bb82a9d25c7384e5dc40a992a8f6c85d4099d9f77c2833641e91bef96f8c7f3e4296ad701dbd2d93ed345fde78bcbd6c430b5294b81be0eccce83efa1b00000000000000000000000000000000000000000000103d09c19608a94bf2d10000000000000000000000000000000000000000000000000000000067f3bdcf9240ac502823b0d89ec923abcd895508c38ff00f763332d89a5b5acb292600632af57fc4382aa10b59e95d963dd2f39072ba9133186af6c73b939a1a5b6afd371b0000000000000000000000000000000000000000000000)
│ ├─ [69692] 0xF9E8AEE871D1e9cED62702050F16E0660a88645a::b90ffe1f{value: 30000000000000}(00000000000000000000000000000000000000000000000000000000000000404254432f5553440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a955444646014254432f555344000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000103d081c72d9f475f0bd0000000000000000000000000000000000000000000000000000000067f3bdcf2e2ffc130ee5e218e62e5babe2b2c6d0fa30f07c44b3eb131bf32e8dbda99b1e419e4016c33066e7f736958fb849a20edf1f84ecc71acd6ebe1a822142d7276a1c00000000000000000000000000000000000000000000103d091c76140eff9a150000000000000000000000000000000000000000000000000000000067f3bdcf27ef18a4bb82a9d25c7384e5dc40a992a8f6c85d4099d9f77c2833641e91bef96f8c7f3e4296ad701dbd2d93ed345fde78bcbd6c430b5294b81be0eccce83efa1b00000000000000000000000000000000000000000000103d09c19608a94bf2d10000000000000000000000000000000000000000000000000000000067f3bdcf9240ac502823b0d89ec923abcd895508c38ff00f763332d89a5b5acb292600632af57fc4382aa10b59e95d963dd2f39072ba9133186af6c73b939a1a5b6afd371b0000000000000000000000000000000000000000000000) [delegatecall]
│ │ ├─ [60369] 0xc0931aEE1064BD5245fEe76A2d740eab8436621e::40366475(00000000000000000000000000000000000000000000000000000000000000404254432f5553440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a955444646014254432f555344000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000103d081c72d9f475f0bd0000000000000000000000000000000000000000000000000000000067f3bdcf2e2ffc130ee5e218e62e5babe2b2c6d0fa30f07c44b3eb131bf32e8dbda99b1e419e4016c33066e7f736958fb849a20edf1f84ecc71acd6ebe1a822142d7276a1c00000000000000000000000000000000000000000000103d091c76140eff9a150000000000000000000000000000000000000000000000000000000067f3bdcf27ef18a4bb82a9d25c7384e5dc40a992a8f6c85d4099d9f77c2833641e91bef96f8c7f3e4296ad701dbd2d93ed345fde78bcbd6c430b5294b81be0eccce83efa1b00000000000000000000000000000000000000000000103d09c19608a94bf2d10000000000000000000000000000000000000000000000000000000067f3bdcf9240ac502823b0d89ec923abcd895508c38ff00f763332d89a5b5acb292600632af57fc4382aa10b59e95d963dd2f39072ba9133186af6c73b939a1a5b6afd371b0000000000000000000000000000000000000000000000) [staticcall]
│ │ │ ├─ [55380] 0xB2F863B68d85b198DDe2fE7da1D8baFdCFf199c0::40366475(00000000000000000000000000000000000000000000000000000000000000404254432f5553440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a955444646014254432f555344000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000103d081c72d9f475f0bd0000000000000000000000000000000000000000000000000000000067f3bdcf2e2ffc130ee5e218e62e5babe2b2c6d0fa30f07c44b3eb131bf32e8dbda99b1e419e4016c33066e7f736958fb849a20edf1f84ecc71acd6ebe1a822142d7276a1c00000000000000000000000000000000000000000000103d091c76140eff9a150000000000000000000000000000000000000000000000000000000067f3bdcf27ef18a4bb82a9d25c7384e5dc40a992a8f6c85d4099d9f77c2833641e91bef96f8c7f3e4296ad701dbd2d93ed345fde78bcbd6c430b5294b81be0eccce83efa1b00000000000000000000000000000000000000000000103d09c19608a94bf2d10000000000000000000000000000000000000000000000000000000067f3bdcf9240ac502823b0d89ec923abcd895508c38ff00f763332d89a5b5acb292600632af57fc4382aa10b59e95d963dd2f39072ba9133186af6c73b939a1a5b6afd371b0000000000000000000000000000000000000000000000) [delegatecall]
│ │ │ │ ├─ [3000] PRECOMPILES::ecrecover(0x6f256b7d268ebd1218609a792f492fed69e60f9b2bbb7a6aa535dd7eb100cb0a, 28, 20891172600762156370158823662620543704900665111721513626205233704845164780318, 29679939319588129535545874401977999632529215185806134268849298554013395396458) [staticcall]
│ │ │ │ │ └─ ← [Return] 0x0000000000000000000000006733d110a59dc160b5c8093066a7f5103885196f
│ │ │ │ ├─ [8142] 0x996520f9717f6141e53D8083168027d7834c172f::55b5190b(7564662d76312e310000000000000000000000000000000000000000000000000000000000000000000000006733d110a59dc160b5c8093066a7f5103885196f) [staticcall]
│ │ │ │ │ ├─ [3246] 0xA7a8eAAA131dc4D2f70636F5F8796e640B119926::55b5190b(7564662d76312e310000000000000000000000000000000000000000000000000000000000000000000000006733d110a59dc160b5c8093066a7f5103885196f) [delegatecall]
│ │ │ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000001
│ │ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000001
│ │ │ │ ├─ [3000] PRECOMPILES::ecrecover(0x8b9e9727843f84ccc91c0597d691d6605a1747bc2d35a5d80ed49bae125b3559, 27, 18062647626320149720686750389925445179000734462126404388533040417546752343801, 50454962982126875055591883219065593480805620376717083982408678482645524954874) [staticcall]
│ │ │ │ │ └─ ← [Return] 0x00000000000000000000000093b502d3eb45b9eae948f8fca01e64d9c0ba538a
│ │ │ │ ├─ [3642] 0x996520f9717f6141e53D8083168027d7834c172f::55b5190b(7564662d76312e3100000000000000000000000000000000000000000000000000000000000000000000000093b502d3eb45b9eae948f8fca01e64d9c0ba538a) [staticcall]
│ │ │ │ │ ├─ [3246] 0xA7a8eAAA131dc4D2f70636F5F8796e640B119926::55b5190b(7564662d76312e3100000000000000000000000000000000000000000000000000000000000000000000000093b502d3eb45b9eae948f8fca01e64d9c0ba538a) [delegatecall]
│ │ │ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000001
│ │ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000001
│ │ │ │ ├─ [3000] PRECOMPILES::ecrecover(0x58a2d42e31439b65105206f2c0eaf9da88c41c7ca83b26422134baee5517faa0, 27, 66151943366697264661083307274467456252907404044355893425437843030218939695203, 19430898983218506555122872121129293766952693229336645534639974540211705806135) [staticcall]
│ │ │ │ │ └─ ← [Return] 0x000000000000000000000000be524616e96bb4b62cce8034ab6bea8f2505b55a
│ │ │ │ ├─ [3642] 0x996520f9717f6141e53D8083168027d7834c172f::55b5190b(7564662d76312e31000000000000000000000000000000000000000000000000000000000000000000000000be524616e96bb4b62cce8034ab6bea8f2505b55a) [staticcall]
│ │ │ │ │ ├─ [3246] 0xA7a8eAAA131dc4D2f70636F5F8796e640B119926::55b5190b(7564662d76312e31000000000000000000000000000000000000000000000000000000000000000000000000be524616e96bb4b62cce8034ab6bea8f2505b55a) [delegatecall]
│ │ │ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000001
│ │ │ │ │ └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000001
│ │ │ │ └─ ← [Return] 0x00000000000000000000000000000000000000000000103d091c76140eff9a150000000000000000000000000000000000000000000000000000000067f3bdcf
│ │ │ └─ ← [Return] 0x00000000000000000000000000000000000000000000103d091c76140eff9a150000000000000000000000000000000000000000000000000000000067f3bdcf
│ │ ├─ emit topic 0: 0xc721ed5888a17a766f8018cb6748b49a685bdec1837b10b49c8c56de9e4d5618
│ │ │ data: 0x000000000000000000000000bd0afee88b7fcee6139467700d6f40f13feda7394254432f55534400000000000000000000000000000000000000000000000000
│ │ └─ ← [Return] 0x00000000000000000000000000000000000000000000103d091c76140eff9a150000000000000000000000000000000000000000000000000000000067f3bdcf
│ └─ ← [Return] 0x00000000000000000000000000000000000000000000103d091c76140eff9a150000000000000000000000000000000000000000000000000000000067f3bdcf
└─ ← [Stop]
Transaction successfully executed.
Gas used: 163365
The table below lists the contract addresses for the UDF Push and Pull marketplace contracts across supported networks. These addresses must be used during integration. They serve as the interaction points for accessing UDF services on specific blockchains.
When implementing your integration, ensure that the correct address for the target network is referenced in your code.
The table below provides the contract addresses for supported assets across various networks. These addresses represent the deployed instances of the assets within Entangle’s ecosystem.
Note that assets are encoded in the bytes32 EVM format using their ASCII representation converted to hexadecimal. You can perform this conversion using the below CLI commands with Python or cast. Simply replace NGL/USD in the command with the asset you are using.
In the output you should expect NGL/USD to be encoded as 0x4e474c2f55534400000000000000000000000000000000000000000000000000


sonic
arbitrum
blast
polygon
optimism
linea
ethereum
binance bnb
monad_testnet
mantle_sepolia
abstract
AIXBT/USD
0x41495842542f5553440000000000000000000000000000000000000000000000
APT/USD
0x4150542F55534400000000000000000000000000000000000000000000000000
AVAX/USD
0x415641582F555344000000000000000000000000000000000000000000000000
BCH/USD
0x4243482F55534400000000000000000000000000000000000000000000000000
BERA/USD
0x424552412F555344000000000000000000000000000000000000000000000000
BICO/USD
0x4249434F2F555344000000000000000000000000000000000000000000000000
BNB/USD
0x424E422F55534400000000000000000000000000000000000000000000000000
BTC/USD
0x4254432F55534400000000000000000000000000000000000000000000000000
CGPT/USD
0x434750542F555344000000000000000000000000000000000000000000000000
DAI/USD
0x4441492F55534400000000000000000000000000000000000000000000000000
DOGE/USD
0x444F47452F555344000000000000000000000000000000000000000000000000
DOT/USD
0x444F542F55534400000000000000000000000000000000000000000000000000
ENJ/USD
0x454E4A2F55534400000000000000000000000000000000000000000000000000
ERN/USD
0x45524E2F55534400000000000000000000000000000000000000000000000000
,
ETH/USD
0x4554482F55534400000000000000000000000000000000000000000000000000
FET/USD
0x4645542F55534400000000000000000000000000000000000000000000000000
FTM/USD
0x46544D2F55534400000000000000000000000000000000000000000000000000
GLMR/USD
0x474c4d522f555344000000000000000000000000000000000000000000000000
GORPLES/USD
0x474F52504C45532F555344000000000000000000000000000000000000000000
HYPE/USD
0x485950452f555344000000000000000000000000000000000000000000000000
ICP/USD
0x4943502F55534400000000000000000000000000000000000000000000000000
IMX/USD
0x494D582F55534400000000000000000000000000000000000000000000000000
LINK/USD
0x4C494E4B2F555344000000000000000000000000000000000000000000000000
LTC/USD
0x4C54432F55534400000000000000000000000000000000000000000000000000
MANA/USD
0x4D414E412F555344000000000000000000000000000000000000000000000000
MANTA/USD
0x4D414E54412F5553440000000000000000000000000000000000000000000000
MELANIA/USD
0x4D454C414E49412F555344000000000000000000000000000000000000000000
MNT/USD
0x4D4E542F55534400000000000000000000000000000000000000000000000000
MOVE/USD
0x4d4f56452f555344000000000000000000000000000000000000000000000000
MOVR/USD
0x4d4f56522f555344000000000000000000000000000000000000000000000000
NEAR/USD
0x4E4541522F555344000000000000000000000000000000000000000000000000
NGL/USD
0x4E474C2F55534400000000000000000000000000000000000000000000000000
PEPE/USD
0x504550452F555344000000000000000000000000000000000000000000000000
POL/USD
0x504F4C2F55534400000000000000000000000000000000000000000000000000
QUAI/USD
0x515541492f555344000000000000000000000000000000000000000000000000
RENDER/USD
0x52454E4445522F55534400000000000000000000000000000000000000000000
RLUSD/USD
0x524C5553442F5553440000000000000000000000000000000000000000000000
RON/USD
0x524f4e2f55534400000000000000000000000000000000000000000000000000
ROSE/USD
0x524F53452F555344000000000000000000000000000000000000000000000000
S/USD
0x532F555344000000000000000000000000000000000000000000000000000000
SAND/USD
0x53414E442F555344000000000000000000000000000000000000000000000000
SHIB/USD
0x534849422F555344000000000000000000000000000000000000000000000000
SOL/USD
0x534F4C2F55534400000000000000000000000000000000000000000000000000
STX/USD
0x5354582F55534400000000000000000000000000000000000000000000000000
SUI/USD
0x5355492F55534400000000000000000000000000000000000000000000000000
SUPER/USD
0x53555045522F5553440000000000000000000000000000000000000000000000
sUSDS/USD
0x73555344532F5553440000000000000000000000000000000000000000000000
TAO/USD
0x54414F2F55534400000000000000000000000000000000000000000000000000
TON/USD
0x544F4E2F55534400000000000000000000000000000000000000000000000000
TRUMP/USD
0x5452554D502F5553440000000000000000000000000000000000000000000000
TRX/USD
0x5452582F55534400000000000000000000000000000000000000000000000000
UNI/USD
0x554E492F55534400000000000000000000000000000000000000000000000000
USD0/USD
0x555344302f555344000000000000000000000000000000000000000000000000
USDS/USD
0x555344532F555344000000000000000000000000000000000000000000000000
VIRTUAL/USD
0x5649525455414c2f555344000000000000000000000000000000000000000000
WLD/USD
0x574C442F55534400000000000000000000000000000000000000000000000000
XLM/USD
0x584C4D2F55534400000000000000000000000000000000000000000000000000
XRP/USD
0x5852502F55534400000000000000000000000000000000000000000000000000
ZEREBRO/USD
0x5a45524542524f2f555344000000000000000000000000000000000000000000
base
eth_sepolia
mantle
quai
quai_orchard_testnet
berachain
5IRE/USD
0x354952452F555344000000000000000000000000000000000000000000000000
AAVE/USD
0x414156452F555344000000000000000000000000000000000000000000000000
ADA/USD
0x4144412F55534400000000000000000000000000000000000000000000000000
AIT/USD
0x4149542F55534400000000000000000000000000000000000000000000000000
python -c "import sys; print(\"0x%s\" % sys.argv[1].encode('ascii').ljust(32,b'\x00').hex())" NGL/USDcast format-bytes32-string NGL/USD