pages/posts/spl-token-cli/index.html

463 lines
30 KiB
HTML
Raw Normal View History

2024-03-15 23:43:34 -04:00
<!DOCTYPE html>
<html lang="en" dir="auto">
<head><meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="robots" content="index, follow">
<title>How to create a Solana Token (SPL) from CLI with metadata | Mafyuh&#39;s Blog</title>
<meta name="keywords" content="Homelab">
<meta name="description" content="I wanted to create an SPL token and after looking online I couldn&rsquo;t find an updated guide. So I thought I would learn and share. There are much easier ways to create these tokens but they cost $ and spending more $ than needed is no fun. This guide costs as little SOL as possible as everything is transacted directly on-chain. Everything is done from the CLI.
This guide just covers the basics, the tools used are way more powerful than what I use them for, this is just creating a basic token with no taxes or locked supply or anything complex, but these tools do support those options.">
<meta name="author" content="Matt">
<link rel="canonical" href="https://mafyuh.com/posts/spl-token-cli/">
<link crossorigin="anonymous" href="/assets/css/stylesheet.b609c58d5c11bb90b1a54e04005d74ad1ddf22165eb79f5533967e57df9c3b50.css" integrity="sha256-tgnFjVwRu5CxpU4EAF10rR3fIhZet59VM5Z&#43;V9&#43;cO1A=" rel="preload stylesheet" as="style">
<link rel="icon" href="https://mafyuh.com/assets/favicon/favicon.ico">
<link rel="icon" type="image/png" sizes="16x16" href="https://mafyuh.com/assets/favicon/favicon-16x16.png">
<link rel="icon" type="image/png" sizes="32x32" href="https://mafyuh.com/assets/favicon/favicon-32x32.png">
<link rel="apple-touch-icon" href="https://mafyuh.com/assets/favicon/apple-touch-icon.png">
<link rel="mask-icon" href="https://mafyuh.com/safari-pinned-tab.svg">
<meta name="theme-color" content="#2e2e33">
<meta name="msapplication-TileColor" content="#2e2e33">
<link rel="alternate" hreflang="en" href="https://mafyuh.com/posts/spl-token-cli/">
<noscript>
<style>
#theme-toggle,
.top-link {
display: none;
}
</style>
<style>
@media (prefers-color-scheme: dark) {
:root {
--theme: rgb(29, 30, 32);
--entry: rgb(46, 46, 51);
--primary: rgb(218, 218, 219);
--secondary: rgb(155, 156, 157);
--tertiary: rgb(65, 66, 68);
--content: rgb(196, 196, 197);
--code-block-bg: rgb(46, 46, 51);
--code-bg: rgb(55, 56, 62);
--border: rgb(51, 51, 51);
}
.list {
background: var(--theme);
}
.list:not(.dark)::-webkit-scrollbar-track {
background: 0 0;
}
.list:not(.dark)::-webkit-scrollbar-thumb {
border-color: var(--theme);
}
}
</style>
</noscript><script defer data-domain="mafyuh.com" src="https://stats.mafyuh.com/js/script.js"></script>
<meta property="og:title" content="How to create a Solana Token (SPL) from CLI with metadata" />
<meta property="og:description" content="I wanted to create an SPL token and after looking online I couldn&rsquo;t find an updated guide. So I thought I would learn and share. There are much easier ways to create these tokens but they cost $ and spending more $ than needed is no fun. This guide costs as little SOL as possible as everything is transacted directly on-chain. Everything is done from the CLI.
This guide just covers the basics, the tools used are way more powerful than what I use them for, this is just creating a basic token with no taxes or locked supply or anything complex, but these tools do support those options." />
<meta property="og:type" content="article" />
<meta property="og:url" content="https://mafyuh.com/posts/spl-token-cli/" /><meta property="article:section" content="posts" />
<meta property="article:published_time" content="2024-03-15T00:13:40+00:00" />
<meta property="article:modified_time" content="2024-03-15T00:13:40+00:00" /><meta property="og:site_name" content="Mafyuh&#39;s Blog" />
<meta name="twitter:card" content="summary"/>
<meta name="twitter:title" content="How to create a Solana Token (SPL) from CLI with metadata"/>
<meta name="twitter:description" content="I wanted to create an SPL token and after looking online I couldn&rsquo;t find an updated guide. So I thought I would learn and share. There are much easier ways to create these tokens but they cost $ and spending more $ than needed is no fun. This guide costs as little SOL as possible as everything is transacted directly on-chain. Everything is done from the CLI.
This guide just covers the basics, the tools used are way more powerful than what I use them for, this is just creating a basic token with no taxes or locked supply or anything complex, but these tools do support those options."/>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1 ,
"name": "Posts",
"item": "https://mafyuh.com/posts/"
},
{
"@type": "ListItem",
"position": 2 ,
"name": "How to create a Solana Token (SPL) from CLI with metadata",
"item": "https://mafyuh.com/posts/spl-token-cli/"
}
]
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": "How to create a Solana Token (SPL) from CLI with metadata",
"name": "How to create a Solana Token (SPL) from CLI with metadata",
"description": "I wanted to create an SPL token and after looking online I couldn\u0026rsquo;t find an updated guide. So I thought I would learn and share. There are much easier ways to create these tokens but they cost $ and spending more $ than needed is no fun. This guide costs as little SOL as possible as everything is transacted directly on-chain. Everything is done from the CLI.\nThis guide just covers the basics, the tools used are way more powerful than what I use them for, this is just creating a basic token with no taxes or locked supply or anything complex, but these tools do support those options.",
"keywords": [
"Homelab"
],
"articleBody": "I wanted to create an SPL token and after looking online I couldnt find an updated guide. So I thought I would learn and share. There are much easier ways to create these tokens but they cost $ and spending more $ than needed is no fun. This guide costs as little SOL as possible as everything is transacted directly on-chain. Everything is done from the CLI.\nThis guide just covers the basics, the tools used are way more powerful than what I use them for, this is just creating a basic token with no taxes or locked supply or anything complex, but these tools do support those options. If you are interested in doing more I would read the proper documentation.\nhttps://docs.solanalabs.com/cli/install https://metaboss.rs/overview.html https://spl.solana.com/token NetworkChuck has a video from late 2021 on doing this, but some commands are a bit outdated, and Solana updated their entire metadata process in 2022.\nI am using an Ubuntu 22.04 VM with 60GB storage to run these commands.\nStarting balance: 0.079975 SOL Ending balance: 0.05731652 SOL Total SOL cost: 0.02265848 SOL ($4.22 on 3/15/2024) Installing Solana Tools First we need to download Solana tools to our system:\nsh -c \"$(curl -sSfL https://release.solana.com/stable/install)\" then run the export path command that is given to you:\nexport PATH=\"/home/mafyuh/.local/share/solana/install/active_release/bin:$PATH\" Restart your terminal session.\nCreating Wallet We will create a new SOL wallet to fund our token. To do this run:\nsolana-keygen new You dont have to put a passphrase if you dont want to. I would backup your recovery seed phrase and take note of the public address. I would fund this wallet with some SOL as well at this time.\nKeep note of the keypair directory for later step.\nCheck your SOL balance with:\nsolana balance Install Rust We need Rust in order to create the token, to install Rust run:\ncurl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh Press enter for default installation. Once completed, restart your session again.\nThen we need to install some needed packages:\nsudo apt install libudev-dev llvm libclang-dev libssl-dev pkg-config build-essential protobuf-compiler -y Install spl-token-cli Now using Rust we are gonna install Solanas CLI tools, this will take a few minutes.\ncargo install spl-token-cli Create Token Creating a new token is simple, make sure your wallet is funded with SOL and just run:\nspl-token create-token Your tokens address will be printed on screen. You will use this address in pretty much all the rest of the steps so keep handy. You do need to have your wallet funded first.\nNow we need to create a token account for this token:\nspl-token create-account Example:\nspl-token create-account 7njsg9BA1xvXX9DNpe5fERHK4zb7MbCHKZ6zsx5k3adr If you get errors like:\n“unable to confirm transaction. This can happen in situations such as transaction expiration and insufficient fee-payer funds”\nYou just need to retry a few times, it will eventually go thru but sometimes takes 3-4 runs.\nMinting Tokens Now that you have a token and an account for the token, you can actually mint some tokens. To do this run:\nspl-token mint \u003c# of tokens\u003e Example:\nspl-token mint 7njsg9BA1xvXX9DNpe5fERHK4zb7MbCHKZ6zsx5k3adr 1000000 CkaGbdriXVMHtzFBPtnpDjQvZ9gM9bwd8XdTTKR2Wx32 To see your tokens you can run:\nspl-token accounts Now you will want to send these tokens to a new address, so make a new wallet and get its pubkey, then to send these tokens run:\nspl-token transfer --fund-recipient --allow-unfunded-recipient \u003c# of tokens\u003e Example:\nspl-token transfer --fund-recipient --allow-unfunded-recipient 7njsg9BA1xvXX9DNpe5fERHK4zb7MbCHKZ6zsx5k3adr 1000000 2DDyEt5N4y77ETWhhUmkZiympQbpjkfrt8FcMKhB1iWU Installing Metaboss Once this completes you can install metaboss which is needed to upload metadata. Again, this takes some time:\ncargo install metaboss Arweave/Github While we wait on metaboss to install, we should start uploading our tokens Logo to a cloud provider, I use Arweave in this exampl
"wordCount" : "910",
"inLanguage": "en",
"datePublished": "2024-03-15T00:13:40Z",
"dateModified": "2024-03-15T00:13:40Z",
"author":{
"@type": "Person",
"name": "Matt"
},
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "https://mafyuh.com/posts/spl-token-cli/"
},
"publisher": {
"@type": "Organization",
"name": "Mafyuh's Blog",
"logo": {
"@type": "ImageObject",
"url": "https://mafyuh.com/assets/favicon/favicon.ico"
}
}
}
</script>
</head>
<body class="" id="top">
<script>
if (localStorage.getItem("pref-theme") === "dark") {
document.body.classList.add('dark');
} else if (localStorage.getItem("pref-theme") === "light") {
document.body.classList.remove('dark')
} else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.body.classList.add('dark');
}
</script>
<header class="header">
<nav class="nav">
<div class="logo">
<a href="https://mafyuh.com/" accesskey="h" title="Mafyuh&#39;s Blog (Alt + H)">
<img src="https://mafyuh.com/assets/favicon/favicon.svg" alt="" aria-label="logo"
height="35">Mafyuh&#39;s Blog</a>
<div class="logo-switches">
<button id="theme-toggle" accesskey="t" title="(Alt + T)">
<svg id="moon" xmlns="http://www.w3.org/2000/svg" width="24" height="18" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round">
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
</svg>
<svg id="sun" xmlns="http://www.w3.org/2000/svg" width="24" height="18" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
</button>
<ul class="lang-switch"><li>|</li>
</ul>
</div>
</div>
<ul id="menu">
<li>
<a href="https://mafyuh.com/tags/" title="Tags">
<span>Tags</span>
</a>
</li>
<li>
<a href="https://mafyuh.com/archives/" title="Archives">
<span>Archives</span>
</a>
</li>
<li>
<a href="https://mafyuh.com/search/" title="Search (Alt &#43; /)" accesskey=/>
<span>Search</span>
</a>
</li>
</ul>
</nav>
</header>
<main class="main">
<article class="post-single">
<header class="post-header">
<div class="breadcrumbs"><a href="https://mafyuh.com/">Home</a>&nbsp;»&nbsp;<a href="https://mafyuh.com/posts/">Posts</a></div>
<h1 class="post-title entry-hint-parent">
How to create a Solana Token (SPL) from CLI with metadata
</h1>
<div class="post-meta"><span title='2024-03-15 00:13:40 +0000 UTC'>March 15, 2024</span>&nbsp;·&nbsp;5 min&nbsp;·&nbsp;910 words&nbsp;·&nbsp;Matt
</div>
</header> <div class="toc">
<details >
<summary accesskey="c" title="(Alt + C)">
<span class="details">Table of Contents</span>
</summary>
<div class="inner"><nav id="TableOfContents">
<ul>
<li><a href="#installing-solana-tools">Installing Solana Tools</a></li>
<li><a href="#creating-wallet">Creating Wallet</a></li>
<li><a href="#install-rust">Install Rust</a></li>
<li><a href="#install-spl-token-cli">Install spl-token-cli</a></li>
<li><a href="#create-token">Create Token</a></li>
<li><a href="#minting-tokens">Minting Tokens</a></li>
<li><a href="#installing-metaboss">Installing Metaboss</a></li>
<li><a href="#arweavegithub">Arweave/Github</a></li>
<li><a href="#creating-metadata">Creating Metadata</a></li>
<li><a href="#updating-metadata">Updating Metadata</a></li>
</ul>
</nav>
</div>
</details>
</div>
<div class="post-content"><p>I wanted to create an SPL token and after looking online I couldn&rsquo;t find an updated guide. So I thought I would learn and share. There are much easier ways to create these tokens but they cost $ and spending more $ than needed is no fun. This guide costs as little SOL as possible as everything is transacted directly on-chain. Everything is done from the CLI.</p>
<p>This guide just covers the basics, the tools used are way more powerful than what I use them for, this is just creating a basic token with no taxes or locked supply or anything complex, but these tools do support those options. If you are interested in doing more I would read the proper documentation.</p>
<ul>
<li><a href="https://docs.solanalabs.com/cli/install">https://docs.solanalabs.com/cli/install</a></li>
<li><a href="https://metaboss.rs/overview.html">https://metaboss.rs/overview.html</a></li>
<li><a href="https://spl.solana.com/token">https://spl.solana.com/token</a></li>
</ul>
<p><a href="https://www.youtube.com/watch?v=befUVytFC80">NetworkChuck has a video</a> from late 2021 on doing this, but some commands are a bit outdated, and Solana updated their entire metadata process in 2022.</p>
<p>I am using an Ubuntu 22.04 VM with 60GB storage to run these commands.</p>
<ul>
<li>Starting balance: 0.079975 SOL</li>
<li>Ending balance: 0.05731652 SOL</li>
<li>Total SOL cost: 0.02265848 SOL ($4.22 on 3/15/2024)</li>
</ul>
<h2 id="installing-solana-tools">Installing Solana Tools<a hidden class="anchor" aria-hidden="true" href="#installing-solana-tools">#</a></h2>
<p>First we need to download Solana tools to our system:</p>
<pre tabindex="0"><code>sh -c &#34;$(curl -sSfL https://release.solana.com/stable/install)&#34;
</code></pre><p>then run the export path command that is given to you:</p>
<pre tabindex="0"><code>export PATH=&#34;/home/mafyuh/.local/share/solana/install/active_release/bin:$PATH&#34;
</code></pre><p>Restart your terminal session.</p>
<h2 id="creating-wallet">Creating Wallet<a hidden class="anchor" aria-hidden="true" href="#creating-wallet">#</a></h2>
<p>We will create a new SOL wallet to fund our token. To do this run:</p>
<pre tabindex="0"><code>solana-keygen new
</code></pre><p>You don&rsquo;t have to put a passphrase if you don&rsquo;t want to. I would backup your recovery seed phrase and take note of the public address. I would fund this wallet with some SOL as well at this time.</p>
<p>Keep note of the keypair directory for later step.</p>
<p>Check your SOL balance with:</p>
<pre tabindex="0"><code>solana balance
</code></pre><h2 id="install-rust">Install Rust<a hidden class="anchor" aria-hidden="true" href="#install-rust">#</a></h2>
<p>We need Rust in order to create the token, to install Rust run:</p>
<pre tabindex="0"><code>curl --proto &#39;=https&#39; --tlsv1.2 -sSf https://sh.rustup.rs | sh
</code></pre><p>Press enter for default installation. Once completed, restart your session again.</p>
<p>Then we need to install some needed packages:</p>
<pre tabindex="0"><code>sudo apt install libudev-dev llvm libclang-dev libssl-dev pkg-config build-essential protobuf-compiler -y
</code></pre><h2 id="install-spl-token-cli">Install spl-token-cli<a hidden class="anchor" aria-hidden="true" href="#install-spl-token-cli">#</a></h2>
<p>Now using Rust we are gonna install Solana&rsquo;s CLI tools, this will take a few minutes.</p>
<pre tabindex="0"><code>cargo install spl-token-cli
</code></pre><h2 id="create-token">Create Token<a hidden class="anchor" aria-hidden="true" href="#create-token">#</a></h2>
<p>Creating a new token is simple, make sure your wallet is funded with SOL and just run:</p>
<pre tabindex="0"><code>spl-token create-token
</code></pre><p>Your token&rsquo;s address will be printed on screen. You will use this address in pretty much all the rest of the steps so keep handy. You do need to have your wallet funded first.</p>
<p>Now we need to create a token account for this token:</p>
<pre tabindex="0"><code>spl-token create-account &lt;TOKEN_ADDRESS&gt;
</code></pre><p>Example:</p>
<pre tabindex="0"><code>spl-token create-account 7njsg9BA1xvXX9DNpe5fERHK4zb7MbCHKZ6zsx5k3adr
</code></pre><p>If you get errors like:</p>
<p>&ldquo;unable to confirm transaction. This can happen in situations such as transaction expiration and insufficient fee-payer funds&rdquo;</p>
<p>You just need to retry a few times, it will eventually go thru but sometimes takes 3-4 runs.</p>
<h2 id="minting-tokens">Minting Tokens<a hidden class="anchor" aria-hidden="true" href="#minting-tokens">#</a></h2>
<p>Now that you have a token and an account for the token, you can actually mint some tokens. To do this run:</p>
<pre tabindex="0"><code>spl-token mint &lt;TOKEN_ADDRESS&gt; &lt;# of tokens&gt; &lt;ACCOUNT_ADDRESS&gt;
</code></pre><p>Example:</p>
<pre tabindex="0"><code>spl-token mint 7njsg9BA1xvXX9DNpe5fERHK4zb7MbCHKZ6zsx5k3adr 1000000 CkaGbdriXVMHtzFBPtnpDjQvZ9gM9bwd8XdTTKR2Wx32
</code></pre><p>To see your tokens you can run:</p>
<pre tabindex="0"><code>spl-token accounts
</code></pre><p>Now you will want to send these tokens to a new address, so make a new wallet and get its pubkey, then to send these tokens run:</p>
<pre tabindex="0"><code>spl-token transfer --fund-recipient --allow-unfunded-recipient &lt;TOKEN_ADDRESS&gt; &lt;# of tokens&gt; &lt;NEW_ADDRESS&gt;
</code></pre><p>Example:</p>
<pre tabindex="0"><code>spl-token transfer --fund-recipient --allow-unfunded-recipient 7njsg9BA1xvXX9DNpe5fERHK4zb7MbCHKZ6zsx5k3adr 1000000 2DDyEt5N4y77ETWhhUmkZiympQbpjkfrt8FcMKhB1iWU
</code></pre><h2 id="installing-metaboss">Installing Metaboss<a hidden class="anchor" aria-hidden="true" href="#installing-metaboss">#</a></h2>
<p>Once this completes you can install metaboss which is needed to upload metadata. Again, this takes some time:</p>
<pre tabindex="0"><code>cargo install metaboss
</code></pre><h2 id="arweavegithub">Arweave/Github<a hidden class="anchor" aria-hidden="true" href="#arweavegithub">#</a></h2>
<p>While we wait on metaboss to install, we should start uploading our tokens Logo to a cloud provider, I use Arweave in this example but you can use anything really. There are also many ways to upload to arweave so this is just a friendly example thats free.</p>
<p>First create an account at <a href="https://akord.com/use-arweave">https://akord.com/use-arweave</a>
Upload your image to a new vault. (PNG)
Click on the information icon next to your image and copy the arweave.net URL. (Not under Share) We need this for our JSON file we will create next.</p>
<p>Now you can create a json file, and in it paste the following:</p>
<pre tabindex="0"><code>{
&#34;name&#34;: &#34;TOKEN_NAME&#34;,
&#34;symbol&#34;: &#34;SYM&#34;,
&#34;description&#34;: &#34;Description of token&#34;,
&#34;image&#34;: &#34;https://arweave.net/image-url-from-above&#34;
}
</code></pre><p>Now save this file with .json extension and upload it to Arweave just like the image. Now we need this JSON file&rsquo;s Arweave link. Copy it from akord and create a new json file in your Solana server&rsquo;s working directory. Fill in the following:</p>
<pre tabindex="0"><code>{
&#34;name&#34;: &#34;TOKEN_NAME&#34;,
&#34;symbol&#34;: &#34;SYM&#34;,
&#34;uri&#34;: &#34;https://arweave.net/json-file-arweave-url&#34;
}
</code></pre><p>Using the JSON file&rsquo;s Arweave link as the URI. Name this file whatever.json.</p>
<h2 id="creating-metadata">Creating Metadata<a hidden class="anchor" aria-hidden="true" href="#creating-metadata">#</a></h2>
<p>First we need to update our RPC URL, to set to mainnet run:</p>
<pre tabindex="0"><code>solana config set --url https://api.mainnet-beta.solana.com --keypair /home/mafyuh/.config/solana/id.json
</code></pre><p>Filling in your keypair directory from earlier.</p>
<p>Now that metaboss is installed, we just need to run 1 command to create our tokens metadata, again it may take a few tries:</p>
<pre tabindex="0"><code>metaboss create metadata -a &lt;TOKEN_ADDRESS&gt; -m whatever.json
</code></pre><p>You should be able to go to solscan and see your updated metadata! It should appear in the SOL wallets soon after.</p>
<h2 id="updating-metadata">Updating Metadata<a hidden class="anchor" aria-hidden="true" href="#updating-metadata">#</a></h2>
<p>If you ever need to update your metadata, you can do so by running:</p>
<pre tabindex="0"><code>metaboss update uri --keypair /home/mafyuh/.config/solana/id.json --account &lt;TOKEN_ADDRESS&gt; --new-uri https://arweave.net/new-arweave-json-url
</code></pre><p>Hope this guide has helped you save some $ when creating your Solana tokens! If you appreciate the post and wanna send some tokens as thanks, my SOL wallet address is: 3RYPrKxC6BNv3XUMf8Cyjg36pw6Qu1txRvqq6LNq9Psj</p>
</div>
<footer class="post-footer">
<ul class="post-tags">
<li><a href="https://mafyuh.com/tags/homelab/">Homelab</a></li>
</ul>
<nav class="paginav">
<a class="next" href="https://mafyuh.com/posts/docker-arr-stack-guide/">
<span class="title">Next »</span>
<br>
<span>Docker Compose Arr Stack Guide</span>
</a>
</nav>
</footer><script src="https://utteranc.es/client.js"
repo="Mafyuh/mafyuh.com"
issue-term="pathname"
label="✨💬✨"
theme="photon-dark"
crossorigin="anonymous"
async>
</script>
</article>
</main>
<footer class="footer">
<span>&copy; 2024 <a href="https://mafyuh.com/">Mafyuh&#39;s Blog</a></span>
<span>
Powered by
<a href="https://gohugo.io/" rel="noopener noreferrer" target="_blank">Hugo</a> &
<a href="https://github.com/adityatelange/hugo-PaperMod/" rel="noopener" target="_blank">PaperMod</a>
</span>
</footer>
<a href="#top" aria-label="go to top" title="Go to Top (Alt + G)" class="top-link" id="top-link" accesskey="g">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 6" fill="currentColor">
<path d="M12 6H0l6-6z" />
</svg>
</a>
<script>
let menu = document.getElementById('menu')
if (menu) {
menu.scrollLeft = localStorage.getItem("menu-scroll-position");
menu.onscroll = function () {
localStorage.setItem("menu-scroll-position", menu.scrollLeft);
}
}
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener("click", function (e) {
e.preventDefault();
var id = this.getAttribute("href").substr(1);
if (!window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
document.querySelector(`[id='${decodeURIComponent(id)}']`).scrollIntoView({
behavior: "smooth"
});
} else {
document.querySelector(`[id='${decodeURIComponent(id)}']`).scrollIntoView();
}
if (id === "top") {
history.replaceState(null, null, " ");
} else {
history.pushState(null, null, `#${id}`);
}
});
});
</script>
<script>
var mybutton = document.getElementById("top-link");
window.onscroll = function () {
if (document.body.scrollTop > 800 || document.documentElement.scrollTop > 800) {
mybutton.style.visibility = "visible";
mybutton.style.opacity = "1";
} else {
mybutton.style.visibility = "hidden";
mybutton.style.opacity = "0";
}
};
</script>
<script>
document.getElementById("theme-toggle").addEventListener("click", () => {
if (document.body.className.includes("dark")) {
document.body.classList.remove('dark');
localStorage.setItem("pref-theme", 'light');
} else {
document.body.classList.add('dark');
localStorage.setItem("pref-theme", 'dark');
}
})
</script>
<script>
document.querySelectorAll('pre > code').forEach((codeblock) => {
const container = codeblock.parentNode.parentNode;
const copybutton = document.createElement('button');
copybutton.classList.add('copy-code');
copybutton.innerHTML = 'copy';
function copyingDone() {
copybutton.innerHTML = 'copied!';
setTimeout(() => {
copybutton.innerHTML = 'copy';
}, 2000);
}
copybutton.addEventListener('click', (cb) => {
if ('clipboard' in navigator) {
navigator.clipboard.writeText(codeblock.textContent);
copyingDone();
return;
}
const range = document.createRange();
range.selectNodeContents(codeblock);
const selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
try {
document.execCommand('copy');
copyingDone();
} catch (e) { };
selection.removeRange(range);
});
if (container.classList.contains("highlight")) {
container.appendChild(copybutton);
} else if (container.parentNode.firstChild == container) {
} else if (codeblock.parentNode.parentNode.parentNode.parentNode.parentNode.nodeName == "TABLE") {
codeblock.parentNode.parentNode.parentNode.parentNode.parentNode.appendChild(copybutton);
} else {
codeblock.parentNode.appendChild(copybutton);
}
});
</script>
</body>
</html>