Verilog Solutions
      • 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
    • Engagement control
    • 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 Versions and GitHub Sync Note Insights Sharing URL Help
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
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
    3
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    ###### tags: `Final Report` STEPN Audit === > Copyright © 2022 by Verilog Solutions. All rights reserved. > April 4, 2022 > by **Verilog Solutions** ![STEPN-Cover](https://hackmd.io/_uploads/HJNicndmc.png) This report presents our engineering engagement with STEPN, a Game-Fi/Social-Fi Web3 application. Users could acquire STEPN NFT sneakers and earn rewards by engaging in outdoor activities. STEPN tokens have two tokens: **GST** & **GMT**. --- ## Table of Content [TOC] --- ## Project Summary STEPN is a Game-Fi/Social-Fi project with a dual-token system (GMT and GST) and an NFT system (STEPN Sneaker). Users acquire the STEPN Sneaker to participate in the move-and-earn program and earn GST and/or GMT. GMT and GST can be used to upgrade Sneakers and increase the rate of earning. GMT is the governance token, and it is currently deployed on Solana and BNB Chain. GST is the unlimited-supply reward token, and it is currently deployed on Solana. --- ## Service Scope Our review focused on the [**main** branch](https://github.com/stepnxyz/bnbcontracts), specifically, commit hash [**c07ff86e5e8060de0cd10d6842405964c2cc5d13**](https://github.com/stepnxyz/bnbcontracts/tree/c07ff86e5e8060de0cd10d6842405964c2cc5d13). Our auditing service for STEPN includes the following two stages: - Pre-Audit Consulting Service - Audit Service 1. **Pre-Audit Consulting Service** As a part of the pre-audit service, the Verilog Solutions team worked closely with the STEPN development team to discuss potential vulnerability and smart contract development best practices in a timely fashion. Verilog Solutions team is very appreciative of establishing an efficient and effective communication channel with the STEPN team, as new findings are often exchanged promptly and fixes were deployed quickly, during the preliminary report stage. 3. **Audit Service** The Verilog Solutions team conducted a thorough study of the STEPN code. The list of findings, along with the severity and solution, is available under the section [**Findings & Improvement Suggestions**](#Findings-amp-Improvement-Suggestions). --- ## GMT & GST Token **Below is the summary of GMT & GST token info:** | Network | Token | Token Address | | ------- | --------- | ------------- | |Solana | GMT | [7i5KKsX2weiTkry7jA4ZwSuXGhs5eJBEjY8vVxR4pfRx](https://solscan.io/token/7i5KKsX2weiTkry7jA4ZwSuXGhs5eJBEjY8vVxR4pfRx) | |Solana | GST | [AFbX8oGjGpmVFywbVouvhQSRmiW2aR1mohfahi4Y2AdB](https://solscan.io/token/AFbX8oGjGpmVFywbVouvhQSRmiW2aR1mohfahi4Y2AdB) | |BNB | GMT | [0x3019BF2a2eF8040C242C9a4c5c4BD4C81678b2A1](https://bscscan.com/address/0x3019BF2a2eF8040C242C9a4c5c4BD4C81678b2A1)| |BNB | GST | n/a (coming soon)| ### Deployment on Solana STEPN dev team used Solana Token Program to create both **GMT** & **GST** tokens. Token Program defines a common implementation for Fungible and Non Fungible tokens. The document of the Token Program: https://spl.solana.com/token The Github of Token Program: https://github.com/solana-labs/solana-program-library **As can be seen from the Solscan, both GMT & GST tokens used Solana Token Program:** ![GMT-Token-scan](https://hackmd.io/_uploads/HyASfSFm9.png) ![GST-Token-scan](https://hackmd.io/_uploads/S1ZoT7KX9.png) Besides, the STEPN project dev team already turned off the emission right for GMT Token as can be seen in the screenshot, GMT token has a fixed supply. ![GMT-Fixed-Supply](https://hackmd.io/_uploads/B10HQrtmq.png) <!-- 2. **The initial GMT token tx:** [4UGbtyDT3HYzTrN4XJpc42ELYL1ViPgwTGSB7qEHKA3GqfCvLXtUT8jWReFbRGjH1DqYkgpsnDhb9B389YhpGp76](https://solscan.io/tx/4UGbtyDT3HYzTrN4XJpc42ELYL1ViPgwTGSB7qEHKA3GqfCvLXtUT8jWReFbRGjH1DqYkgpsnDhb9B389YhpGp76) ![GMT-Init](https://hackmd.io/_uploads/r1xgQNYQ9.png) 3. **The initial GST token tx:** [pbaokYRV2SBXKKJNdrTkWkkyFHb17tV2HVk3Keb7mpF4kbw6NUnik35QEZ6VpDK46HuQUZfJRPJh7r7GQq1a7bM](https://solscan.io/tx/pbaokYRV2SBXKKJNdrTkWkkyFHb17tV2HVk3Keb7mpF4kbw6NUnik35QEZ6VpDK46HuQUZfJRPJh7r7GQq1a7bM) ![GST-Init](https://hackmd.io/_uploads/HJaFX4F75.png) --> ### Deployment on BNB Chain Currently, only the GMT token has been deployed on BNB Chain, and the token has been implemented by the Binance Bridge team. The contract deployment address is [0x3019BF2a2eF8040C242C9a4c5c4BD4C81678b2A1](https://bscscan.com/address/0x3019BF2a2eF8040C242C9a4c5c4BD4C81678b2A1). **Below is the summary of GMT token on BNB Chain:** | Title | Info | | ------- | ------------------- | | Network | BNB Chain | | Token | GMT | | Upgradable Contract? | Yes | | Proxy Address | [0x3019BF2a2eF8040C242C9a4c5c4BD4C81678b2A1](https://bscscan.com/address/0x3019BF2a2eF8040C242C9a4c5c4BD4C81678b2A1) | | Implementation Address | [0xba5fe23f8a3a24bed3236f05f2fcf35fd0bf0b5c](https://bscscan.com/address/0xba5fe23f8a3a24bed3236f05f2fcf35fd0bf0b5c) | Implementation Contract Source Code can be found in [**GMT BNB Implementation**](###GMT-BNB-Smart-Chain-Implementation-Code). In summary, the implemented smart contracts follow the ERC20 standards. ___ ## Privileged Roles 1. `GreenSatoshiToken.sol`: a. `Owner` can `mint()` any amount of GST tokens to any address without limitations. 2. `STEPNNFT.sol`: a. `Owner` can `setBaseURI()`, `mint()`. --- ## Findings & Improvement Suggestions #### <html></html> <style> .info { background-color:mediumseagreen; font-size: 12px; color: white; border-radius:4px; padding: 1px 4px; font-weight: 500; display: inline-block; margin: 2px; letter-spacing: 0.3px} </style><style> .minor { background-color: #698999; font-size: 12px; color: white; border-radius:4px; padding: 1px 4px; font-weight: 500; display: inline-block; margin: 2px; letter-spacing: 0.3px} </style><style> .medium { background-color: #FFCA0F; color: #121212; font-size: 12px; border-radius:4px; padding: 1px 4px; font-weight: 500; display: inline-block; margin: 2px; letter-spacing: 0.3px} </style><style> .major{ background-color: #FF6B4A; color: white; font-size: 12px; border-radius:4px; padding: 1px 4px; font-weight: 500; display: inline-block; margin: 2px; letter-spacing: 0.3px} </style><style> .critical{ background-color: #FF0000; color: white; font-size: 12px; border-radius:4px; padding: 1px 4px; font-weight: 500; display: inline-block; margin: 2px; letter-spacing: 0.3px} </style> <span class='info'>Informational</span><span class='minor'>Minor</span><span class='medium'>Medium</span><span class='major'>Major</span><span class='critical'>Critical</span> | | Total | Acknowledged | Resolved | | ------------- | ----- | ------------ | -------- | | Critical | 0 | 0 | 0 | | Major | 0 | 0 | 0 | | Medium | 2 | 2 | 0 | | Minor | 0 | 0 | 0 | | Informational | 0 | 0 | 0 | ### Critical none ;) ### Major none ;) ### Medium 1. Centralization Risks on `GreenSatoshiToken.sol`. <span class='medium'>Medium</span> ```solidity= function mint(address to, uint256 amount) public onlyOwner { _mint(to, amount); } ``` **Description**: `Owner` of this smart contract can mint tokens to certain addresses, Private key leaks may result in the unlimited token supply issue. **Recommendation**: uses a `multisig` wallet to prevent a single point of failure. **Feedback from Project Team**: `As disclosed in the STEPN whitepaper, GST has an unlimited supply therefore we have to enable the function to mint an unlimited amount of GST.` 2. Centralization Risks on `STEPNNFT.sol`. <span class='medium'>Medium</span> ```soldidity= function setBaseURI(string memory buri) public onlyOwner { require(bytes(buri).length > 0, "wrong base uri"); _buri = buri; } function mint(address to, uint256 tokenId) public onlyOwner { _safeMint(to, tokenId); } ``` **Description**: `Owner` of this smart contract can mint tokens to certain addresses, Private key leaks may result in the unlimited NFT supply issue. `Owner` of this smart contract can change the base URL of the NFT. **Recommendation**: uses `multisig` wallet to prevent a single point of failure. **Feedback from Project Team**: `STEPN’s NFT sneaker also has an unlimited supply, therefore we have to beagle the function to allow an unlimited amount of NFT sneakers to be minted by our users.` ### Minor none ;) ### Informational none ;) ## Reference Code In this section, we listed the deployed contract on-chain for your reference. ### GMT BNB Chain Implementation Code ```solidity= contract BEP20TokenImplementation is Context, IBEP20, Initializable { using SafeMath for uint256; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; uint8 private _decimals; address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); bool private _mintable; constructor() public { } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(_owner == _msgSender(), "Ownable: caller is not the owner"); _; } /** * @dev sets initials supply and the owner */ function initialize(string memory name, string memory symbol, uint8 decimals, uint256 amount, bool mintable, address owner) public initializer { _owner = owner; _name = name; _symbol = symbol; _decimals = decimals; _mintable = mintable; _mint(owner, amount); } /** * @dev Leaves the contract without the owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } /** * @dev Returns if the token is mintable or not */ function mintable() external view returns (bool) { return _mintable; } /** * @dev Returns the bep token owner. */ function getOwner() external override view returns (address) { return _owner; } /** * @dev Returns the token decimals. */ function decimals() external override view returns (uint8) { return _decimals; } /** * @dev Returns the token symbol. */ function symbol() external override view returns (string memory) { return _symbol; } /** * @dev Returns the token name. */ function name() external override view returns (string memory) { return _name; } /** * @dev See {BEP20-totalSupply}. */ function totalSupply() external override view returns (uint256) { return _totalSupply; } /** * @dev See {BEP20-balanceOf}. */ function balanceOf(address account) external override view returns (uint256) { return _balances[account]; } /** * @dev See {BEP20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) external override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {BEP20-allowance}. */ function allowance(address owner, address spender) external override view returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {BEP20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) external override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev See {BEP20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {BEP20}; * * Requirements: * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for `sender`'s tokens of at least * `amount`. */ function transferFrom(address sender, address recipient, uint256 amount) external override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "BEP20: transfer amount exceeds allowance")); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {BEP20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {BEP20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "BEP20: decreased allowance below zero")); return true; } /** * @dev Creates `amount` tokens and assigns them to `msg.sender`, increasing * the total supply. * * Requirements * * - `msg.sender` must be the token owner * - `_mintable` must be true */ function mint(uint256 amount) public onlyOwner returns (bool) { require(_mintable, "this token is not mintable"); _mint(_msgSender(), amount); return true; } /** * @dev Burn `amount` tokens and decreasing the total supply. */ function burn(uint256 amount) public returns (bool) { _burn(_msgSender(), amount); return true; } /** * @dev Moves tokens `amount` from `sender` to `recipient`. * * This is internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ function _transfer(address sender, address recipient, uint256 amount) internal { require(sender != address(0), "BEP20: transfer from the zero address"); require(recipient != address(0), "BEP20: transfer to the zero address"); _balances[sender] = _balances[sender].sub(amount, "BEP20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements * * - `to` cannot be the zero address. */ function _mint(address account, uint256 amount) internal { require(account != address(0), "BEP20: mint to the zero address"); _totalSupply = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); emit Transfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal { require(account != address(0), "BEP20: burn from the zero address"); _balances[account] = _balances[account].sub(amount, "BEP20: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); emit Transfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens. * * This is internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve(address owner, address spender, uint256 amount) internal { require(owner != address(0), "BEP20: approve from the zero address"); require(spender != address(0), "BEP20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Destroys `amount` tokens from `account`.`amount` is then deducted * from the caller's allowance. * * See {_burn} and {_approve}. */ function _burnFrom(address account, uint256 amount) internal { _burn(account, amount); _approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "BEP20: burn amount exceeds allowance")); } } ``` ### GST Token Contract STEPN team implemented a solidity version of GST token, which uses `Openzeppelin`'s ERC20 standard libraries: ```Solidity= // contracts/GreenSatoshiToken.sol // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; /** * Green Satoshi Token * @author STEPN */ contract GreenSatoshiToken is ERC20, ERC20Burnable, Ownable { constructor() ERC20("GreenSatoshiToken", "GST") {} function mint(address to, uint256 amount) public onlyOwner { _mint(to, amount); } function decimals() public view virtual override returns (uint8) { return 8; } } ``` ### STEPNNFT nft contract ```solidity= // contracts/STEPNNFT.sol // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; /** * STEPN NFTs * @author STEPN */ contract STEPNNFT is ERC721Enumerable, Ownable { // base uri for nfts string private _buri; constructor() ERC721("STEPNNFT", "SNFT") {} function _baseURI() internal view override returns (string memory) { return _buri; } function setBaseURI(string memory buri) public onlyOwner { require(bytes(buri).length > 0, "wrong base uri"); _buri = buri; } function mint(address to, uint256 tokenId) public onlyOwner { _safeMint(to, tokenId); } function burn(uint256 tokenId) public virtual { require( _isApprovedOrOwner(_msgSender(), tokenId), "burn caller is not owner nor approved" ); _burn(tokenId); } } ``` --- ## Disclaimer Verilog Solutions receives compensation from one or more clients for performing the smart contract and auditing analysis contained in these reports. The report created is solely for Clients and published with their consent. As such, the scope of our audit is limited to a review of code, and only the code we note as being within the scope of our audit detailed in this report. It is important to note that the Solidity code itself presents unique and unquantifiable risks since the Solidity language itself remains under current development and is subject to unknown risks and flaws. Our sole goal is to help reduce the attack vectors and the high level of variance associated with utilizing new and consistently changing technologies. Thus, Verilog Solutions in no way claims any guarantee of security or functionality of the technology we agree to analyze. In addition, Verilog Solutions reports do not provide any indication of the technologies proprietors, business, business model, or legal compliance. As such, reports do not provide investment advice and should not be used to make decisions about investment or involvement with any particular project. Verilog Solutions has the right to distribute the Report through other means, including via Verilog Solutions publications and other distributions. Verilog Solutions makes the reports available to parties other than the Clients (i.e., “third parties”) – on its website in hopes that it can help the blockchain ecosystem develop technical best practices in this rapidly evolving area of innovation.

    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