lido
      • 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
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Write
        • Owners
        • Signed-in users
        • Everyone
        Owners 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
    • Make a copy
    • Transfer ownership
    • Delete this note
    • 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 Help
Menu
Options
Engagement control Make a copy 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
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Write
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners 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
    # 📝 Curated Module v2. Spec ![cm_v2](https://hackmd.io/_uploads/r1X6FO3bZx.png) Curated Module v2 (CM v2) is a new chapter for the Lido protocol. Based on the solid codebase of CSM, it inherits new concepts introduced in the latest [CSM v3](https://hackmd.io/@lido/csm-v3-spec), like support for `0x02` validators, integration features, and many more. A robust bonding mechanism allows CM v2 to streamline Node Operator management, reduce operational complexity, and increase protocol security. With CM v2, the curated part of the Lido Validator set will benefit from all the advantages CSM has to offer. Since most of the essential features of CM v2 are inherited from the latest version of CSM (CSM v3), this document will be framed as an amendment to the original [CSM v3 Specification](https://hackmd.io/@lido/csm-v3-spec). It will only cover aspects that are new or different in CM v2 compared to CSM v3. > Terms validator, key, validator key, and deposit data meanings are the same within the document - [Project repo](https://github.com/lidofinance/community-staking-module) - Written in [Solidity 0.8.33](https://github.com/ethereum/solidity/tree/v0.8.33) - Developed in [Foundry](https://github.com/foundry-rs/foundry) ## General Architecture ![image](https://hackmd.io/_uploads/ryNEb7U8bl.png) The scheme above illustrates the smart contract architecture of CM v2 and the changes made compared to CSM v3. ### New and changed contracts :::info For the missing contract descriptions, please refer to the [CSM v3 Specification](https://hackmd.io/@lido/csm-v3-spec). ::: #### `CuratedModule.sol` *CuratedModule on the scheme* :::info New in CM v2 ::: `CuratedModule.sol` is a core module contract conforming to the `IStakingModule` and `IStakingModuleV2` interfaces. The contract inherits from `BaseModule.sol` (cotains all common parts with CSM) to allow for the extended functionality while maintaining consistency with the original CSM. It stores information about Node Operators and deposit data (DD). This contract is responsible for all interactions with the `StakingRouter`, namely, the DD management and some of the Node Operator's parameters. Node Operators manage their validator keys and other parameters that they can modify through this contract. **Changes compared to CSM v3:** - The management system for the NodeOperators address is changed to allow custom role members to set both `rewardAddress` and `managerAddress` in emergency case. - Stake distribution is done via a ["greedy" weighted stake allocation strategy](#Allocation-Strategy). #### `CuratedGate.sol` *CuratedGates on the scheme* :::info New in CM v2 ::: `CuratedGate.sol` is a supplementary contract that enables Node Operator creation for the vetted addresses, which serves as an entry point to `CuratedModule.sol`. Alongside Node Operator creation, a contract can assign a custom Node Operator type (bondCurveId) in `Accounting.sol`. Deployed using `CuratedGateFactory.sol` to allow the addition of the new instances later without additional code security audits. The list of curated participants is individually upgradable for each instance of the `CuratedGate.sol` using a dedicated EasyTrack factory. #### `MetaOperatorsRegistry.sol` *MetaRegistry on the scheme* :::info New in CM v2 ::: `MetaOperatorsRegistry.sol` is a supplementary contract that stores information about `OperatorGroups` and operator weights (see [Meta Operators Registry](#Meta-Operators-Registry)). It also stores Node Operator names and descriptions. Both Node Operators and CM v2 Committee multisig can edit the values. ### Off-chain tools and actors #### `CM Bot` :::info Changed in CM v2 ::: `CM Bot` is a daemon application that monitors and reports withdrawal, slashing, and consolidation events associated with CM validators. Also responsible for validator ejection invocation due to strikes. > [CM Bot repo](https://github.com/lidofinance/csm-prover-tool) **Changes in CM v2:** - The bot can now also serve CM v2. There will be a separate instances for CSM and CM. #### `CM Oracle` :::info Changed in CM v2 ::: `CM Oracle` (also known as CM Performance Oracle) is a module in the common Lido on Ethereum (LoE) Oracle set. It is operated by the existing Oracles set alongside [Accounting Oracle](https://docs.lido.fi/contracts/accounting-oracle), [Validator Exit Bus Oracle](https://docs.lido.fi/contracts/validators-exit-bus-oracle), and CSM Performance Oracle. It is responsible for calculating the CM v2 Node Operators' reward distribution and strike assignment based on their performance on the CL. `CM Oracle` uses the same code as [`CSM Oracle`](https://hackmd.io/@lido/csm-v3-spec#CSM-Oracle) does. #### `CMC` :::info New in CM v2 ::: `CMC` (also known as CM Committee Multisig) is an off-chain committee responsible for overseeing CM v2 and managing several flows and operations within it. Somewhat similar to [Community Staking Module Committee](https://research.lido.fi/t/community-staking-module-committee/8333) Multisig, but with the extended permissions. ## Fundamental concepts ### Meta Operators Registry `CuratedModule.sol` uses `MetaOperatorsRegistry.sol` contract to fetch information about Node Operators' stake and allocation weights. `MetaOperatorsRegistry.sol` has a permissioned method (expected to be called by Curated Module Committee) to add information about `OperatorGroup`. `OperatorGroup` has the following form: ```solidity struct OperatorGroup { SubNodeOperator[] subNodeOperators, // A list of sub-node operators ExternalOperator[] externalOperators // A list of external operators data } struct SubNodeOperator { uint64 nodeOperatorId // Id of the sub node operator in CMv2 uint16 share // Sub-node operator share in BP } struct ExternalOperator { uint8 externalOpperatorType // Type of the external operator bytes externalOperatorData // Generelized data about external operator outside CMv2 } ``` These structures are used to determine effective weights for sub-node operators and to account for stake in external modules. #### Sub-node operator weights Sub-node operator weights are calculated as follows. Assume we have 3 sub-node-operators with weights $w_1$, $w_2$, $w_3$, and the sub-node operator shares $s_1$, $s_2$, $s_3$ . Then, effective weights for the sub-node operators are $ew_1 = (w_1*s_1)/10000$, $ew_2 =(w_2*s_2)/10000$, $ew_3 =(w_3*s_3)/10000$ respectively. ![image](https://hackmd.io/_uploads/B1oSe0yIbx.png) The sum of the effective weights should not exceed $max(w_1, w_2, w_3)$. Hence, $\sum_{i=1}^{n}s_i \le 10000$. To ensure consistency we can require this sum to be equal to 10_000 BP when creating, or updating `OperatorGroup`. `SubNodeOperator.share` allows node operators to determine stake distribution between their sub-node operators. For example, if the operator wants to get ~80% of the stake allocated to their DVT setup, and the rest to the vanilla sub-node operator, they can set shares to 8000 BP, and 2000 BP. :::info `SubNodeOperator.share` does not guarantee the exact stake split between operators, but rather determine the weight distribution between sub-node operators If sub-node operators have different original weights. ::: #### Accounting for the external stake Now we need to determine how to account for the stake in the external modules. We need to fetch active stake for all `ExternalOperator` record in `OperatorGroup`, sum it up into $totalExternalStake$ and calculate additional stake for each sub-node-operator in the group as $es_1 = totalExternalStake * ew_1 /(ew_1+ew_2+ew_3)$, $es_2 = totalExternalStake * ew_2 /(ew_1+ew_2+ew_3)$, and $es_3 = totalExternalStake * ew_3 /(ew_1+ew_2+ew_3)$ respectively. This will allow us to split the external stake with respect to the sub-node-operators' effective weights and ensure fair stake allocation. ![image](https://hackmd.io/_uploads/ryc32vk8bx.png) #### Restrictions - One operator can be a part of only one `OperatorGroup`. - One external operator can be associated with only one `OperatorGroup`. - Only CMv1 should be supported in the first version of `MetaOperatorsRegistry.sol` as an external module. - The sum of sub-node operator shares should always be 10000 BP. - If the operator is not a participant of an `OperatorGroup`, return 0 weight for this node operator to disallow stake allocation to the operators not added to the group. :::info If the operator is not added to the `OperatorGroup` and gets some allocations, after adding them to the group with the other sub-node-operators, we might end up with the stake over the target allocation on this Node Operator. Hence, we prohibit allocations to the operators not added to the `OperatorGroup`. ::: ### Allocation Strategy CM v2 uses the "greedy" weighted stake allocation strategy to determine the order in which keys are provided for deposits. The high-level description of the algorithm is the following: First, `currentStake` and `targetStake` are determined for each Node Operator in the module. `targetStake` is defined with respect to the Node Operator allocation weight obtained from [Meta Operators Registry](#Meta-Operators-Registry) as `targetStake = totalStakeInModule * operatorWeight / totalOperatorsWeight`. `currentStake` also accounts for external stake via [Meta Operators Registry](#Meta-Operators-Registry). ![image](https://hackmd.io/_uploads/SkVg08ABWl.png) Second, all Node Operators are sorted by their `imbalance`, where `imbalance = max(0, targetStake - currentStake)`, and `capacities` are determined for each Node Operator. ![image](https://hackmd.io/_uploads/rk4FCUCHWg.png) Next, stake is allocated as follows: - We start allocating stake from the most imbalanced Node Operator - We allocate stake to this Node Operator until: - Node Operator target stake is reached (1) OR - Node Operator capacity is exhausted (2) OR - There is no more stake to allocate (3) - Then we move to the next most imbalanced Node Operator ![image](https://hackmd.io/_uploads/BJuizPCrWg.png) ## Main flows :::info For the missing flow descriptions, please refer to the [CSM v3 Specification](https://hackmd.io/@lido/csm-v3-spec). ::: ### Create Node Operator :::info Changed in CM v2 - Node Operator creation is done via `CuratedGates` and requires no keys or bond upload. ::: ![image](https://hackmd.io/_uploads/r10ogvJI-l.png) Node Operator creation uses [`CuratedGate.sol`](#CuratedGatesol) instances attached to [`CuratedModule.sol`](#CuratedModulesol) via `CREATE_NODE_OPERATOR_ROLE`. Unlike CSM, CM v2 assumes that the creation of the Node Operator is performed without uploading deposit data or a bond. Deposit data can be uploaded later using the flow below. Different instances of [`CuratedGate.sol`](#CuratedGatesol) represent different Node Operator types. Node Operators can join Curated Module v2 using the [`CuratedGate.sol`](#CuratedGatesol) instance, where their address is added to the participants list. Once used, the address can not be used again to join via the same instance of [`CuratedGate.sol`](#CuratedGatesol). #### Operator addition to `OperatorGroup` To become eligible for deposits, a Node Operator should be a part of an OperatorGroup in the `MetaOperatorsRegistry.sol`. Adding to the group is a permissioned operation performed by CMC. ### Stake Allocation :::info Changed in CM v2 - FIFO queues are replaced with a "greedy" weighted stake allocation strategy [described above](#Allocation-Strategy). - Unlike CSM v3 that can have deployments supporting either `0x01` or `0x02` validator WC, CM v2 only supports `0x02`. ::: #### Initial `32 ETH` deposits ![image](https://hackmd.io/_uploads/SyeCeP1IWl.png) Once uploaded, deposit data becomes available for allocation of the initial `32 ETH` deposits. To allocate initial deposits to the CM v2 Node Operators, the `StakingRouter` calls the `obtainDepositData(depositsCount)` method to retrieve the next `depositsCount` depositable keys, as determined by the stake [allocation strategy](#Allocation-Strategy). #### Top-ups for `0x02` keys ![image](https://hackmd.io/_uploads/r1n0lw1Ubl.png) `0x02` validators get deposits in two phases: - [Initial `32 ETH` deposit](#Initial-32-ETH-deposits). - Top-ups up to `2048 ETH`. The top-up phase involves a call from the `StakingRouter` to [`CuratedModule.sol`](#CuratedModulesol), providing information about the keys planned for top-up and top-up limits calculated using CL proofs. [`CuratedModule.sol`](#CuratedModulesol) calculates the stake allocation using the `maxDepositAmount` provided and allocates the stake among the provided keys, respecting the calculated allocation and the `topUpLimits` supplied by the `StakingRouter`. [`CuratedModule.sol`](#CuratedModulesol) also verifies that the keys provided belong to the corresponding Node Operators for security reasons. ### Node Operator Addresses Management :::info Changed in CM v2 - A dedicated role capable of changing both manager and reward addresses is added. ::: Node Operators in CM v2 can manage their addresses in the same way as CSM Node Operators. The primary difference is that in CM v2, designated role members can update both the manager and reward addresses. It is assumed that these changes are made directly by the Lido DAO via on-chain voting or by a designated emergency committee, which acts only if the Node Operator's `managerAddress` is compromised and no longer reachable. To streamline Node Operator operations, it is proposed to set `extendedManagerPermissions = true` for all Node Operators in CM v2. This will allow for flexibility in address management. ## Contracts specifications :::info For the missing contract specifications, please refer to the [CSM v3 Specification](https://hackmd.io/@lido/csm-v3-spec). ::: ### [`CuratedModule.sol`](https://github.com/lidofinance/community-staking-module/tree/develop/docs/src/src/CuratedModule.sol) ### [`CuratedGate.sol`](https://github.com/lidofinance/community-staking-module/tree/develop/docs/src/src/CuratedGate.sol) ### [`MetaOperatorsRegistry.sol`](https://github.com/lidofinance/community-staking-module/tree/develop/docs/src/src/MetaOperatorsRegistry.sol) ## Administrative actions Curated Module v2 contracts support a set of administrative actions, including: - Changing the configuration options. - Upgrading the system's code. Each action can only be performed by a designated admin (`DEFAULT_ADMIN_ROLE`) or other role members. Only members of `DEFAULT_ADMIN_ROLE` can manage role members for the roles in CM v2 contracts. ## Roles to actors mapping ### [`CuratedModule.sol`](#CuratedModulesol) | Role | Assignee | | ------------------------------------------ | ------------------------------------------------- | | `DEFAULT_ADMIN_ROLE` | Aragon Agent | | `PAUSE_ROLE` | Gate Seal contract and DG Reseal Manager | | `RESUME_ROLE` | DG Reseal Manager | | `STAKING_ROUTER_ROLE` | `StakingRouter` contract | | `REPORT_GENERAL_DELAYED_PENALTY_ROLE` | CM Committee Multisig | | `SETTLE_GENERAL_DELAYED_PENALTY_ROLE` | `EasyTrackEVMScriptExecutor` | | `VERIFIER_ROLE` | [`Verifier.sol`](#Verifier) | | `REPORT_REGULAR_WITHDRAWN_VALIDATORS_ROLE` | [`Verifier.sol`](#Verifier) | | `REPORT_SLASHED_WITHDRAWN_VALIDATORS_ROLE` | `EasyTrackEVMScriptExecutor` | | `RECOVERER_ROLE` | Not assigned by default | | `CREATE_NODE_OPERATOR_ROLE` | Instances of [`CuratedGate.sol`](#CuratedGatesol) | | `OPERATOR_ADDRESSES_ADMIN_ROLE` | Not assigned by default | ### `Accounting.sol` | Role | Assignee | | ------------------------- | --------------------------------------------------------------------------- | | `DEFAULT_ADMIN_ROLE` | Aragon Agent | | `PAUSE_ROLE` | Gate Seal contract and DG Reseal Manager | | `RESUME_ROLE` | DG Reseal Manager | | `MANAGE_BOND_CURVES_ROLE` | Not assigned by default | | `SET_BOND_CURVE_ROLE` | Instances of [`CuratedGate.sol`](#CuratedGatesol) | | `RECOVERER_ROLE` | Not assigned by default | ### `FeeDistributor.sol` | Role | Assignee | | -------------------- | ----------------------- | | `DEFAULT_ADMIN_ROLE` | Aragon Agent | | `RECOVERER_ROLE` | Not assigned by default | ### `FeeOracle.sol` | Role | Assignee | | -------------------------------- | ---------------------------------------------------- | | `DEFAULT_ADMIN_ROLE` | Aragon Agent | | `SUBMIT_DATA_ROLE` | Not assigned by default | | `PAUSE_ROLE` | Gate Seal contract and DG Reseal Manager | | `RESUME_ROLE` | DG Reseal Manager | | `RECOVERER_ROLE` | Not assigned by default | | `MANAGE_CONSENSUS_CONTRACT_ROLE` | Not assigned by default | | `MANAGE_CONSENSUS_VERSION_ROLE` | Not assigned by default | ### `HashConsensus.sol` | Role | Assignee | | -------------------------------- | ----------------------- | | `DEFAULT_ADMIN_ROLE` | Aragon Agent | | `MANAGE_MEMBERS_AND_QUORUM_ROLE` | Aragon Agent | | `DISABLE_CONSENSUS_ROLE` | Not assigned by default | | `MANAGE_FRAME_CONFIG_ROLE` | Not assigned by default | | `MANAGE_FAST_LANE_CONFIG_ROLE` | Not assigned by default | | `MANAGE_REPORT_PROCESSOR_ROLE` | Not assigned by default | ### `Verifier.sol` | Role | Assignee | | -------------------- | ---------------------------------------------------- | | `DEFAULT_ADMIN_ROLE` | Aragon Agent | | `PAUSE_ROLE` | Gate Seal contract and DG Reseal Manager | | `RESUME_ROLE` | DG Reseal Manager | ### `ParametersRegistry.sol` :::info Note that the contract uses a custom role check modifier that allows both `roleMember` and `roleAdmin` to call methods ::: | Role | Assignee | | ------------------------------------------- | ----------------------- | | `DEFAULT_ADMIN_ROLE` | Aragon Agent | | `MANAGE_GENERAL_PENALTIES_AND_CHARGES_ROLE` | CM Committee Multisig | | `MANAGE_KEYS_LIMIT_ROLE` | Not assigned by default | | `MANAGE_QUEUE_CONFIG_ROLE` | Not assigned by default | | `MANAGE_PERFORMANCE_PARAMETERS_ROLE` | Not assigned by default | | `MANAGE_REWARD_SHARE_ROLE` | Not assigned by default | | `MANAGE_VALIDATOR_EXIT_PARAMETERS_ROLE` | Not assigned by default | ### `Ejector.sol` | Role | Assignee | | ---------------------------- | ---------------------------------------------------- | | `DEFAULT_ADMIN_ROLE` | Aragon Agent | | `PAUSE_ROLE` | Gate Seal contract and DG Reseal Manager | | `RESUME_ROLE` | DG Reseal Manager | | `RECOVERER_ROLE` | Not assigned by default | ### `ValidatorStrikes.sol` | Role | Assignee | | ---------------------------- | ----------------------- | | `DEFAULT_ADMIN_ROLE` | Aragon Agent | ### `ExitPenalties.sol` This contract does not have roles. ### [`CuratedGate.sol`](#CuratedGatesol) | Role | Assignee | | -------------------- | ---------------------------- | | `DEFAULT_ADMIN_ROLE` | Aragon Agent | | `PAUSE_ROLE` | CM Committee Multisig | | `RESUME_ROLE` | Not assigned by default | | `SET_TREE_ROLE` | `EasyTrackEVMScriptExecutor` | | `RECOVERER_ROLE` | Not assigned by default | ### [`MetaOperatorsRegistry.sol`](#MetaOperatorsRegistrysol) `TBD` ## Upgradability [`CuratedModule.sol`](#CuratedModulesol), `Accounting.sol`, `FeeOracle.sol`, `FeeDistributor.sol`, `ParametersRegistry.sol`, `ValidatorStrikes.sol`, `ExitPenalties.sol`, [`CuratedGate.sol`](#CuratedGatesol), and [`MetaOperatorsRegistry.sol`](#MetaOperatorsRegistrysol) are upgradable using [OssifiableProxy](https://github.com/lidofinance/community-staking-module/blob/main/src/lib/proxy/OssifiableProxy.sol) contracts. [`Verifier.sol`](#Verifier), [`HashConsensus.sol`](#HashConsensussol), and [`Ejector.sol`](#Ejectorsol) are not upgradable and should be redeployed if needed. ## Security considerations CM v2 shares all of the security considerations mentioned for CSM v3 in [CSM v3 Specification](https://hackmd.io/@lido/csm-v3-spec#Security-considerations). ## Known issues CM v2 shares all of the known issues mentioned for CSM v3 in [CSM v3 Specification](https://hackmd.io/@lido/csm-v3-spec#Known-issues). ## Links - [CSM v3 Specification](https://hackmd.io/@lido/csm-v3-spec) - [CM v2 Features Summary](https://hackmd.io/@lido/cm-v2-features) - [LIP-29. Community Staking Module v2](https://github.com/lidofinance/lido-improvement-proposals/blob/develop/LIPS/lip-29.md) - [LIP-26. Community Staking Module](https://github.com/lidofinance/lido-improvement-proposals/blob/develop/LIPS/lip-26.md) - [DG Reseal Manager](https://github.com/lidofinance/dual-governance/blob/main/contracts/ResealManager.sol)

    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