/** * HTTP API: WP_Http_Curl class * * @package WordPress * @subpackage HTTP * @since 4.4.0 */ /** * Core class used to integrate Curl as an HTTP transport. * * HTTP request method uses Curl extension to retrieve the url. * * Requires the Curl extension to be installed. * * @since 2.7.0 * @deprecated 6.4.0 Use WP_Http * @see WP_Http */ #[AllowDynamicProperties] class WP_Http_Curl { /** * Temporary header storage for during requests. * * @since 3.2.0 * @var string */ private $headers = ''; /** * Temporary body storage for during requests. * * @since 3.6.0 * @var string */ private $body = ''; /** * The maximum amount of data to receive from the remote server. * * @since 3.6.0 * @var int|false */ private $max_body_length = false; /** * The file resource used for streaming to file. * * @since 3.6.0 * @var resource|false */ private $stream_handle = false; /** * The total bytes written in the current request. * * @since 4.1.0 * @var int */ private $bytes_written_total = 0; /** * Send a HTTP request to a URI using cURL extension. * * @since 2.7.0 * * @param string $url The request URL. * @param string|array $args Optional. Override the defaults. * @return array|WP_Error Array containing 'headers', 'body', 'response', 'cookies', 'filename'. A WP_Error instance upon error */ public function request( $url, $args = array() ) { $defaults = array( 'method' => 'GET', 'timeout' => 5, 'redirection' => 5, 'httpversion' => '1.0', 'blocking' => true, 'headers' => array(), 'body' => null, 'cookies' => array(), 'decompress' => false, 'stream' => false, 'filename' => null, ); $parsed_args = wp_parse_args( $args, $defaults ); if ( isset( $parsed_args['headers']['User-Agent'] ) ) { $parsed_args['user-agent'] = $parsed_args['headers']['User-Agent']; unset( $parsed_args['headers']['User-Agent'] ); } elseif ( isset( $parsed_args['headers']['user-agent'] ) ) { $parsed_args['user-agent'] = $parsed_args['headers']['user-agent']; unset( $parsed_args['headers']['user-agent'] ); } // Construct Cookie: header if any cookies are set. WP_Http::buildCookieHeader( $parsed_args ); $handle = curl_init(); // cURL offers really easy proxy support. $proxy = new WP_HTTP_Proxy(); if ( $proxy->is_enabled() && $proxy->send_through_proxy( $url ) ) { curl_setopt( $handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP ); curl_setopt( $handle, CURLOPT_PROXY, $proxy->host() ); curl_setopt( $handle, CURLOPT_PROXYPORT, $proxy->port() ); if ( $proxy->use_authentication() ) { curl_setopt( $handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY ); curl_setopt( $handle, CURLOPT_PROXYUSERPWD, $proxy->authentication() ); } } $is_local = isset( $parsed_args['local'] ) && $parsed_args['local']; $ssl_verify = isset( $parsed_args['sslverify'] ) && $parsed_args['sslverify']; if ( $is_local ) { /** This filter is documented in wp-includes/class-wp-http-streams.php */ $ssl_verify = apply_filters( 'https_local_ssl_verify', $ssl_verify, $url ); } elseif ( ! $is_local ) { /** This filter is documented in wp-includes/class-wp-http.php */ $ssl_verify = apply_filters( 'https_ssl_verify', $ssl_verify, $url ); } /* * CURLOPT_TIMEOUT and CURLOPT_CONNECTTIMEOUT expect integers. Have to use ceil since. * a value of 0 will allow an unlimited timeout. */ $timeout = (int) ceil( $parsed_args['timeout'] ); curl_setopt( $handle, CURLOPT_CONNECTTIMEOUT, $timeout ); curl_setopt( $handle, CURLOPT_TIMEOUT, $timeout ); curl_setopt( $handle, CURLOPT_URL, $url ); curl_setopt( $handle, CURLOPT_RETURNTRANSFER, true ); curl_setopt( $handle, CURLOPT_SSL_VERIFYHOST, ( true === $ssl_verify ) ? 2 : false ); curl_setopt( $handle, CURLOPT_SSL_VERIFYPEER, $ssl_verify ); if ( $ssl_verify ) { curl_setopt( $handle, CURLOPT_CAINFO, $parsed_args['sslcertificates'] ); } curl_setopt( $handle, CURLOPT_USERAGENT, $parsed_args['user-agent'] ); /* * The option doesn't work with safe mode or when open_basedir is set, and there's * a bug #17490 with redirected POST requests, so handle redirections outside Curl. */ curl_setopt( $handle, CURLOPT_FOLLOWLOCATION, false ); curl_setopt( $handle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS ); switch ( $parsed_args['method'] ) { case 'HEAD': curl_setopt( $handle, CURLOPT_NOBODY, true ); break; case 'POST': curl_setopt( $handle, CURLOPT_POST, true ); curl_setopt( $handle, CURLOPT_POSTFIELDS, $parsed_args['body'] ); break; case 'PUT': curl_setopt( $handle, CURLOPT_CUSTOMREQUEST, 'PUT' ); curl_setopt( $handle, CURLOPT_POSTFIELDS, $parsed_args['body'] ); break; default: curl_setopt( $handle, CURLOPT_CUSTOMREQUEST, $parsed_args['method'] ); if ( ! is_null( $parsed_args['body'] ) ) { curl_setopt( $handle, CURLOPT_POSTFIELDS, $parsed_args['body'] ); } break; } if ( true === $parsed_args['blocking'] ) { curl_setopt( $handle, CURLOPT_HEADERFUNCTION, array( $this, 'stream_headers' ) ); curl_setopt( $handle, CURLOPT_WRITEFUNCTION, array( $this, 'stream_body' ) ); } curl_setopt( $handle, CURLOPT_HEADER, false ); if ( isset( $parsed_args['limit_response_size'] ) ) { $this->max_body_length = (int) $parsed_args['limit_response_size']; } else { $this->max_body_length = false; } // If streaming to a file open a file handle, and setup our curl streaming handler. if ( $parsed_args['stream'] ) { if ( ! WP_DEBUG ) { $this->stream_handle = @fopen( $parsed_args['filename'], 'w+' ); } else { $this->stream_handle = fopen( $parsed_args['filename'], 'w+' ); } if ( ! $this->stream_handle ) { return new WP_Error( 'http_request_failed', sprintf( /* translators: 1: fopen(), 2: File name. */ __( 'Could not open handle for %1$s to %2$s.' ), 'fopen()', $parsed_args['filename'] ) ); } } else { $this->stream_handle = false; } if ( ! empty( $parsed_args['headers'] ) ) { // cURL expects full header strings in each element. $headers = array(); foreach ( $parsed_args['headers'] as $name => $value ) { $headers[] = "{$name}: $value"; } curl_setopt( $handle, CURLOPT_HTTPHEADER, $headers ); } if ( '1.0' === $parsed_args['httpversion'] ) { curl_setopt( $handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0 ); } else { curl_setopt( $handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1 ); } /** * Fires before the cURL request is executed. * * Cookies are not currently handled by the HTTP API. This action allows * plugins to handle cookies themselves. * * @since 2.8.0 * * @param resource $handle The cURL handle returned by curl_init() (passed by reference). * @param array $parsed_args The HTTP request arguments. * @param string $url The request URL. */ do_action_ref_array( 'http_api_curl', array( &$handle, $parsed_args, $url ) ); // We don't need to return the body, so don't. Just execute request and return. if ( ! $parsed_args['blocking'] ) { curl_exec( $handle ); $curl_error = curl_error( $handle ); if ( $curl_error ) { if ( PHP_VERSION_ID < 80000 ) { // curl_close() has no effect as of PHP 8.0. curl_close( $handle ); } return new WP_Error( 'http_request_failed', $curl_error ); } if ( in_array( curl_getinfo( $handle, CURLINFO_HTTP_CODE ), array( 301, 302 ), true ) ) { if ( PHP_VERSION_ID < 80000 ) { // curl_close() has no effect as of PHP 8.0. curl_close( $handle ); } return new WP_Error( 'http_request_failed', __( 'Too many redirects.' ) ); } if ( PHP_VERSION_ID < 80000 ) { // curl_close() has no effect as of PHP 8.0. curl_close( $handle ); } return array( 'headers' => array(), 'body' => '', 'response' => array( 'code' => false, 'message' => false, ), 'cookies' => array(), ); } curl_exec( $handle ); $processed_headers = WP_Http::processHeaders( $this->headers, $url ); $body = $this->body; $bytes_written_total = $this->bytes_written_total; $this->headers = ''; $this->body = ''; $this->bytes_written_total = 0; $curl_error = curl_errno( $handle ); // If an error occurred, or, no response. if ( $curl_error || ( 0 === strlen( $body ) && empty( $processed_headers['headers'] ) ) ) { if ( CURLE_WRITE_ERROR /* 23 */ === $curl_error ) { if ( ! $this->max_body_length || $this->max_body_length !== $bytes_written_total ) { if ( $parsed_args['stream'] ) { if ( PHP_VERSION_ID < 80000 ) { // curl_close() has no effect as of PHP 8.0. curl_close( $handle ); } fclose( $this->stream_handle ); return new WP_Error( 'http_request_failed', __( 'Failed to write request to temporary file.' ) ); } else { if ( PHP_VERSION_ID < 80000 ) { // curl_close() has no effect as of PHP 8.0. curl_close( $handle ); } return new WP_Error( 'http_request_failed', curl_error( $handle ) ); } } } else { $curl_error = curl_error( $handle ); if ( $curl_error ) { if ( PHP_VERSION_ID < 80000 ) { // curl_close() has no effect as of PHP 8.0. curl_close( $handle ); } return new WP_Error( 'http_request_failed', $curl_error ); } } if ( in_array( curl_getinfo( $handle, CURLINFO_HTTP_CODE ), array( 301, 302 ), true ) ) { if ( PHP_VERSION_ID < 80000 ) { // curl_close() has no effect as of PHP 8.0. curl_close( $handle ); } return new WP_Error( 'http_request_failed', __( 'Too many redirects.' ) ); } } if ( PHP_VERSION_ID < 80000 ) { // curl_close() has no effect as of PHP 8.0. curl_close( $handle ); } if ( $parsed_args['stream'] ) { fclose( $this->stream_handle ); } $response = array( 'headers' => $processed_headers['headers'], 'body' => null, 'response' => $processed_headers['response'], 'cookies' => $processed_headers['cookies'], 'filename' => $parsed_args['filename'], ); // Handle redirects. $redirect_response = WP_Http::handle_redirects( $url, $parsed_args, $response ); if ( false !== $redirect_response ) { return $redirect_response; } if ( true === $parsed_args['decompress'] && true === WP_Http_Encoding::should_decode( $processed_headers['headers'] ) ) { $body = WP_Http_Encoding::decompress( $body ); } $response['body'] = $body; return $response; } /** * Grabs the headers of the cURL request. * * Each header is sent individually to this callback, and is appended to the `$header` property * for temporary storage. * * @since 3.2.0 * * @param resource $handle cURL handle. * @param string $headers cURL request headers. * @return int Length of the request headers. */ private function stream_headers( $handle, $headers ) { $this->headers .= $headers; return strlen( $headers ); } /** * Grabs the body of the cURL request. * * The contents of the document are passed in chunks, and are appended to the `$body` * property for temporary storage. Returning a length shorter than the length of * `$data` passed in will cause cURL to abort the request with `CURLE_WRITE_ERROR`. * * @since 3.6.0 * * @param resource $handle cURL handle. * @param string $data cURL request body. * @return int Total bytes of data written. */ private function stream_body( $handle, $data ) { $data_length = strlen( $data ); if ( $this->max_body_length && ( $this->bytes_written_total + $data_length ) > $this->max_body_length ) { $data_length = ( $this->max_body_length - $this->bytes_written_total ); $data = substr( $data, 0, $data_length ); } if ( $this->stream_handle ) { $bytes_written = fwrite( $this->stream_handle, $data ); } else { $this->body .= $data; $bytes_written = $data_length; } $this->bytes_written_total += $bytes_written; // Upon event of this function returning less than strlen( $data ) curl will error with CURLE_WRITE_ERROR. return $bytes_written; } /** * Determines whether this class can be used for retrieving a URL. * * @since 2.7.0 * * @param array $args Optional. Array of request arguments. Default empty array. * @return bool False means this class can not be used, true means it can. */ public static function test( $args = array() ) { if ( ! function_exists( 'curl_init' ) || ! function_exists( 'curl_exec' ) ) { return false; } $is_ssl = isset( $args['ssl'] ) && $args['ssl']; if ( $is_ssl ) { $curl_version = curl_version(); // Check whether this cURL version support SSL requests. if ( ! ( CURL_VERSION_SSL & $curl_version['features'] ) ) { return false; } } /** * Filters whether cURL can be used as a transport for retrieving a URL. * * @since 2.7.0 * * @param bool $use_class Whether the class can be used. Default true. * @param array $args An array of request arguments. */ return apply_filters( 'use_curl_transport', true, $args ); } } When TVL Lies: A Practical Guide to DeFi Analytics and Dashboards for U.S. Users – Shweta Poddar Weddings Photography

Imagine you are a researcher slicing TVL (Total Value Locked) across protocols to justify an institutional pitch, or a DeFi user hunting yield across chains while avoiding surprise contract risk. You open a dashboard, see a headline TVL number, and make a decision—allocate capital, file a report, or write a tweet. That moment is worth interrogating because headline metrics can hide structural choices, measurement artefacts, and incentives. The better you understand how dashboards assemble data, the less likely you are to be blindsided by a rebase, a bridged-asset delisting, or a temporary oracle mismatch.

This article walks through the mechanics of modern DeFi analytics platforms and dashboards, illustrates where common metrics break, compares three approaches to building dashboards, and gives practical heuristics U.S.-based users and researchers can apply immediately. The goal: one sharper mental model for TVL and protocol analytics, one corrected misconception about “objective” numbers, and a few concrete checklists for assessing yield opportunities without mistaking noise for signal.

Animated loader used by a DeFi analytics dashboard showing multi-chain aggregation; useful to illustrate cross-chain data flow and timing issues

How modern DeFi dashboards assemble their story

At a mechanism level, dashboards do three things: (1) gather on-chain state (balances, pool reserves, contract calls) from many blockchains; (2) normalize and price assets to a common unit (usually USD); (3) present aggregated metrics (TVL, volumes, fees, revenue). Each step contains trade-offs. Data ingestion can be direct node queries or third-party indexers; pricing can use on-chain or off-chain oracles or heuristic market averages; aggregation can be per-protocol, per-chain, per-asset or hybrid. Different choices change the meaning of the headline metric.

Take TVL. It sounds simple—sum of assets locked—but which assets? LP tokens? Vault derivatives? Wrapped bridged tokens? How are illiquid assets priced? Dashboards that aim for completeness may include wrapped assets and derivative positions; those that aim for conservatism exclude bridged tokens until a known liquidity window exists. Both are defensible but lead to materially different TVL numbers and risk exposures. Understanding the assumptions behind the number is essential before using TVL as a risk proxy, an investment trigger, or a ranking signal.

Case study: aggregator-of-aggregators and user-facing swaps

Aggregators that sit on top of other aggregators introduce another layer of design decisions. A specific architecture runs swaps through the native router contracts of underlying aggregators rather than wrapping trades in proprietary smart contracts. Mechanically, this preserves the security model of the base platforms: the user’s trade executes directly on 1inch, CowSwap, Matcha, etc., and any platform-level airdrop eligibility attached to interacting with those routers remains intact. It also means the aggregator can operate without user accounts or personal data collection—important for privacy-conscious users and relevant to U.S. compliance conversations where custody and data retention are scrutinized.

That architecture has operational consequences. Because the aggregator does not insert its own contract, it cannot levy additional swap fees without changing the underlying router behavior. Instead, monetization often happens via referral revenue sharing by attaching referral codes to routed swaps. For users, this preserves price parity with directly using the aggregator while allowing the platform to earn a slice of existing fees. For researchers, it means trade volumes reported through such a dashboard more closely match the underlying aggregator activity and are less likely to be distorted by internal fee mechanics.

Comparing three dashboard philosophies: completeness, conservatism, and usability

Consider three viable design goals and what each gives up:

  • Completeness attempts to count every token and derivative across chains. Pros: broad coverage for multi-chain research and TVL comparisons. Cons: can overstate economic substance; wrapped or bridged tokens inflate TVL unless carefully annotated.
  • Conservatism excludes ambiguous or illiquid instruments, focusing on assets with clear redemption paths. Pros: lower false positives for solvency risk. Cons: understates exposure to emerging ecosystems and may miss early yield opportunities.
  • Usability simplifies choices into ranked lists, P/F and P/S ratios, and one-click filters. Pros: faster decision-making for traders. Cons: masks subtle modelling assumptions unless those are surfaced in metadata.

There is no uniquely correct choice—only a fit-for-purpose choice. For U.S. institutions doing compliance reviews, conservatism often wins. For active traders hunting alpha across many chains, completeness plus strong annotations is preferable. For public-facing dashboards seeking broad adoption, usability with transparent links to raw data is pragmatic.

Where metrics break: three common failure modes

Failure mode 1 — pricing mismatch: hourly or daily price feeds can lag fast markets. When a volatile token experiences a sharp move, TVL denominated in USD will change substantially even if on-chain balances do not. The practical implication is that short-term TVL changes can reflect price volatility rather than real user flows.

Failure mode 2 — double counting: LP tokens and vault strategies that wrap LP tokens can be counted twice unless the dashboard unwraps positions. A researcher relying on raw TVL numbers without checking composition risks overstating protocol concentration across the ecosystem.

Failure mode 3 — bridge and wrapped-token ambiguity: multi-chain dashboards must decide whether wrapped/bridged assets represent “real” liquidity on the destination chain. This is a policy choice; some platforms include them and annotate source chains, others exclude them until smart contract audits or withdrawal proofs are robust. For U.S. users, where custody and regulatory clarity are concerns, distinguishing native liquidity from synthetic representations is a meaningful analytic boundary.

Practical heuristics: what to check before you act

If you have five minutes, use this checklist before allocating capital or publishing a headline:

  • Open the TVL breakdown by asset. Are wrapped/bridged tokens a large share? If yes, treat TVL as exposure to bridge counterparty risk.
  • Inspect the time resolution. Is the dashboard reporting hourly, daily, or weekly points? Use higher granularity for active trading decisions and daily for strategic views.
  • Find the fees and revenue tabs. If possible, compute implied yield (protocol fees / TVL). Some dashboards offer P/F or P/S ratios—understand whether these use on-chain fees or off-chain accruals.
  • Check the swap execution model if you plan to trade. Aggregators that route trades through native router contracts preserve airdrop eligibility and security models, and often attach referral codes rather than charging extra fees—important for both privacy and cost.

One useful rule of thumb: when TVL and fees diverge (high TVL, low fees), ask whether capital is passive (like staking with fixed rewards) or earning transaction fees. That distinction changes expected returns and resilience to market stress.

Developer tools, APIs, and reproducible research

Good dashboards expose APIs and open-source code so researchers can reproduce results. Access to hourly and daily data enables event studies—e.g., measuring TVL flows around U.S. regulatory announcements or analyzing how new DEX routing integration affects executed prices. For serious analysis, prefer platforms that allow programmatic access and provide data provenance (which chain, which contract address, how tokens were priced). An open access model also reduces sampling bias in academic and policy research because you don’t need proprietary feeds to test hypotheses.

One practical pointer: when re-running a study, snapshot the dashboard’s data schema and price source. A difference in oracle choice can flip a P/F ratio inference from attractive to unattractive. Small modelling differences compound quickly when multiplied across thousands of contracts and dozens of chains.

Limitations, trade-offs, and what to watch next

Limitations are central, not cosmetic. No dashboard can perfectly represent economic risk. Oracles can be manipulated, wrapped tokens can be depegged, and indexing can fail in edge cases. Equally, less-inclusive metrics can be misleadingly conservative. The trade-off boils down to transparency: prefer dashboards that show provenance, let you drill into contract-level balances, and provide multiple denominators (native token units, USD at different oracles, and protocol-specific metrics like active users or transaction counts).

Signals to watch in the near term (conditional scenarios): if more analytics platforms adopt referral-based monetization with zero added swap fees, user cost friction will fall and on-chain swap volumes may increase, all else equal. Conversely, if regulators in the U.S. impose data-retention or KYC requirements on analytics operators, privacy-preserving, no-sign-up models could face pressure—an event that would change how users evaluate on-chain visibility versus privacy.

Finally, one non-obvious insight: valuation metrics borrowed from traditional finance—Price-to-Fees (P/F), Price-to-Sales (P/S)—work as starting points but need protocol-specific calibrations. A lending protocol’s fee capture dynamics are not the same as a DEX’s. Treat these ratios as lenses, not final answers: they compare how markets price cash-flow intensity, but are sensitive to distribution mechanics (who receives fees) and tokenomics (inflation schedules, vesting). Always ask: who gets the fees, and how sticky is that revenue?

FAQ

Q: How reliable is TVL as a proxy for protocol health?

A: TVL is a useful starting point but incomplete. It measures capital quantity, not revenue quality, risk of withdrawal (liquidity), or counterparty exposures (bridges, wrapped tokens). Combine TVL with fee metrics, active user counts, and contract-level balance checks. Also inspect asset composition and oracle choices to avoid being misled by price moves or double-counting.

Q: Which dashboard should I trust for cross-chain research?

A: Trust choices depend on your goals. For broad, open access and multi-chain coverage with developer tooling, favor platforms that provide APIs and transparent data provenance. If you need conservative figures for compliance, pick dashboards that exclude ambiguous bridged tokens or clearly annotate them. For an example of a multi-chain, open-access analytics service that emphasizes privacy and routes swaps through native aggregators, see defillama.

Takeaway: dashboards are interpretive tools, notacles that print truth. Read the metadata, question the aggregation choices, and combine quantitative dashboards with qualitative checks (contracts, audits, and community signals). For U.S. users balancing yield chasing and regulatory awareness, the disciplined approach is to use multiple dashboards, prefer platforms that show raw contract data, and treat valuation ratios as hypothesis-generating instruments rather than final verdicts.

Uncategorized

Leave a Comment

Your email address will not be published. Required fields are marked *