uprotocore
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # [Multidata Network Whitepaper](https://multidata.ai) Multidata Oracle is specifically designed for storing and efficiently updating a large number of on-chain price quotes. Utilizing logarithmic delta encoding for price updates, this solution maximizes both storage efficiency and cost-effectiveness. With decentralization at its core, the deterministic quoting approach enables the integration of techniques such as threshold signatures. By allowing multiple parties to construct price feeds off-chain and deliver them on-chain using a single verifiable signature, Multidata Oracle achieves significant gas savings compared to other oracle protocols. The critical role of off-chain logic in enhancing the system's overall efficiency and performance cannot be underestimated, as it enables more streamlined and cost-effective operations. ![](https://i.imgur.com/r1ojUNw.png) *Currently, a beta version of the oracle quotes 1000+ assets on:* * Multidata * Arbitrum * Aurora * Avalanche * BitTorrent Chain * Boba Network * Boba Network * BNB Chain * CELO * CLV Chain * Cronos Chain * Dogechain * Gnosis Chain * Moonriver * Moonbeam * HECO Chain * Klaytn * OKC * Polygon * Fantom * Optimism * Oasis * Harmony * Syscoin * [Ethereum](https://etherscan.io/address/0xf315a5cc91338a3886d3c3a11E7b494f3302B3fA) Chainlink-compatible backup oracle on Ethereum mainnet #### Stablecoins ``` USDC, USDT, TUSD, BUSD, sUSD, DUSD, USDN, DAI, FEI, UST, HUSD, mUSD, USDP ``` #### Tokens ``` WBTC, WETH, ORN, BOND, UMA, YAM, DNT, STAKE, BAT, MANA, YFI, 1INCH, ARMOR, DPI, NMR, VSP, KP3R, UNI, REPv2, SOCKS, CREAM, BADGER, BOR, REN, PICKLE, OXT, COVER, WOO, TRU, FTM, CVX, NU, LINK, BZRX, POND, HEGIC, LDO, GNO, SUSHI, MATIC, AAVE, OGN, ANKR, FRAX, KEEP, xSUSHI, MPH, AKRO, SXP, DUCK, SHIB, OCEAN, MKR, CRO, ADX, BAND, LRC, SNX, WOOFY, RGT, renFIL, CRV, ZRX, renBTC, ENJ, RARI, ANT, ALPHA, MM, MTA, CEL, SFI, PERP, BAL, COMP, GRT, OMG, ALCX, KNC, INJ, ANY, SYS ``` #### Compound tokens (cTokens) ``` cWBTC, cUNI, cCOMP, cLINK, cUSDT, cDAI ``` #### Yearn Vaults ``` yvCurve-HUSD, yvCurve-UST, yvCurve-USDN, husd3CRV, yvLINK, yvCurve-BUSD, yvUSDT, yvCurve-3pool, ust3CRV, yvBOOST, yvWBTC, yvCurve-FRAX, yv1INCH, yvYFI, TUSD3CRV-f, yvSNX, yvUNI, yvWETH, yvsUSD, yvCurve-TUSD ``` #### Sushiswap LP tokens ``` SLP: WETH-USDT, USDC-WETH, WBTC-WETH, UMA-WETH, YFI-WETH, WETH-yveCRV, 1INCH-WETH, COMP-WETH, WETH-CRV, REN-WETH, SUSHI-WETH, SNX-WETH, BAND-WETH, DAI-WETH, LINK-WETH, AAVE-WETH, UNI-WETH, sUSD-WETH, renDOGE-WETH ``` #### Uniswap V2 LP tokens ``` WETH-USDT, WETH-renBTC, USDC-WETH, WBTC-WETH, renZEC-WETH, WETH-AMPL, HEGIC-WETH, YFI-WETH, STAKE-WETH, WETH-CRV, SNX-WETH, WETH-renFIL, WETH-RARI, KP3R-WETH, UMA-WETH, yvBOOST-WETH, FEI-WETH, DAI-WETH, MKR-WETH, COMP-WETH, AAVE-WETH, LINK-WETH, DUCK-WETH, UNI-WETH ``` #### 3Pool LP tokens (3crv) ``` usdn3CRV, husd3CRV, ust3CRV ``` #### Stocks, Bonds, Currencies, Commodities on Gnosis Chain Contract address [`0xA0D41dA88Cce5404D407D549eB68730F78b6Be4e`](https://blockscout.com/xdai/mainnet/address/0xA0D41dA88Cce5404D407D549eB68730F78b6Be4e/transactions) #### Sources of data ``` Binance, Huobi, Kraken, BitMart, Hotbit, MEXC, OKX, Coinbase, Uniswap V2, Uniswap V3, Sushi, Compound, Curve ``` ## Specification The chosen solution involves coordinating participants and providing intermediate data storage through a side network. Benefits of using a side network include: - No central point of communication or failure - DDoS protection for participants - Easy network switching in case of complete network failure or DoS - No reliance on the side network's trustworthiness ## Flow ### Adding Assets To enable querying and updating, assets must be added to the contract. This process is simple and uses regular quoting mechanics to obtain initial prices. Access control is the same as for updating quotes, but other governance strategies can be implemented. Multiple on-chain oracles with different precision/gas cost trade-offs may exist. ### Quoting The quoting process in oracle solutions involves the deterministic interpretation of a set of rules, allowing any party to obtain the same result regardless of calculation time or party-specific conditions. Each rule typically combines data feeds from the most reliable sources of liquidity for a specific asset. A key idea behind this quoting approach is to eliminate reliance on any single party, such as centralized exchanges or decentralized exchanges. Additionally, it does not assume that major stablecoin prices are close to 1. All data feeds are implemented with built-in flash loan resistance, addressing a common vulnerability in oracle solutions. This approach fosters decentralization and enhances the robustness of the oracle system. ### Multiparty Operation At present, multiparty ECDSA is being considered as a solution for decentralization, since ECDSA verification is relatively inexpensive on the Ethereum mainnet. Auxiliary protocol message exchanges can be conducted trustlessly through more affordable networks, adding transparency and robustness to the system. This approach provides a decentralized and secure method for updating and verifying information within oracle systems. ### On-Chain Querying To obtain the price of an asset, only one function with an intuitive interface needs to be called. The interpretation of the price depends on the price format specified by the quoting rules. The oracle protocol itself is format-agnostic and flexible, allowing for seamless integration and adaptation to various use cases. /// /// ## Components ### Oracle contract The contract is quite minimalistic. For each asset, it keeps the price at some moment in the past, and the difference (delta) to calculate the last known price on the fly. It is much cheaper to store a batch of differences than to update the prices one by one. Additionally, the contract exposes several view functions to support the updater library operation (see below). ### Updater library Since the contract exposes quite a low-level data model (as well as some other primitives), the approach to updating the quotes can be quite flexible and can be changed on the fly. It will not require the contract redeployment as quote updating is coded off-chain in a form of a library. The idea is simple: if the difference between the base price and the current price can be represented by a small delta, use the delta. Otherwise, set the base price to the current price. Because a delta is valid only for a particular base price, we must ensure that the base price is not updated concurrently. To save storage read calls a simple 64-bit checksum is used. This task is also handled by the library. ### Quoting library A set of quoting rules is expressed in a configuration file. A quoting library processes the rules and constructs a tree of objects called price feeds. Here we take a modular approach to decompose a quoting task as a tree of ready-made price feeds, e.g. UniswapV3Feed. There may be subtleties in handling one or another price source. To keep the quoting approach deterministic, all these subtleties must be specified and handled by the parties in the same way. They are not allowed to expose any non-determinism or randomness. Currently, there is no formal specification of quoting rules and feeds. However, it can easily be derived from the existing implementation. We are assuming that in the future there will be alternative implementations to facilitate decentralization and reliability. ### Daemon There is the last piece to glue things together - the code that pulls data from quoting library and feeds it into the contract using the updater library. This code can be run as a Unix daemon or as a Docker container. ### Chainlink Compatibility contract We provide Chainlink-compatible aggregator (`IChainlinkAggregatorV2V3Interface`) for every quoted asset via special contract which is able to generate numerous aggregators in a gas-efficient way. ### Threat model 1. At least `M = 2 * N // 3 + 1` participants are honest, non-compromised and operational. 2. A mainnet (the network which hosts the oracle contract) is reasonably operational, live, and protected from deep reorganizations. 3. Mainnet nodes used by honest participants work correctly. 4. No significant changes of the participant set are performed at once. Only one participant set change may be affected by a mainnet reorganization at the same time. ### Multiparty protocols Are required to achieve the "implementation secure enough to provide feeds to $1B+ TVL projects" objective. 1. **Deterministic feeds**. Attach to each privileged transaction at least M out of N (v, r, s)-signatures instead of `owner`'s signature. * **cons**: - only "manual" punishment (expulsion) for dishonest nodes - signees have to compute feeds in a deterministic way, even slightest disagreement ruins liveness. - dishonest nodes may skip the quoting work by copying other's results - up to `3340 * N` extra gas cost - small changes to the main contract - small coordination helper contract for a cheap net - small daemon changes 2. **almost deterministic feeds**. In the case when there is a disagreement on the feed data, extra stage is taken to sign the feed data where for each asset the price reported by at least M participants is taken, or the current one, if no such price exists. * **cons**: - only "manual" punishment (expulsion) for dishonest nodes - signees are urged to compute feeds in a deterministic way, but slight disagreements are acceptable. - dishonest nodes may skip the quoting work by copying other's results - up to `3340 * N` extra gas cost - extra stage in a case of a disagreement - small changes to the main contract - medium-complexity coordination helper contract for a cheap net - medium-complexity daemon changes 3. **Non-deterministic feeds, commit-reveal**. The first stage: commit of hashes of feed data. The second stage: feed data reveal. After the second stage, a median is computed for each asset in the cheap net's helper contract. During the third stage, an array of resulting medians is signed by all honest participants. * **cons**: - only "manual" punishment (expulsion) for dishonest nodes - up to `3340 * N` extra gas cost - extra 2 stages - small changes to the main contract - medium-complexity coordination helper contract for a cheap net - medium-complexity daemon changes 4. **Non-deterministic feeds, median-in-the-mainnet**. For feed update transactions signees also provide feed data along with signatures. In the main contract a median of the submitted values is computed. Commit-reveal makes no sense here since a byzantine node still can send his data to the main contract. * **cons**: - only "manual" punishment (expulsion) for dishonest nodes - dishonest nodes may skip the quoting work by copying other's results - up to `(3340 + calldata) * N` extra gas cost (up to `7000 * N` for 170 assets) - small changes to the main contract - small coordination helper contract for a cheap net - medium-complexity daemon changes The last stage in the options 1-3 (M+ individual signings) may be replaced with a threshold ECDSA signature generation, eliminating the "up to `3340 * N` extra gas cost" downside, but adding complexity to the daemon. Additionally, any multi-signature participant change requires new group key generation and updating the owner of the main contract. ## Participants The oracle is managed by a set of participating parties designated by an Ethereum-compatible key pair. There is a quorum requirement (a positive number not greater than the number of the participants) to enact an oracle operation. There may be at least two approaches to cryptographically secure authorize an operation by such a participants committee: * Transmit a quorum of signatures to the mainnet and verify them on-chain using `ecrecover` in a loop. Implementation of this approach is named the multisignature case in the document below. It's a simple solution but requires an extra approx. `5300 * (quorum - 1)` gas (further optimizations are possible). * Create an Ethereum-compatible threshold signature and verify it on-chain using `ecrecover`. Signing the mainnet transaction itself is less favorable as changing the fee or the sender (see mainnet transacting schedule) requires re-signing. Implementation of this approach is named the threshold signature case in the document below. Regardless of the way chosen, most of the protocol stays the same. Differences are highlighted. ## Message hashing & signing The word "message" below in the phrases like "a hash of a message" or "a signature of a message" means a serialized typed structured data in accordance with EIP-712. `keccak256` of such a message (which must also be used during signing) must be produced according to the standard. The message will be described with a `typeHash`. `DOMAIN_SEPARATOR` must be computed as follows: ```solidity DOMAIN_SEPARATOR = keccak256( abi.encode( keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'), keccak256(bytes("Metadata.Multiparty.Protocol")), keccak256(bytes("1")), chainid, oracleContractAddress )); ``` Where `chainid` is a chain id of the mainnet contract and `oracleContractAddress` is the mainnet contract address. The phrase "create calldata" below means to create transaction data for a mentioned function signature and arguments. ## Updating the oracle ### Epoch schedule Epoch number (== epoch start time) for unix time `t` is `t / EPOCH_DURATION * EPOCH_DURATION`. An epoch is divided into stages. Stage times below are unitless fractions and must be multiplied by `EPOCH_DURATION` to get the time in seconds since the start of the epoch. Participant must process an epoch only once. ### Commit stage The stage time range is [0, 0.15). #### Participant Detect prices for all known assets in any way feasible. If for an asset the price can't be determined, use NO_PRICE (== max uint) as a special value. Create a uint prices array ordered the order of the assets. Compute the hash of the message `Commit(address sender,uint32 epochId,uint256[] prices)` where `sender` is the participant's address. Transact `commit(epochId, hash)` to the contract. #### The contract `commit(uint32 epochId, bytes32 hash)`. Check that `epochId` is current; the participant is valid; the participant has not yet transacted `commit` for the `epochId`. Record `epochId => participant => hash` & emit an event. ### Reveal stage The stage time range is [0.15, 0.20). If a quorum of the committed hashes is not present, the epoch is marked as failed (but no explicit storage write is needed), no further actions are needed. #### Participant Transact `reveal(epochId, prices, pricesSignature)` to the contract, where `prices` are the data created during the commit stage for the `epochId`. `pricesSignature` is a signature of the message `Reveal(uint32 epochId,uint256[] prices)`. *<small>Comment: `pricesSignature` is needed not to trust the side network and not to assume correct functioning of the used side network node.</small>* #### The contract `reveal(uint32 epochId, uint256[] prices, Signature pricesSignature)`. Check that `epochId` is current; the participant is valid; the participant has not yet transacted `reveal` for the `epochId`; the previously committed prices hash matches the hash of the `Commit` message constructed from `msg.sender`, `epochId`, `prices`. Emit `epochId` in an event. *<small>Comment: no need to record all the prices to the state or emit them as they can be read by participants from the tx data.</small>* ### Computation stage If a quorum of the revealed price arrays is not present, the epoch is marked as failed (but no explicit storage write is needed), no further actions are needed. #### Participant Any participant which successfully revealed during the current epoch does the following. Read successfully revealed prices from the corresponding transaction data. Take the participant set, quorum, current base prices, and epoch number from the mainnet contract. If the participant is not in the participant set, it stops the current epoch processing. Remove revealed prices reported by the participants not found on the mainnet or with incorrect `pricesSignature`. Remove duplicate submissions by the same participant. Compute the price for each asset: if no quorum is present (too many NO_PRICE or some were removed) then no price update (delta=0), the median of the prices otherwise (computed in Solidity uint terms). *<small>Comment: filtering revealed prices is an extra safety check. Normally, nothing should be removed, see Participant set synchronization.</small>* Translate resulting prices into full_update_assets, full_update_prices, new_delta_bytes arrays as in [updater.py](https://github.com/unitprotocol/neworacles/blob/7059f26f50f3798059254d28ed3c42d53c63d6a3/python/unitprotocol/fo/updater.py#L63-L111). Sign the message `Update(uint32 epochId,uint32 previousEpochId,address[] assets,uint256[] basePrices,bytes deltas)` (where `previousEpochId` is the current epoch number, `assets` are full_update_assets, `basePrices` are full_update_prices, `deltas` are new_delta_bytes). *<small>Comment: `previousEpochId` is needed because deltas are relative, and an update is applicable only to the state for which it was created.</small>* * Multisignature case: transact `signed(epochId, signature)` to the contract. * Threshold signature case: TBD #### The contract * Multisignature case: `signed(uint32 epochId, signature)`: emit `epochId, signature` for a valid participant if not emitted yet and `epochId` is current. * Threshold signature case: TBD ### Update stage #### Participant Gather a quorum of unique valid signatures or a threshold signature from the contract for the `Update` message produced at the previous stage. Use the participant set from the previous stage to check the validity of the signature participants. Transact to the mainnet contract along with the signature(s). ## Privileged functions ### Participant Create calldata for a privileged transaction, e.g.: `addAsset(asset, currentPrice, salt, deadline)`. * Multisignature case: sign the message `Vote(bytes calldata)` and transact `vote(calldata, signature)` to the contract. As soon as the quorum of unique valid signatures is present (from the current mainnet contract viewpoint), transact to the mainnet. * Threshold signature case: sign the message `Vote(bytes calldata)`. Note that each particular transaction calldata must include a unique salt to prevent replay attacks. Additionally, a deadline timestamp may be included to limit the TTL of the action proposed. Also, note that the privileged call signatures gathering requires scanning the entire side network contract lifetime for corresponding events. ### The side network contract * Multisignature case: `vote(bytes calldata, signature)`: emit an event for a valid participant. * Threshold signature case: TBD ### The main network contract Check the signatures. Check the deadline, if any. Check that the given salt is not used yet; mark the salt as used. Exec the call (via self-call, perhaps). ## Participant set synchronization Participant set synchronization (between the contracts) is desirable for participants filtering, but not required. Is done as a privileged function (see above), but with extra work performed. *<small>Comment: the on-chain participant set management code may be shared between the contracts.</small>* ### Participant Participants produce a privileged function signature(s). Participants don't initiate a new privileged function call on the side network until the current fully signed call is mined on the mainnet. Participants don't bypass the side network contract. ### The side network contract * Multisignature case: detecting set modification calls (`addParticipant`, `setQuorum`, etc) via selectors, applying them to self (via self-call, perhaps), and emitting an event when the quorum is present. * Threshold signature case: the current owner signs `NewOwner(address newOwner,uint quorum,address[] participants,uint salt,uint deadline)`, the contract extracts participants. ## Mainnet transacting schedule Even when all participants are honest, there is the tragedy of the commons (of some degree) regarding paying for mainnet transactions. A simple schedule may be suggested to address the issue. Let `T` be the time associated with some data, deterministic for all participants (e.g. `epochId` or the timestamp of the block when a quorum for the data was reached). Let `D` be an arbitrary mainnet-related duration, e.g. 1 minute for the Ethereum mainnet. Then, `i`-th participant must deliver a transaction to the mainnet during the time slot starting at `T + D * (uint(keccak256(data)) + i) % totalParticipants` of the length `D`. The absence of such a transaction can be easily proven by looking at the mainnet. ### Updating the oracle * The protocol does not rely on strict participant clock synchronization - in the worst case signed data won't match and multiparty signature won't be produced. * Moreover, it's desirable to wait for some blocks after a stage beginning to avoid network reorganizations. * The protocol relies on off-chain computations by the participants and doesn't rely on on-chain computation. Again, no multiparty signature in the worst case. Such an approach has several benefits: small gas footprint (heavy computations are impossible even on side networks), simpler code, no side network state dependence. ### Participant set synchronization As an ultimate (and quite cheap) mitigation of the de-synchronization case, a new side network contract deployment may be suggested. ## Connectors ### DEXs connectors: * UniswapV2Feed - for quoting assets at UniswapV2Like swaps: univ2, sushi, shiba, pancake * UniswapV2LPFeed - for quoting liquidity pool tokens at UniswapV2Like swaps: univ2, sushi, shiba, pancake * UniswapV3Feed - for quoting assets at UniswapV3 swap * BearingFeed - for quoting bearing tokens. * CurveLPFeed - for quoting liquidity pool tokens at curve.finance pools * CurveFeed - for quoting assets at curve.finance pools * YvFeed - for quoting yv tokens * CompoundFeed - for quoting compound tokens ### CEXs connectors: * SimpleStockFeed - quote of stocks * BinanceFeed - for quoting assets at binance * HuobiFeed - huobi * KrakenFeed - kraken * GateioFeed - gateio * CoinbaseSimpleFeed - returns symbol price ### Other feeds: * MulFeed - multiply price of one feed to another * MedianFeed - find median value from multiple feeds prices, allowed to set count of absent values * InvertFeed - 1 / feed price * AliasFeed - add synonym to feed name * ConstantFeed - for int constants * FallbackFeed - Queries a list of feeds and returns the first successful response ### Multidata MedianFeeds: A Cost-Effective Alternative for Managing Metric Data on Blockchain Networks Multidata MedianFeeds presents an innovative alternative approach to directly writing data on the target network, where transactional gas costs can be prohibitively expensive. By employing a multi-data chain with free gas and fallback to a cheaper network, MedianFeeds effectively minimizes costs while maintaining efficiency. This system uses tiny epochs, direct transactions from validators, and multiparty updates for metric and validator sets. Through a robust validation process involving Merkle tree roots, proofs, and metric value checks, MedianFeeds ensures data integrity and security. The accompanying JavaScript SDK also allows seamless integration with decentralized applications (DApps), making it a versatile and cost-effective solution for managing large-scale metric data. ### High-Level Overview MedianFeeds: * Tiny epoch (5 min) within a multidata chain with fallback to a cheaper network * Direct transactions from each validator * Not multiparty, since synchronization requires time * Multiparty update for metric set and validator set * Calculates median for each metric Validators: * Collect values for a large number of metrics * Write all of them to MedianFeeds * At the end of each round, retrieve values of all metrics on the block at the end of the epoch * Create a Merkle tree and obtain the root * Sign the Merkle tree root for each target network through a multiparty mechanism * Send the root and VRS to MedianFeeds Proof Feeds: * Accept root, VRS, proof, metric value, timestamp, and epoch * Verify that the root signature is correct * Validate that the Merkle proof is valid for the root JavaScript SDK: * Allows DApps to retrieve metric values from MedianFeeds * Collects values of all metrics on the block at the end of the last epoch * Generates a Merkle tree, root, and proof for the required metric Metric Values Users: * Obtain epoch, Merkle root, VRS, Merkle proof, metric ID, metric value, and metric update timestamp from the JavaScript SDK * Send this data to their own contract * The contract checks if the metric update timestamp is suitable for its purposes * The contract calls MerkleFeeds and verifies that the metric value and metric update timestamp are correct #### MedianFeeds Standard `ICoreMetadataOracleReader` interface for reading. Due to implementation limitations, the price 2**256-1 is not supported. Update values function: `update(uint32 epoch_, uint[] calldata metricIds_, uint256[] calldata prices_)` Set signed Merkle tree root for the previous epoch for each target network: ```solidity function setSignedMerkleTreeRoot( uint chainId_, address contractAddr_, uint32 epoch_, bytes32 root_, uint8 v_, bytes32 r_, bytes32 s_ ); ``` Administrative functions (adding validators, adding/updating metrics) are the same as in the Multiparty oracle. This function can be called several times per epoch by each validator, but with different metric IDs. It's recommended to send batches of 100 values. In this case, the gas cost will usually range between 1,138,512 and 9,262,586 (best to worst-case, depending on which metrics the median is calculated for). #### Validators The update of the metric and validator sets is the same as in MultiPartyFeeds. It is necessary to port the following to Python (or find an existing implementation): https://github.com/OpenZeppelin/merkle-tree. Each epoch, every validator sends all available values to MedianFeeds. At the end of each MedianFeeds epoch (on the block of epoch end): * Retrieve quotes for all metrics * Calculate Merkle tree root for all values (epoch, metric ID, metric values, update timestamp ("uint32", "uint256", "uint256", "uint32")). See https://github.com/OpenZeppelin/merkle-tree * Sign Merkle tree root for each contract in target networks and send to MedianFeeds (setSignedMerkleTreeRoot) It might be necessary to allow sending roots not only from the previous epoch ## Main concepts The connection of Multidata oracle to your smartcontract is quite easy. The examples below show how to get prices of `WETH` and `UNI-V2 WETH-CRV` and on Gnosis chain. ### Proxies Development of contracts is continuing and a new version of the oracle contract could be deployed. To avoid changing the oracle contract address on each such deployment consumers can use the address of the proxy contract, which has the same interface (`ICoreMetadataOracleReader`) as the oracle contract. After the deployment of a new version of the oracle contract (and if the new version is backward compatible) implementation of the proxy is changed and migration for proxy users is seamless. ### Base 2**112 for prices All prices are stored with base 2**112. It allows to sure values less than zero. - 0.01 is stored as 0.01 * 2**112 = 51922968585348276285304963292200 - 100 is stored as 100 * 2**112 = 519229685853482762853049632922009600 ## Examples on popular languages (multiparty feed) ### Solidity To use oracle's prices in your smartcontract interface `ILegacyMetadataOracleV0_1` should be used. ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./interfaces/legacy/ILegacyMetadataOracleV0_1.sol"; contract DataConsumerLegacy { ILegacyMetadataOracleV0_1 internal oracle; /** * network: Gnosis * oracle address: 0xf315a5cc91338a3886d3c3a11E7b494f3302B3fA */ constructor() { oracle = ILegacyMetadataOracleV0_1(0xf315a5cc91338a3886d3c3a11E7b494f3302B3fA); } /** * Returns prices of `ETH` and `UNI-V2 WETH-CRV` in USD */ function getLatestPrices() public view returns (uint[2] memory) { address[] memory assets = new address[](2); assets[0] = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; assets[1] = 0x3dA1313aE46132A397D90d95B1424A9A7e3e0fCE; ILegacyMetadataOracleV0_1.Quote[] memory values = oracle.quoteAssets(assets); return [ values[0].price / 2**112, values[1].price / 2**112 ]; } } ``` ## JavaScript ### web3 This example uses [web3.js](https://web3js.readthedocs.io/) to fetch prices of `ETH` and `UNI-V2 WETH-CRV` in Gnosis chain. ```js const Web3 = require("web3") const ORACLE_ADDR = '0xf315a5cc91338a3886d3c3a11E7b494f3302B3fA'; const RPC_URL = "https://rpc.gnosischain.com"; const ASSETS = [ '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', '0x3dA1313aE46132A397D90d95B1424A9A7e3e0fCE', ] const oracleReaderABI = [{"inputs": [{"internalType": "address[]","name": "assets","type": "address[]"}],"name": "quoteAssets","outputs": [{"components": [{"internalType": "uint256","name": "price","type": "uint256"},{"internalType": "uint32","name": "updateTS","type": "uint32"}],"internalType": "struct ILegacyMetadataOracleV0_1.Quote[]","name": "quotes","type": "tuple[]"}],"stateMutability": "view","type": "function"}] const web3 = new Web3(RPC_URL) const oracle = new web3.eth.Contract(oracleReaderABI, ORACLE_ADDR) oracle.methods.quoteAssets(ASSETS) .call() .then((prices) => { // handle code console.log("Latest prices", prices) }) ``` ### ethers.js This example uses [ethers.js](https://docs.ethers.io/) to fetch prices of `ETH` and `UNI-V2 WETH-CRV` in Gnosis chain. ```js const { ethers } = require("ethers") const ORACLE_ADDR = '0xf315a5cc91338a3886d3c3a11E7b494f3302B3fA'; const RPC_URL = "https://rpc.gnosischain.com"; const ASSETS = [ '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', '0x3dA1313aE46132A397D90d95B1424A9A7e3e0fCE', ] const oracleReaderABI = [{"inputs": [{"internalType": "address[]","name": "assets","type": "address[]"}],"name": "quoteAssets","outputs": [{"components": [{"internalType": "uint256","name": "price","type": "uint256"},{"internalType": "uint32","name": "updateTS","type": "uint32"}],"internalType": "struct ILegacyMetadataOracleV0_1.Quote[]","name": "quotes","type": "tuple[]"}],"stateMutability": "view","type": "function"}] const provider = new ethers.providers.JsonRpcProvider(RPC_URL) const oracle = new ethers.Contract(ORACLE_ADDR, oracleReaderABI, provider) oracle.quoteAssets(ASSETS) .then((prices) => { // handle code console.log("Latest prices", prices) }) ``` ## Python This example uses [web3.py](https://web3py.readthedocs.io/) to fetch prices of `ETH` and `UNI-V2 WETH-CRV` in Gnosis chain. ```python from web3 import Web3 ORACLE_ADDR = '0xf315a5cc91338a3886d3c3a11E7b494f3302B3fA' RPC_URL = "https://rpc.gnosischain.com" ASSETS = [ '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', '0x3dA1313aE46132A397D90d95B1424A9A7e3e0fCE', ] ORACLE_READER_ABI = [{"inputs": [{"internalType": "address[]","name": "assets","type": "address[]"}],"name": "quoteAssets","outputs": [{"components": [{"internalType": "uint256","name": "price","type": "uint256"},{"internalType": "uint32","name": "updateTS","type": "uint32"}],"internalType": "struct ILegacyMetadataOracleV0_1.Quote[]","name": "quotes","type": "tuple[]"}],"stateMutability": "view","type": "function"}] web3 = Web3(Web3.HTTPProvider(RPC_URL)) contract = web3.eth.contract(address=ORACLE_ADDR, abi=ORACLE_READER_ABI) latestPrices = contract.functions.quoteAssets(ASSETS).call() print(latestPrices) ``` # For Chainlink users We provide Chainlink-compatible aggregator (`IChainlinkAggregatorV2V3Interface`) for every quoted asset which allows to get the latest price of asset. See below example of getting prices for `ETH` and `UNI-V2 WETH-CRV` with chainlink compatible aggregators in Gnosis chain ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./interfaces/IChainlinkAggregatorV2V3Interface.sol"; contract DataConsumerChainlinkCompatibility { IChainlinkAggregatorV2V3Interface internal aggregatorEth; IChainlinkAggregatorV2V3Interface internal aggregatorLpWethCrv; /** * network: Gnosis */ constructor() { aggregatorEth = IChainlinkAggregatorV2V3Interface(0x05B10FAd302f202809BF2Fd1F1456e211C1A9B20); aggregatorLpWethCrv = IChainlinkAggregatorV2V3Interface(0xD3DCD5fEB3ffB266C6d9829607271dEa61eBa84C); } /** * Returns prices of `ETH` and `UNI-V2 WETH-CRV` in USD */ function getLatestPrices() public view returns (uint[2] memory) { (,int priceEth,,,) = aggregatorEth.latestRoundData(); (,int priceLP,,,) = aggregatorLpWethCrv.latestRoundData(); return [ uint(priceEth) / 10**8, uint(priceLP) / 10**8 ]; } } ``` # Oracle Api reference ## ILegacyMetadataOracleV0_1 Functions in `ILegacyMetadataOracleV0_1` | Name | Description | Returns | |------------------------------------------|----------------------------------------------|-----------------------------------------------| | `getStatus` | Returns last update TS of prices | `Status {uint32 updateTS;uint64 pricesHash;}` | | `getAssets` | Gets a list of assets quoted by this oracle. | `address[]` | | `hasAsset(address asset)` | Checks if an asset is quoted by this oracle. | `bool` | | `quoteAssets(address[] calldata assets)` | Gets last known quotes for the assets | `Quote[] {uint256 price;uint32 updateTS;}` | # Examples of usage by DEFI projects Let's describe line-by-line how it works: ```solidity ... interface IOracleUsd { function assetToUsd(address asset, uint256 amount) external view returns (uint256); } /// @title MetadataOracle wrapper for Unit protocol contract UnitMetadataOracle is IOracleUsd { // Multidata oracle's address is stored in immutable. It allows to save gas on read of this variable ICoreMetadataOracleReader public immutable metadataOracle; // Also immutable variable, stores max allowed price age uint256 public immutable maxPriceAgeSeconds; constructor(address metadataOracle_, uint256 maxPriceAgeSeconds_) { metadataOracle = ICoreMetadataOracleReader(metadataOracle_); maxPriceAgeSeconds = maxPriceAgeSeconds_; } /** * @notice Evaluates the cost of amount of asset in USD. * @dev reverts on non-supported asset or stale price. * @param asset evaluated asset * @param amount amount of asset in the smallest units * @return result USD value, scaled by 10**18 * 2**112 */ function assetToUsd(address asset, uint256 amount) external view override returns (uint256 result) { // Prepare arguments for calling oracle. Despite the dact that we need to quote only one price // we still need to make array address[] memory input = new address[](1); input[0] = asset; // quoteAssets returns array. Since we pass only one asset in argument only one element is returned. ICoreMetadataOracleReader.Quote memory quote = metadataOracle.quoteAssets(input)[0]; // Here we see very important concept of using oracles: we need to check that price returned by oracle // is up to date. Usage of outdated price may lead to losses require(block.timestamp - quote.updateTS <= maxPriceAgeSeconds, 'STALE_PRICE'); // Multidata oracle store price for whole unit of currency (taking in account 2**112 base) // For example price for 1 eth is stored as X * 2**112 USD where X is the price in USD // Unit protocol wants to get price for amount of asset passed in min units of asset in USD with decimals 18 // For example to get price for 1 WETH unit protocol passes 10**18 as amount // and wants to get X * 10**18 * 2**112 in response where X is the price in USD // If asset has decimal <> 18 price must be scaled accordingly uint256 decimals = uint256(IERC20Like(asset).decimals()); require(decimals < 256); // Let's assume that we need to get price for 1 USDT which has decimals = 6. // 10**6 is passed as amount // scaleDecimals = 18-6=12 int256 scaleDecimals = 18 - int256(decimals); // result = 1 * 2**112 * 10**6 result = quote.price * amount; if (scaleDecimals > 0) // result = 1 * 2**112 * 10**6 * 10**12 = 1 * 2**112 * 10**18. So price 1 with base 2**112 and decimals 18 is returned result *= uint256(10) ** uint256(scaleDecimals); else if (scaleDecimals < 0) result /= uint256(10) ** uint256(-scaleDecimals); } } ``` ## Simple stable with Multidata oracle example As an example of integration BubHub oracle to DEFI projects let's create simple stablecoin backed with several assets. ### Requirements For our example we formulate just a few requirements. Please see section [What next?](#what-next) additionally - Issue stablecoin backed with any asset for which oracle has price - Repay debt ### Developing of the contract First will create skeleton of our stable. We will inherit from OpenZeppelin ERC20 contract and will pass oracle's address as constructor argument. ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "../interfaces/legacy/ILegacyMetadataOracleV0_1.sol"; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract SimpleStable is ERC20 { ILegacyMetadataOracleV0_1 internal oracle; constructor(ILegacyMetadataOracleV0_1 oracle_) ERC20('Stablecoin', 'STBL') { oracle = oracle_; } } ``` Then will add structures to store information about issued stables and stored collaterals for user. And also will add skeletons of methods for borrow and repay debt. To execute `borrow` method user must approve `assetAmount_` of `asset_` to our contract. ```solidity ... /** * @notice user => asset => debt */ mapping (address => mapping (address => uint)) public debts; mapping (address => mapping (address => uint)) public collaterals; ... function borrow(address asset_, uint assetAmount_) public { } function repay(address asset_) public { } ... ``` Next will add `nonReentrant` modificator for our methods to prevent malicious actions from assets (they could try to call methods of stable coin inside of `asset`.`transfer` method) ```solidity ... import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; contract SimpleStable is ERC20, ReentrancyGuard { ... function borrow(address asset_, uint assetAmount_) public nonReentrant { ... function repay(address asset_) public nonReentrant { ... ``` Then let's add method for retrieving price of asset. Method makes request to Multidata oracle network, checks that price is up-to-date and returns price. ```solidity ... uint public constant MAX_PRICE_AGE = 3 hours; ... function getPriceBase112(address asset_) internal view returns (uint) { address[] memory assets = new address[](1); assets[0] = asset_; ILegacyMetadataOracleV0_1.Quote[] memory values = oracle.quoteAssets(assets); require(block.timestamp - values[0].updateTS <= MAX_PRICE_AGE, 'STALE_PRICE'); return values[0].price; } ... ``` Next will implement borrow method. This method gets collateral from user and issue stables to this user. ```solidity ... import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; ... contract SimpleStable is ERC20, ReentrancyGuard { using SafeERC20 for IERC20; ... function borrow(address asset_, uint assetAmount_) public nonReentrant { // we can get as collateral only assets for which we know price require(oracle.hasAsset(asset_), "Asset is not supported"); // to simplify example we allow borrow only once per asset require(debts[msg.sender][asset_] == 0, "Debt already exists"); // calculation of total debt: // debt = price * amount * 10**(decimals of stable) / 10**(decimals of asset) / 2**112 // lets look deeply to components of this formula // - price - price in oracles is store with multiplied to 2**112 and stored for 1 unit of asset. // - amount - we want to know price of all collateral // - 10**(decimals of stable) / 10**(decimals of asset) = 10**(18-assetDecimals) - adjusting with decimals of asset and our stable coin (=18) // - 2**112 - divide to price base uint assetDecimals = IERC20Metadata(asset_).decimals(); uint debt = getPriceBase112(asset_) * assetAmount_ * 10**(18-assetDecimals) / 2**112; // save information about debt and stored collateral debts[msg.sender][asset_] = debt; collaterals[msg.sender][asset_] = assetAmount_; // mint stablecoin right to user _mint(msg.sender, debt); // get collateral from user. We use safeTransfer to fail on any error during transfer IERC20(asset_).safeTransferFrom(msg.sender, address(this), assetAmount_); } ... ``` And the last method for repay debt ```solidity ... function repay(address asset_) public nonReentrant { // execute only for borrowers uint debt = debts[msg.sender][asset_]; require(debt > 0, "No debt"); // collateral of borrower uint collateralAmount = collaterals[msg.sender][asset_]; // clear info about debt and collaterals debts[msg.sender][asset_] = 0; collaterals[msg.sender][asset_] = 0; // burn stablecoin right from user's account. No need to approve stablecoin _burn(msg.sender, debt); // transfer collateral to user. Fail on eny error IERC20(asset_).safeTransfer(msg.sender, collateralAmount); } ... ``` That's all :) Full code of contract you can see on [SimpleStable.sol](/contracts/defi/SimpleStable.sol). Also you can see simple test for this contract [here](/test/defi/SimpleStableTest.js). In this test [Mock of oracle](/contracts/test-helpers/OracleMock.sol) was used. ### Collateralization ratio We issue stablecoin for full values of collateral. It is wrong since after some small price dump we will have value of issued stables < values of collaterals. It will lead to depeg of stable coin. To avoid this such parameter as `Initial collateralization ratio (ICR)` must be introduced. `ICR` = `Issued stablecoins` / `Current value of colateral`. For example with CR = 70% with collateral with value $100 only 70 stablecoin could be issued. Going further since different assets have different volatility `ICR` must be different for different assets. For more volatile assets (for example which price can change for 100%/day) CR must be small. For more stable assets CR could be bigger. ### Changes in position To simplify example we allow issuing stablecoints only once for each asset and repay only whole debt. It is convenient for user to increase/decrease their position. `ICR` of total debt and total value of collateral must be taken in account in such cases. ### Liquidations What happens if price dump is much more than `Collateralization ratio` could compensate? We will have unbacked debt again. To avoid such situtions liquidation mechanism and `Liquidation ratio` must be introduced. If current `CR` is more `ICR` for some value (`Liquidation ratio`) auction to sell debt of user must be started. After debt is bought in auction, buyer receives part of collateral (values as repaid stables + some premium), the rest of collateral goes to initial borrower. ## Multidata Network Roadmap 1. Factory of user-defined oracles 1. API for historical data * as an additional exporter * daily logs - to a S3 bucket 3. Liquidity control. * `Uniswap*Feed` : don't produce a quote if the pool is illiquid. * the same for `Curve*` * control for CEXes? 4. Multi-party. 5. More tests for feeds. 6. Use type `2` Ethereum transactions on Ethereum mainnet. 7. Contracts v2. * externally set epoch timestamps (plus: for replay attack protection). Done in multiparty. * `setPrices+updateDeltas` combined function. Done in multiparty. * `epoch+baseprices+assets+...` combined getter. Done in multiparty. * increase the compiler runs parameter, but check the effects on gas and contract sizes * can `AggregatorShim` be replaced with lightweight proxies a la in Wrapped SSLP? * individual update timestamps for assets * doc-comments 9. Computations. * use `Decimal` everywhere * remove any `float` intermediaries 10. Split the `neworacles` monolith into a set of reusable libraries (packages) and services (in dedicated repos) 11. Multi-tiered liquidity-price results, e.g.: `[($100_000, $0.5), ($1000_000, $0.4), ($10_000_000, $0.2)]`. 12. Don't require intermediate feed naming in config. * e.g., write Mul(UniswapFeed(...), ETHUSD) without a name for UniswapFeed. 13. Automatic feed detection & configuration. For fucking everything. 14. Super integration tests (?Smoke-tests) * on push to master run instance with the same oracles config as in production * write prices to testnet * check that prices were written and maybe compare them to cmc 14. More data feeds * Currencies * Commodities * Bonds ### Undisputed objectives 1. Implementation secure enough to provide feeds to $1B+ TVL projects. 2. Gas-efficient. 3. Each asset must have an update timestamp. ## Glossary **Logarithmic delta encoding** is a technique that is used to minimize the amount of data that needs to be transmitted when updating a value, such as a price quote. It works by only transmitting the difference between the new value and the previous value, rather than the entire new value. This can be particularly useful when the values being transmitted are expected to change significantly over time, as it allows for more efficient updates by transmitting only the changes rather than the entire new value. Together with batched updates, this yields impressive network fee savings. **Elliptic Curve Digital Signature Algorithm (ECDSA)** is a cryptographic algorithm that is used to create digital signatures. It is a variant of the Digital Signature Algorithm (DSA) and is based on the mathematics of elliptic curves. Digital signatures are used to verify the authenticity and integrity of a message or piece of data. They work by generating a unique, unforgeable signature for the data using a private key, which can then be verified using a corresponding public key. This allows the recipient of the signed data to verify that it has not been tampered with and that it was indeed sent by the owner of the private key. ECDSA is widely used in various applications, including blockchain technology, where it is often used to create digital signatures for transactions. It is considered to be more secure and efficient than some other digital signature algorithms, and is used in many cryptographic protocols, including SSL/TLS, PGP, and SSH. **EIP-712** is an Ethereum Improvement Proposal that outlines a standard for creating structured messages in the Ethereum ecosystem. Structured messages are data structures that can be signed and verified by Ethereum wallets and smart contracts, and they can be used to communicate complex data between parties in a secure and verifiable way. EIP-712 defines a standard format for structured messages, including the fields that must be included and the encoding rules that must be followed. It also specifies the use of domain separation, which allows multiple structured messages to coexist within the same Ethereum ecosystem without conflicting with each other. EIP-712 was created to improve the usability and security of communication in Ethereum. **keccak256** is a cryptographic hash function that is used to create a fixed-size, unique hash value (also called a message digest) from an input message. It is part of the SHA-3 (Secure Hash Algorithm 3) family of hash functions, which were designed to be secure and resistant to attack. keccak256 is commonly used in Ethereum to create a unique identifier for a contract or other data on the blockchain. The hash value is computed by applying the keccak256 algorithm to the input message, and it is typically represented as a string of 64 hexadecimal characters. Because the hash value is fixed-size and unique, it can be used to verify the integrity and authenticity of the input message, as well as to detect any changes to the message. **Multi-tiered liquidity-price** results are one of the important tools in financial markets for determining the relationship between liquidity and price for a particular asset. In simple terms, liquidity refers to the amount of an asset that is available for trade, while price refers to the cost of purchasing the asset. Understanding the relationship between these two factors is crucial for traders and investors, as it allows them to make informed decisions about when and how to buy or sell an asset. One way to represent this relationship is through the use of multi-tiered liquidity-price results. These results are essentially lists of liquidity-price pairs that are sorted in order of increasing liquidity. Each pair indicates the liquidity and price associated with a particular trade size. For example, a liquidity-price pair may indicate that a liquidity of $100,000 is associated with a price of $0.5, while another pair may indicate that a liquidity of $1,000,000 is associated with a price of $0.4. Multi-tiered liquidity-price results are often used by financial institutions, such as banks and brokers, to determine the optimal trade size and price for a particular asset. **A price-weighted index** is a type of stock market index that reflects the changes in the value of a group of stocks based on the price of each stock, rather than the market capitalization (the total value of all the shares of a company). The value of the index is calculated by taking the sum of the prices of all the stocks in the index, and dividing it by the total number of stocks in the index. The value of the index is then adjusted for any changes in the number of stocks in the index. For example, suppose we have a price-weighted index that tracks the prices of three stocks: Stock A, Stock B, and Stock C. The prices of these stocks are $100, $50, and $200, respectively. The value of the index would be calculated as follows: (100 + 50 + 200) / 3 = $116.67 If the price of Stock B were to increase to $75, the value of the index would be recalculated as follows: (100 + 75 + 200) / 3 = $133.33 **Price-weighted indices** are typically used to track the performance of a group of stocks that have similar characteristics, such as stocks in a particular sector or industry. They are generally considered to be a simple and easy-to-understand way to track the performance of a group of stocks. However, they have some drawbacks compared to other types of stock market indices. For example, because the value of the index is determined by the prices of the individual stocks, it is more susceptible to changes in the prices of a few heavily weighted stocks, rather than the overall performance of the group as a whole. Additionally, price-weighted indices do not take into account the market capitalization of the stocks in the index, which can lead to a bias towards larger, more established companies. **Capitalization-weighted index** is a type of financial index that measures the performance of a group of securities, such as stocks or bonds, in a market. The weight of each security in the index is determined by its market capitalization, which is calculated by multiplying the security's price by the number of outstanding shares. Securities with a higher market capitalization have a greater influence on the performance of the index. For example, suppose an index includes three stocks: Stock A, Stock B, and Stock C. Stock A has a market capitalization of $100 million, Stock B has a market capitalization of $50 million, and Stock C has a market capitalization of $25 million. The weight of Stock A in the index would be 40%, the weight of Stock B would be 20%, and the weight of Stock C would be 10%. This means that the performance of Stock A would have a greater impact on the overall performance of the index compared to the performance of Stock B and Stock C. Capitalization-weighted indexes are commonly used to track the performance of a particular market or sector. For example, the S&P 500 is a well-known capitalization-weighted index that tracks the performance of the 500 largest publicly traded companies in the United States. Other examples of capitalization-weighted indexes include the NASDAQ 100, which tracks the performance of the 100 largest non-financial companies listed on the NASDAQ stock exchange, and the FTSE 100, which tracks the performance of the 100 largest companies listed on the London Stock Exchange. # Contacts [Site](https://multidata.ai) [Explorer](https://explorer.multidata.ai) hello@multidata.ai

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully