<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Kaia KIPs</title>
    <description>A feed of all KIPs</description>
    <link>https://kips.kaia.io</link>
    <atom:link href="https://kips.kaia.io/all.xml" rel="self" type="application/rss+xml" />
    <lastBuildDate>Fri, 06 Mar 2026 00:35:00 +0000</lastBuildDate>
    
    
      <item>
        <title></title>
        <category></category>
        
        <description>&lt;style type=&quot;text/css&quot; media=&quot;screen&quot;&gt;
  .container {
    margin: 10px auto;
    max-width: 600px;
    text-align: center;
  }
  h1 {
    margin: 30px 0;
    font-size: 4em;
    line-height: 1;
    letter-spacing: -1px;
  }
&lt;/style&gt;

&lt;div class=&quot;container&quot;&gt;
  &lt;h1&gt;404&lt;/h1&gt;

  &lt;p&gt;&lt;strong&gt;Page not found :(&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;The requested page could not be found.&lt;/p&gt;
&lt;/div&gt;
</description>
        <pubDate></pubDate>
        <link>https://kips.kaia.io/404</link>
        <guid isPermaLink="true">https://kips.kaia.io/404</guid>
      </item>
    
      <item>
        <title>All</title>
        <category></category>
        
        <description>&lt;style type=&quot;text/css&quot;&gt;
  .kiptable .title {
    width: 67%;
  }

  .kiptable .author {
    width: 33%;
  }
&lt;/style&gt;

  
  
  

  
  
  
    &lt;h2 id=&quot;draft&quot;&gt;Draft&lt;/h2&gt;
    &lt;table class=&quot;kiptable&quot;&gt;
      &lt;thead&gt;
        &lt;tr&gt;&lt;th class=&quot;kipnum&quot;&gt;Number&lt;/th&gt;&lt;th class=&quot;title&quot;&gt;Title&lt;/th&gt;&lt;th class=&quot;author&quot;&gt;Author&lt;/th&gt;&lt;/tr&gt;
      &lt;/thead&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-223&quot;&gt;223&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;Reduce calldata gas cost and introduce floor data gas&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;https://github.com/hyunsooda&quot;&gt;Lake&lt;/a&gt;, &lt;a href=&quot;https://github.com/blukat29&quot;&gt;Ollie&lt;/a&gt;, &lt;a href=&quot;https://github.com/2dvorak&quot;&gt;Sawyer&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-226&quot;&gt;226&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;Consensus liquidity for Kaia&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;https://github.com/hyeonLewis&quot;&gt;Lewis&lt;/a&gt;, &lt;a href=&quot;https://github.com/ian0371&quot;&gt;Ian&lt;/a&gt;, &lt;a href=&quot;https://github.com/blukat29&quot;&gt;Ollie&lt;/a&gt;, &lt;a href=&quot;https://github.com/hyunsooda&quot;&gt;Lake&lt;/a&gt;, &lt;a href=&quot;https://github.com/aidan-kwon&quot;&gt;and Aidan&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-227&quot;&gt;227&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;Candidate and Validator Evaluation&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;https://github.com/jiseongnoh&quot;&gt;Joseph&lt;/a&gt;, &lt;a href=&quot;https://github.com/hyeonLewis&quot;&gt;Lewis&lt;/a&gt;, &lt;a href=&quot;https://github.com/ian0371&quot;&gt;Ian&lt;/a&gt;, &lt;a href=&quot;https://github.com/blukat29&quot;&gt;Ollie&lt;/a&gt;, &lt;a href=&quot;https://github.com/hyunsooda&quot;&gt;Lake&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-228&quot;&gt;228&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;SetCode for EOA&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;https://github.com/blukat29&quot;&gt;Ollie&lt;/a&gt;, &lt;a href=&quot;https://github.com/Mdaiki0730&quot;&gt;Garry&lt;/a&gt;, &lt;a href=&quot;https://github.com/shiki-tak&quot;&gt;Shiki&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-239&quot;&gt;239&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;EXTCODEHASH opcode for Kaia&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;https://github.com/hyeonLewis&quot;&gt;Lewis&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-249&quot;&gt;249&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;Slot-Based Auction on Kaia&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;https://github.com/jiseongnoh&quot;&gt;Joseph&lt;/a&gt;, &lt;a href=&quot;https://github.com/hyeonLewis&quot;&gt;Lewis&lt;/a&gt;, &lt;a href=&quot;https://github.com/hyunsooda&quot;&gt;Lake&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-276&quot;&gt;276&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;eth_config JSON-RPC Method&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;https://github.com/Mdaiki0730&quot;&gt;Garry&lt;/a&gt;, &lt;a href=&quot;https://github.com/blukat29&quot;&gt;Ollie&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-277&quot;&gt;277&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;Self Validator Registration&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;https://github.com/hyeonLewis&quot;&gt;Lewis&lt;/a&gt;, &lt;a href=&quot;https://github.com/blukat29&quot;&gt;Ollie&lt;/a&gt;, &lt;a href=&quot;https://github.com/ian0371&quot;&gt;Ian&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-279&quot;&gt;279&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;BlobTx for Kaia&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;https://github.com/blukat29&quot;&gt;Ollie&lt;/a&gt;, &lt;a href=&quot;https://github.com/tnasu&quot;&gt;Nasu&lt;/a&gt;, &lt;a href=&quot;https://github.com/ulbqb&quot;&gt;Shogo&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-286&quot;&gt;286&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;Permissionless Validator Lifecycle&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;https://github.com/hyeonLewis&quot;&gt;Lewis&lt;/a&gt;, &lt;a href=&quot;https://github.com/ian0371&quot;&gt;Ian&lt;/a&gt;, &lt;a href=&quot;https://github.com/blukat29&quot;&gt;Ollie&lt;/a&gt;, &lt;a href=&quot;https://github.com/hyunsooda&quot;&gt;Lake&lt;/a&gt;, &lt;a href=&quot;https://github.com/jiseongnoh&quot;&gt;and Joseph&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-287&quot;&gt;287&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;Permissionless Staking Policy&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;https://github.com/hyeonLewis&quot;&gt;Lewis&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-290&quot;&gt;290&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;Permissionless Smart Contracts&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;https://github.com/hyeonLewis&quot;&gt;Lewis&lt;/a&gt;, &lt;a href=&quot;https://github.com/ian0371&quot;&gt;Ian&lt;/a&gt;, &lt;a href=&quot;https://github.com/blukat29&quot;&gt;Ollie&lt;/a&gt;, &lt;a href=&quot;https://github.com/hyunsooda&quot;&gt;Lake&lt;/a&gt;, &lt;a href=&quot;https://github.com/jiseongnoh&quot;&gt;and Joseph&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
    &lt;/table&gt;
  

  
  
  
    &lt;h2 id=&quot;review&quot;&gt;Review&lt;/h2&gt;
    &lt;table class=&quot;kiptable&quot;&gt;
      &lt;thead&gt;
        &lt;tr&gt;&lt;th class=&quot;kipnum&quot;&gt;Number&lt;/th&gt;&lt;th class=&quot;title&quot;&gt;Title&lt;/th&gt;&lt;th class=&quot;author&quot;&gt;Author&lt;/th&gt;&lt;/tr&gt;
      &lt;/thead&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-245&quot;&gt;245&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;Transaction Bundle&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;https://github.com/ian0371&quot;&gt;Ian&lt;/a&gt;, &lt;a href=&quot;https://github.com/blukat29&quot;&gt;Ollie&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-247&quot;&gt;247&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;Gasless Transaction&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;https://github.com/ian0371&quot;&gt;Ian&lt;/a&gt;, &lt;a href=&quot;https://github.com/shiki-tak&quot;&gt;Shiki&lt;/a&gt;, &lt;a href=&quot;https://github.com/blukat29&quot;&gt;Ollie&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
    &lt;/table&gt;
  

  
  
  

  
  
  
    &lt;h2 id=&quot;final&quot;&gt;Final&lt;/h2&gt;
    &lt;table class=&quot;kiptable&quot;&gt;
      &lt;thead&gt;
        &lt;tr&gt;&lt;th class=&quot;kipnum&quot;&gt;Number&lt;/th&gt;&lt;th class=&quot;title&quot;&gt;Title&lt;/th&gt;&lt;th class=&quot;author&quot;&gt;Author&lt;/th&gt;&lt;/tr&gt;
      &lt;/thead&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-3&quot;&gt;3&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;Klaytn Keystore Format v4&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;mailto:colin.kim@groundx.xyz&quot;&gt;Junghyun Colin Kim&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-7&quot;&gt;7&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;Fungible Token Standard&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;mailto:colin.kim@groundx.xyz&quot;&gt;Junghyun Colin Kim&lt;/a&gt;, &lt;a href=&quot;mailto:ethan.kim@groundx.xyz&quot;&gt;Kyungup Kim&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-13&quot;&gt;13&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;Interface Query Standard&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;mailto:colin.kim@groundx.xyz&quot;&gt;Junghyun Colin Kim&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-17&quot;&gt;17&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;Non-fungible Token Standard&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;mailto:colin.kim@groundx.xyz&quot;&gt;Junghyun Colin Kim&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-34&quot;&gt;34&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;Klaytn SDK Common Architecture&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;mailto:jasmine.kim@groundx.xyz&quot;&gt;Jimin Kim&lt;/a&gt;, &lt;a href=&quot;mailto:kale.kim@groundx.xyz&quot;&gt;Seonyong Kim&lt;/a&gt;, &lt;a href=&quot;mailto:colin.kim@groundx.xyz&quot;&gt;Junghyun Colin Kim&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-37&quot;&gt;37&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;Token Standard&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;mailto:kai.kim@groundx.xyz&quot;&gt;Kyungkoo Kai Kim&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-71&quot;&gt;71&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;Dynamic Gas Fee Pricing Mechanism&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;mailto:jared.fi@krustuniverse.com&quot;&gt;Woojin Lee (jared)&lt;/a&gt;, &lt;a href=&quot;mailto:colin.klaytn@krustuniverse.com&quot;&gt;Junghyun Colin Kim&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-81&quot;&gt;81&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;Implementing the on-chain governance voting method&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;mailto:yeriel.lee@krustuniverse.com&quot;&gt;Yeri&lt;/a&gt;, &lt;a href=&quot;mailto:daniel.cc@krustuniverse.com&quot;&gt;Daniel&lt;/a&gt;, &lt;a href=&quot;mailto:aidan.kwon@krustuniverse.com&quot;&gt;Aidan&lt;/a&gt;, &lt;a href=&quot;mailto:ollie.j@krustuniverse.com&quot;&gt;Ollie&lt;/a&gt;, &lt;a href=&quot;mailto:eddie.kim0@krustuniverse.com&quot;&gt;Eddie&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-82&quot;&gt;82&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;A new GC reward structure due to abolition of the Gini coefficient&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;mailto:yeriel.lee@krustuniverse.com&quot;&gt;Yeri&lt;/a&gt;, &lt;a href=&quot;mailto:daniel.cc@krustuniverse.com&quot;&gt;Daniel&lt;/a&gt;, &lt;a href=&quot;mailto:aidan.kwon@krustuniverse.com&quot;&gt;Aidan&lt;/a&gt;, &lt;a href=&quot;mailto:ollie.j@krustuniverse.com&quot;&gt;Ollie&lt;/a&gt;, &lt;a href=&quot;mailto:sam.seo@krustuniverse.com&quot;&gt;Sam&lt;/a&gt;, &lt;a href=&quot;mailto:uno.lee@krustuniverse.com&quot;&gt;Uno&lt;/a&gt;, &lt;a href=&quot;mailto:eddie.kim0@krustuniverse.com&quot;&gt;Eddie&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-87&quot;&gt;87&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;NFT Avatar in Multi-Metaverse&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;https://github.com/yeri-lee&quot;&gt;Yeri Lee&lt;/a&gt;, &lt;a href=&quot;https://github.com/kjhman21&quot;&gt;Junghyun Colin Kim&lt;/a&gt;, &lt;a href=&quot;https://github.com/kernys&quot;&gt;Wonbae Kim&lt;/a&gt;, &lt;a href=&quot;https://github.com/taronsung&quot;&gt;Seokrin Sung&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-97&quot;&gt;97&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;Signed Data Standard&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;mailto:95decode@gmail.com&quot;&gt;TaeRim Lee&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-103&quot;&gt;103&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;Treasury Fund Rebalancing&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;https://github.com/aidan-kwon&quot;&gt;Aidan&lt;/a&gt;, &lt;a href=&quot;https://github.com/toniya-klaytn&quot;&gt;Toniya&lt;/a&gt;, &lt;a href=&quot;https://github.com/blukat29&quot;&gt;Ollie&lt;/a&gt;, &lt;a href=&quot;https://github.com/ian0371&quot;&gt;Ian&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-113&quot;&gt;113&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;BLS public key registry&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;https://github.com/ian0371&quot;&gt;Ian&lt;/a&gt;, &lt;a href=&quot;https://github.com/blukat29&quot;&gt;Ollie&lt;/a&gt;, &lt;a href=&quot;https://github.com/jiseongnoh&quot;&gt;Joseph&lt;/a&gt;, &lt;a href=&quot;https://github.com/aidan-kwon&quot;&gt;and Aidan&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-114&quot;&gt;114&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;Supplant DIFFICULTY opcode with RANDOM&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;https://github.com/ian0371&quot;&gt;Ian&lt;/a&gt;, &lt;a href=&quot;https://github.com/blukat29&quot;&gt;Ollie&lt;/a&gt;, &lt;a href=&quot;https://github.com/jiseongnoh&quot;&gt;Joseph&lt;/a&gt;, &lt;a href=&quot;https://github.com/aidan-kwon&quot;&gt;and Aidan&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-146&quot;&gt;146&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;Unpredictable Proposer Selection&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;https://github.com/ian0371&quot;&gt;Ian&lt;/a&gt;, &lt;a href=&quot;https://github.com/blukat29&quot;&gt;Ollie&lt;/a&gt;, &lt;a href=&quot;https://github.com/jiseongnoh&quot;&gt;Joseph&lt;/a&gt;, &lt;a href=&quot;https://github.com/aidan-kwon&quot;&gt;and Aidan&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-149&quot;&gt;149&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;Unified System Contract Management Process&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;https://github.com/hyeonLewis&quot;&gt;Lewis&lt;/a&gt;, &lt;a href=&quot;https://github.com/ian0371&quot;&gt;Ian&lt;/a&gt;, &lt;a href=&quot;https://github.com/blukat29&quot;&gt;Ollie&lt;/a&gt;, &lt;a href=&quot;https://github.com/hyunsooda&quot;&gt;Lake&lt;/a&gt;, &lt;a href=&quot;https://github.com/aidan-kwon&quot;&gt;and Aidan&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-160&quot;&gt;160&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;An Update of Treasury Fund Rebalancing&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;https://github.com/blukat29&quot;&gt;Ollie&lt;/a&gt;, &lt;a href=&quot;https://github.com/yoomee1313&quot;&gt;Yumiel&lt;/a&gt;, &lt;a href=&quot;https://github.com/ian0371&quot;&gt;Ian&lt;/a&gt;, &lt;a href=&quot;https://github.com/aidan-kwon&quot;&gt;Aidan&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-162&quot;&gt;162&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;Priority Fee Mechanism&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;https://github.com/blukat29&quot;&gt;Ollie&lt;/a&gt;, &lt;a href=&quot;https://github.com/2dvorak&quot;&gt;Sawyer&lt;/a&gt;, &lt;a href=&quot;https://github.com/aidan-kwon&quot;&gt;Aidan&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-163&quot;&gt;163&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;CnStakingV3 with public delegation&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;https://github.com/hyeonLewis&quot;&gt;Lewis&lt;/a&gt;, &lt;a href=&quot;https://github.com/ian0371&quot;&gt;Ian&lt;/a&gt;, &lt;a href=&quot;https://github.com/blukat29&quot;&gt;Ollie&lt;/a&gt;, &lt;a href=&quot;https://github.com/aidan-kwon&quot;&gt;and Aidan&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
    &lt;/table&gt;
  

  
  
  
    &lt;h2 id=&quot;active&quot;&gt;Active&lt;/h2&gt;
    &lt;table class=&quot;kiptable&quot;&gt;
      &lt;thead&gt;
        &lt;tr&gt;&lt;th class=&quot;kipnum&quot;&gt;Number&lt;/th&gt;&lt;th class=&quot;title&quot;&gt;Title&lt;/th&gt;&lt;th class=&quot;author&quot;&gt;Author&lt;/th&gt;&lt;/tr&gt;
      &lt;/thead&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-1&quot;&gt;1&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;KIP Purpose and Guidelines&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;mailto:colin.kim@groundx.xyz&quot;&gt;Junghyun Kim&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-201&quot;&gt;201&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;KIP Process Upgrade&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;mailto:neo.yiu@kaia.io&quot;&gt;Neo Yiu&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
    &lt;/table&gt;
  

  
  
  
    &lt;h2 id=&quot;abandoned&quot;&gt;Abandoned&lt;/h2&gt;
    &lt;table class=&quot;kiptable&quot;&gt;
      &lt;thead&gt;
        &lt;tr&gt;&lt;th class=&quot;kipnum&quot;&gt;Number&lt;/th&gt;&lt;th class=&quot;title&quot;&gt;Title&lt;/th&gt;&lt;th class=&quot;author&quot;&gt;Author&lt;/th&gt;&lt;/tr&gt;
      &lt;/thead&gt;
      
        &lt;tr&gt;
          &lt;td class=&quot;kipnum&quot;&gt;&lt;a href=&quot;/KIPs/kip-237&quot;&gt;237&lt;/a&gt;&lt;/td&gt;
          &lt;td class=&quot;title&quot;&gt;Storage Compression&lt;/td&gt;
          &lt;td class=&quot;author&quot;&gt;&lt;a href=&quot;https://github.com/hyunsooda&quot;&gt;Lake&lt;/a&gt;, &lt;a href=&quot;https://github.com/blukat29&quot;&gt;Ollie&lt;/a&gt;&lt;/td&gt;
        &lt;/tr&gt;
      
    &lt;/table&gt;
  

  
  
  

  
  
  


</description>
        <pubDate></pubDate>
        <link>https://kips.kaia.io/all</link>
        <guid isPermaLink="true">https://kips.kaia.io/all</guid>
      </item>
    
      <item>
        <title></title>
        <category></category>
        
        <description>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;rss version=&quot;2.0&quot; xmlns:atom=&quot;http://www.w3.org/2005/Atom&quot;&gt;
  &lt;channel&gt;
    &lt;title&gt;Kaia KIPs&lt;/title&gt;
    &lt;description&gt;A feed of all KIPs&lt;/description&gt;
    &lt;link&gt;{{ site.url }}&lt;/link&gt;
    &lt;atom:link href=&quot;{{ site.url }}/all.xml&quot; rel=&quot;self&quot; type=&quot;application/rss+xml&quot; /&gt;
    &lt;lastBuildDate&gt;{{ site.time | date_to_rfc822 }}&lt;/lastBuildDate&gt;
    {% assign kips = site.pages | sort: &apos;kip&apos; %}
    {% for kip in kips %}
      &lt;item&gt;
        &lt;title&gt;{{ kip.title | xml_escape }}&lt;/title&gt;
        &lt;category&gt;{{ kip.type | xml_escape }}&lt;/category&gt;
        {% if kip.discussions-to %}
          &lt;comments&gt;{{ kip.discussions-to | xml_escape }}&lt;/comments&gt;
        {% endif %}
        &lt;description&gt;{{ kip.content | xml_escape }}&lt;/description&gt;
        &lt;pubDate&gt;{{ kip.created | date_to_rfc822 }}&lt;/pubDate&gt;
        &lt;link&gt;{{ site.url }}{{ kip.url }}&lt;/link&gt;
        &lt;guid isPermaLink=&quot;true&quot;&gt;{{ site.url }}{{ kip.url }}&lt;/guid&gt;
      &lt;/item&gt;
    {% endfor %}
  &lt;/channel&gt;
&lt;/rss&gt;
</description>
        <pubDate></pubDate>
        <link>https://kips.kaia.io/rss/all.xml</link>
        <guid isPermaLink="true">https://kips.kaia.io/rss/all.xml</guid>
      </item>
    
      <item>
        <title>Core</title>
        <category></category>
        
        <description>{% assign kips=site.pages|where:&quot;type&quot;,&quot;Core&quot;%}
{% include kiptable.html kips=kips %}
</description>
        <pubDate></pubDate>
        <link>https://kips.kaia.io/core</link>
        <guid isPermaLink="true">https://kips.kaia.io/core</guid>
      </item>
    
      <item>
        <title>Ecosystem</title>
        <category></category>
        
        <description>{% assign kips=site.pages|where:&quot;type&quot;,&quot;Ecosystem&quot; %}
{% include kiptable.html kips=kips %}
</description>
        <pubDate></pubDate>
        <link>https://kips.kaia.io/ecosystem</link>
        <guid isPermaLink="true">https://kips.kaia.io/ecosystem</guid>
      </item>
    
      <item>
        <title>Home</title>
        <category></category>
        
        <description>&lt;h1 class=&quot;page-heading&quot;&gt;KIPs
  &lt;a href=&quot;https://github.com/kaiachain/kips&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://img.shields.io/badge/KIPS-000000?logo=github&amp;logoColor=white&quot; alt=&quot;Github KIPS&quot;&gt;&lt;/a&gt;
  &lt;!-- &lt;a href=&quot;https://devforum.kaia.io/c/kips&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://img.shields.io/badge/Dev Forum-bff009?logo=discourse&amp;logoColor=black&quot; alt=&quot;Discourse channel for Kaiachain&quot;&gt;&lt;/a&gt; --&gt;
  &lt;a href=&quot;https://discord.gg/kaiachain&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://img.shields.io/badge/Kaia Discord-5865f2?logo=discord&amp;logoColor=white&quot; alt=&quot;Discord channel for Kaiachain&quot;&gt;&lt;/a&gt;
  &lt;a href=&quot;rss/all.xml&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://img.shields.io/badge/rss-Everything-red.svg&quot; alt=&quot;RSS&quot;&gt;&lt;/a&gt;
  &lt;a href=&quot;rss/last-call.xml&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://img.shields.io/badge/rss-Last Calls-red.svg&quot; alt=&quot;RSS&quot;&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;Kaia Improvement Proposals (KIPs) describe standards for the Kaia platform, including core protocol specifications, client APIs, and contract standards. &lt;/p&gt;

&lt;h2&gt;Contributing&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;Review &lt;a href=&quot;KIPs/kip-201&quot;&gt;KIP-201&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Fork the &lt;a target=&quot;_blank&quot; href=&quot;https://github.com/kaiachain/kips&quot;&gt;repository&lt;/a&gt; by clicking &quot;Fork&quot; in the top right.&lt;/li&gt;
  &lt;li&gt;Add your KIP to the forked  repository. There is a &lt;a target=&quot;_blank&quot; href=&quot;https://github.com/kaiachain/kips/blob/main/kip-template.md&quot;&gt;template KIP here&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Submit a Pull Request to Kaia&apos;s &lt;a target=&quot;_blank&quot; href=&quot;https://github.com/kaiachain/kips&quot;&gt;KIPs repository&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;b&gt;Note:&lt;/b&gt; 
We strongly recommend discussing KIP ideas with the community in the &lt;a href=&quot;https://devforum.kaia.io/c/kips&quot; target=&quot;_blank&quot;&gt;Developer Forum - TBD&lt;/a&gt; to collect feedback and validate ideas before submitting a Draft.

&lt;h2&gt;KIP status terms&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Idea&lt;/strong&gt; - An idea that is pre-draft. This is not tracked within the KIP Repository.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Draft&lt;/strong&gt; - a KIP that is undergoing rapid iteration and changes.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Review&lt;/strong&gt; - a KIP that is marked as a ready for and requested peer review by the reviewers.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Last Call&lt;/strong&gt; - a KIP that is listed prominently as a pinned issue&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Accepted&lt;/strong&gt; - a KIP that has been in ‘Last Call’ for at least 2 weeks, any technical changes that were requested have been addressed by the author, and finally get approved by the Kaia core developers.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Final&lt;/strong&gt; - a KIP that has been released as a standard specification. If a Core KIP is in ‘Final’, its implementation has been included in at least one Kaia client.&lt;/li&gt;
&lt;/ul&gt;

{% include types_lastcall.html %} </description>
        <pubDate></pubDate>
        <link>https://kips.kaia.io/</link>
        <guid isPermaLink="true">https://kips.kaia.io/</guid>
      </item>
    
      <item>
        <title></title>
        <category></category>
        
        <description>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;rss version=&quot;2.0&quot; xmlns:atom=&quot;http://www.w3.org/2005/Atom&quot;&gt;
  &lt;channel&gt;
    &lt;title&gt;Kaia KIPs Last Call Review&lt;/title&gt;
    &lt;description&gt;All KIPs which are in the two-week &quot;last call&quot; status, please help review these and provide your feedback!&lt;/description&gt;
    &lt;link&gt;{{ site.url }}&lt;/link&gt;
    &lt;atom:link href=&quot;{{ site.url }}/last-call.xml&quot; rel=&quot;self&quot; type=&quot;application/rss+xml&quot; /&gt;
    &lt;lastBuildDate&gt;{{ site.time | date_to_rfc822 }}&lt;/lastBuildDate&gt;
    {% assign kips = site.pages | sort: &apos;kip&apos; %}
    {% for kip in kips %}
      {% if kip.status == &quot;Last Call&quot; %}
      {% capture description %}
        &lt;p&gt;&lt;strong&gt;KIP #{{ kip.kip }} - {{kip.title }}&lt;/strong&gt; is in Last Call status. It is authored by {{ kip.author }} and was originally created {{ kip.created }}. It is in the type {{ kip.type }}. Please review and note any changes that should block acceptance.&lt;/p&gt;
        {% if kip.discussions-to %}
          &lt;p&gt;The author has requested that discussions happen at the following URL: {{ kip.discussions-to }}&lt;/p&gt;
        {% else %}
          &lt;p&gt;Please visit the [kaia/KIPs issues to comment](https://github.com/kaiachain/KIPs/issues/{{kip.kip}}).&lt;/p&gt;
        {% endif %}
        &lt;hr /&gt;
        {{ kip.content }}        
      {% endcapture %}
      &lt;item&gt;
        &lt;title&gt;{{ kip.title | xml_escape }}&lt;/title&gt;
        &lt;description&gt;{{ description | xml_escape }}&lt;/description&gt;
        &lt;pubDate&gt;{{ kip.date | date_to_rfc822 }}&lt;/pubDate&gt;
        &lt;link&gt;{{ site.url }}/{{ kip.url }}&lt;/link&gt;
        &lt;guid isPermaLink=&quot;true&quot;&gt;{{ site.url }}/{{ kip.url }}&lt;/guid&gt;
      &lt;/item&gt;
      {% endif %}
    {% endfor %}
  &lt;/channel&gt;
&lt;/rss&gt;
</description>
        <pubDate></pubDate>
        <link>https://kips.kaia.io/rss/last-call.xml</link>
        <guid isPermaLink="true">https://kips.kaia.io/rss/last-call.xml</guid>
      </item>
    
      <item>
        <title>Meta</title>
        <category></category>
        
        <description>{% assign kips=site.pages|where:&quot;type&quot;,&quot;Meta&quot; %}
{% include kiptable.html kips=kips %}
</description>
        <pubDate></pubDate>
        <link>https://kips.kaia.io/meta</link>
        <guid isPermaLink="true">https://kips.kaia.io/meta</guid>
      </item>
    
      <item>
        <title></title>
        <category></category>
        
        <description>@import &quot;minima&quot;;

.site-header {
    .wrapper {
      display: flex;
      flex-direction: column;
      align-items: center;
    }
}</description>
        <pubDate></pubDate>
        <link>https://kips.kaia.io/assets/css/style.css</link>
        <guid isPermaLink="true">https://kips.kaia.io/assets/css/style.css</guid>
      </item>
    
      <item>
        <title>Tokenization</title>
        <category></category>
        
        <description>{% assign kips=site.pages|where:&quot;type&quot;,&quot;Tokenization&quot; %}
{% include kiptable.html kips=kips %}
</description>
        <pubDate></pubDate>
        <link>https://kips.kaia.io/tokenization</link>
        <guid isPermaLink="true">https://kips.kaia.io/tokenization</guid>
      </item>
    
      <item>
        <title></title>
        <category></category>
        
        <description>@import &quot;minima&quot;;
</description>
        <pubDate></pubDate>
        <link>https://kips.kaia.io/assets/main.css</link>
        <guid isPermaLink="true">https://kips.kaia.io/assets/main.css</guid>
      </item>
    
      <item>
        <title></title>
        <category></category>
        
        <description>Creative Commons Legal Code

CC0 1.0 Universal

    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
    LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
    INFORMATION ON AN &quot;AS-IS&quot; BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
    REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
    PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
    THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
    HEREUNDER.

Statement of Purpose

The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an &quot;owner&quot;) of an original work of
authorship and/or a database (each, a &quot;Work&quot;).

Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works (&quot;Commons&quot;) that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.

For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the &quot;Affirmer&quot;), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.

1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights (&quot;Copyright and
Related Rights&quot;). Copyright and Related Rights include, but are not
limited to, the following:

  i. the right to reproduce, adapt, distribute, perform, display,
     communicate, and translate a Work;
 ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person&apos;s image or
     likeness depicted in a Work;
 iv. rights protecting against unfair competition in regards to a Work,
     subject to the limitations in paragraph 4(a), below;
  v. rights protecting the extraction, dissemination, use and reuse of data
     in a Work;
 vi. database rights (such as those arising under Directive 96/9/EC of the
     European Parliament and of the Council of 11 March 1996 on the legal
     protection of databases, and under any national implementation
     thereof, including any amended or successor version of such
     directive); and
vii. other similar, equivalent or corresponding rights throughout the
     world based on applicable law or treaty, and any national
     implementations thereof.

2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer&apos;s Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the &quot;Waiver&quot;). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer&apos;s heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer&apos;s express Statement of Purpose.

3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer&apos;s express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer&apos;s Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
&quot;License&quot;). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer&apos;s
express Statement of Purpose.

4. Limitations and Disclaimers.

 a. No trademark or patent rights held by Affirmer are waived, abandoned,
    surrendered, licensed or otherwise affected by this document.
 b. Affirmer offers the Work as-is and makes no representations or
    warranties of any kind concerning the Work, express, implied,
    statutory or otherwise, including without limitation warranties of
    title, merchantability, fitness for a particular purpose, non
    infringement, or the absence of latent or other defects, accuracy, or
    the present or absence of errors, whether or not discoverable, all to
    the greatest extent permissible under applicable law.
 c. Affirmer disclaims responsibility for clearing rights of other persons
    that may apply to the Work or any use thereof, including without
    limitation any person&apos;s Copyright and Related Rights in the Work.
    Further, Affirmer disclaims responsibility for obtaining any necessary
    consents, permissions or other rights required for any use of the
    Work.
 d. Affirmer understands and acknowledges that Creative Commons is not a
    party to this document and has no duty or obligation with respect to
    this CC0 or use of the Work.</description>
        <pubDate></pubDate>
        <link>https://kips.kaia.io/LICENSE</link>
        <guid isPermaLink="true">https://kips.kaia.io/LICENSE</guid>
      </item>
    
      <item>
        <title>Terms of Use</title>
        <category></category>
        
        <description># Terms of Use

## 1. Your Use of Open Source Software

We may make (but are not obligated to make) the source code of Kaia Blockchain Network Platform (&quot;Platform&quot;), the software on the Platform, etc. for download as open source software. If you use this open source software, you agree to be bound by and comply with any license that applies to this open source software. You will not indicate that you are associated with us in connection with your use, modifications or distributions of this open source software.

## 2. Services Provided on the Platform

The Platform is a combination of peer-to-peer subnetworks of nodes transmitting transactions and blocks to execute value transfers and run smart contracts. The Core Cell Network (&quot;CCN&quot;), which is one of the subnetworks that constitute the Platform, verifies and executes transactions that occur on the Platform. The CCN is operated by Kaia Governance Council, which is a collective group of Core Cell Operators, and Kaia is not directly involved in any services that are provided in or individual transactions that occur on the Platform.

## 3. Your Installation of BApp on the Platform

Your use of open source software is free of charge. However, you may be required to pay a certain number of KLAY as a transaction fee in order to execute a transaction on the Platform, including the installation of the BApp on the Platform.

Once transaction is executed successfully and respective block generation is verified successfully by the Platform’s mechanism, the block is irreversibly stored in the blockchain. As such, your installation of the BApp and any other transactions on the Platform, as well as the submission of transaction fee is final and irrevocable.

## 4. User Content

If you or the users of your BApp post, upload, input, provide or submit any content on the Platform (collectively, your &quot;User Content&quot;), you must ensure that the User Content provided by you or the users of your BApp at that or at any other time is true, accurate, up to date and complete and that any User Content you or the users of your BApp post, upload, input, provide or submit via the Platform do not breach or infringe legal rights of any third party. To the extent that is technically possible, you agree to prevent, remove or block access of any User Content that you or the users of your BApp post, upload, input, provide or submit via the Platform that violate or may violate legal rights (such as rights of privacy and publicity) of others or any applicable laws or regulations. We do not own, control or endorse any User Content that is transmitted, stored or processed via the Platform or sent to us and we are not responsible or liable for any User Content. We make no assurance that any of Your Content will be secured or that such content will remain confidential.

You are solely responsible and liable for all of your User Content and for your use of any interactive features, links or information or content on the Platform, and you represent and warrant that (i) you own all intellectual property rights (or have obtained all necessary permissions) to provide your User Content and to grant the licenses in these Terms of Use; (ii) your User Content will not violate any agreements or confidentiality obligations; and (iii) your User Content will not violate, infringe or misappropriate any intellectual property right or other proprietary right, including the right of publicity or privacy, of any person or entity. 

You shall not include in User Content, or upload, transmit to or create or include in the Services environment any production data or any sensitive, proprietary, confidential or other data with particular data protection requirements such as personal data or personally identifiable information relating to an identified or identifiable natural person. 

You are prohibited from using the Platform to post or transmit any threatening, libellous, defamatory, obscene, scandalous, inflammatory, pornographic or profane material, any material that is contrary to applicable local, federal, or international laws and regulations, or any material that could constitute or encourage unlawful conduct. You must ensure that your User Content do not include such materials. We may from time to time monitor or review material transmitted or posted using the Network, and we reserve the right to delete any material we deem inappropriate.

We are under no obligation to do so and assume no responsibility or liability arising from any material transmitted or posted using the Platform.

You understand that any information you or users of your BApp upload to the Platform will be distributed among the blockchain nodes and may not removable due to technical limitations of the blockchain technology.

You are entirely responsible for any and all activities that occur under your account or your BApp (if any). You agree to notify us immediately of any unauthorized use of your User Content, your BApp or account or any other breach of security. We will not be liable for any loss or damages that you may incur as a result of someone else using your User Content, your BApp or account, either with or without your knowledge. However, you could be held liable for losses incurred by us or another party due to someone else using your User Content, your BApp or account. You may not use anyone else’s User Content, your BApp or account at any time without the permission of such person or entity. 

By posting, uploading, inputting, providing or submitting your User Content to the Platform, you grant to participants of the Platform and any necessary sub-licensees a non-exclusive, worldwide, perpetual, right and permission to use, reproduce, copy, edit, modify, translate, reformat, create derivative works from, distribute, transmit, publicly perform and publicly display your User Content and sub-license such rights to others.

If we have reason to believe that there is likely to be a breach of security, breach or misuse of the Platform or if you breach any of your obligations under these terms, we may suspend your use of the Platform at any time and for any reason. 
</description>
        <pubDate></pubDate>
        <link>https://kips.kaia.io/TERMS-OF-USE</link>
        <guid isPermaLink="true">https://kips.kaia.io/TERMS-OF-USE</guid>
      </item>
    
      <item>
        <title></title>
        <category></category>
        
        <description>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;{% if page.xsl %}&lt;?xml-stylesheet type=&quot;text/xml&quot; href=&quot;{{ &apos;/feed.xslt.xml&apos; | absolute_url }}&quot;?&gt;{% endif %}&lt;feed xmlns=&quot;http://www.w3.org/2005/Atom&quot; {% if site.lang %}xml:lang=&quot;{{ site.lang }}&quot;{% endif %}&gt;&lt;generator uri=&quot;https://jekyllrb.com/&quot; version=&quot;{{ jekyll.version }}&quot;&gt;Jekyll&lt;/generator&gt;&lt;link href=&quot;{{ page.url | absolute_url }}&quot; rel=&quot;self&quot; type=&quot;application/atom+xml&quot; /&gt;&lt;link href=&quot;{{ &apos;/&apos; | absolute_url }}&quot; rel=&quot;alternate&quot; type=&quot;text/html&quot; {% if site.lang %}hreflang=&quot;{{ site.lang }}&quot; {% endif %}/&gt;&lt;updated&gt;{{ site.time | date_to_xmlschema }}&lt;/updated&gt;&lt;id&gt;{{ page.url | absolute_url | xml_escape }}&lt;/id&gt;{% assign title = site.title | default: site.name %}{% if page.collection != &quot;posts&quot; %}{% assign collection = page.collection | capitalize %}{% assign title = title | append: &quot; | &quot; | append: collection %}{% endif %}{% if page.category %}{% assign category = page.category | capitalize %}{% assign title = title | append: &quot; | &quot; | append: category %}{% endif %}{% if title %}&lt;title type=&quot;html&quot;&gt;{{ title | smartify | xml_escape }}&lt;/title&gt;{% endif %}{% if site.description %}&lt;subtitle&gt;{{ site.description | xml_escape }}&lt;/subtitle&gt;{% endif %}{% if site.author %}&lt;author&gt;&lt;name&gt;{{ site.author.name | default: site.author | xml_escape }}&lt;/name&gt;{% if site.author.email %}&lt;email&gt;{{ site.author.email | xml_escape }}&lt;/email&gt;{% endif %}{% if site.author.uri %}&lt;uri&gt;{{ site.author.uri | xml_escape }}&lt;/uri&gt;{% endif %}&lt;/author&gt;{% endif %}{% if page.tags %}{% assign posts = site.tags[page.tags] %}{% else %}{% assign posts = site[page.collection] %}{% endif %}{% if page.category %}{% assign posts = posts | where: &quot;categories&quot;, page.category %}{% endif %}{% unless site.show_drafts %}{% assign posts = posts | where_exp: &quot;post&quot;, &quot;post.draft != true&quot; %}{% endunless %}{% assign posts = posts | sort: &quot;date&quot; | reverse %}{% assign posts_limit = site.feed.posts_limit | default: 10 %}{% for post in posts limit: posts_limit %}&lt;entry{% if post.lang %}{{&quot; &quot;}}xml:lang=&quot;{{ post.lang }}&quot;{% endif %}&gt;{% assign post_title = post.title | smartify | strip_html | normalize_whitespace | xml_escape %}&lt;title type=&quot;html&quot;&gt;{{ post_title }}&lt;/title&gt;&lt;link href=&quot;{{ post.url | absolute_url }}&quot; rel=&quot;alternate&quot; type=&quot;text/html&quot; title=&quot;{{ post_title }}&quot; /&gt;&lt;published&gt;{{ post.date | date_to_xmlschema }}&lt;/published&gt;&lt;updated&gt;{{ post.last_modified_at | default: post.date | date_to_xmlschema }}&lt;/updated&gt;&lt;id&gt;{{ post.id | absolute_url | xml_escape }}&lt;/id&gt;{% assign excerpt_only = post.feed.excerpt_only | default: site.feed.excerpt_only %}{% unless excerpt_only %}&lt;content type=&quot;html&quot; xml:base=&quot;{{ post.url | absolute_url | xml_escape }}&quot;&gt;&lt;![CDATA[{{ post.content | strip }}]]&gt;&lt;/content&gt;{% endunless %}{% assign post_author = post.author | default: post.authors[0] | default: site.author %}{% assign post_author = site.data.authors[post_author] | default: post_author %}{% assign post_author_email = post_author.email | default: nil %}{% assign post_author_uri = post_author.uri | default: nil %}{% assign post_author_name = post_author.name | default: post_author %}&lt;author&gt;&lt;name&gt;{{ post_author_name | default: &quot;&quot; | xml_escape }}&lt;/name&gt;{% if post_author_email %}&lt;email&gt;{{ post_author_email | xml_escape }}&lt;/email&gt;{% endif %}{% if post_author_uri %}&lt;uri&gt;{{ post_author_uri | xml_escape }}&lt;/uri&gt;{% endif %}&lt;/author&gt;{% if post.category %}&lt;category term=&quot;{{ post.category | xml_escape }}&quot; /&gt;{% elsif post.categories %}{% for category in post.categories %}&lt;category term=&quot;{{ category | xml_escape }}&quot; /&gt;{% endfor %}{% endif %}{% for tag in post.tags %}&lt;category term=&quot;{{ tag | xml_escape }}&quot; /&gt;{% endfor %}{% assign post_summary = post.description | default: post.excerpt %}{% if post_summary and post_summary != empty %}&lt;summary type=&quot;html&quot;&gt;&lt;![CDATA[{{ post_summary | strip_html | normalize_whitespace }}]]&gt;&lt;/summary&gt;{% endif %}{% assign post_image = post.image.path | default: post.image %}{% if post_image %}{% unless post_image contains &quot;://&quot; %}{% assign post_image = post_image | absolute_url %}{% endunless %}&lt;media:thumbnail xmlns:media=&quot;http://search.yahoo.com/mrss/&quot; url=&quot;{{ post_image | xml_escape }}&quot; /&gt;&lt;media:content medium=&quot;image&quot; url=&quot;{{ post_image | xml_escape }}&quot; xmlns:media=&quot;http://search.yahoo.com/mrss/&quot; /&gt;{% endif %}&lt;/entry&gt;{% endfor %}&lt;/feed&gt;</description>
        <pubDate></pubDate>
        <link>https://kips.kaia.io/feed.xml</link>
        <guid isPermaLink="true">https://kips.kaia.io/feed.xml</guid>
      </item>
    
      <item>
        <title>KIP Purpose and Guidelines</title>
        <category>Meta</category>
        
        <description>## What is a KIP?

KIP stands for Kaia Improvement Proposal. A KIP is a design document providing information to the Kaia community, or describing a new feature for Kaia or its processes or environment. The KIP should provide a concise technical specification of the feature and a rationale for the feature. The KIP author is responsible for building consensus within the community and documenting dissenting opinions.

## KIP Rationale

We intend KIPs to be the primary mechanisms for proposing new features, for collecting community technical input on an issue, and for documenting the design decisions that have gone into Kaia. Because the KIPs are maintained as text files in a versioned repository, their revision history is the historical record of the feature proposal.

For Kaia implementers, KIPs are a convenient way to track the progress of their implementation. Ideally each implementation maintainer would list the KIPs that they have implemented. This will give end users a convenient way to know the current status of a given implementation or library.

## KIP Types

There are three types of KIP:

- A **Standard Track KIP** describes any change that affects most or all Kaia implementations, such as a change to the network protocol, a change in block or transaction validity rules, proposed application standards/conventions, or any change or addition that affects the interoperability of applications using Kaia. Furthermore Standard Track KIPs can be broken down into the following categories. Standards Track KIPs consist of two parts, a design document and implementation.
  - **Core** - improvements requiring a consensus fork as well as changes that are not necessarily consensus critical but may be relevant to core development.
  - **Networking** - includes improvements related to networking layers.
  - **Storage** - includes improvements related to storage layers.
  - **Interface** - includes improvements around client API/RPC specifications and standards, and also certain language-level standards like method names and contract ABIs.
  - **KCT** - includes improvements or standards related to Kaia compatible tokens.
  - **SDK** - includes improvements related to SDKs.
  - **Application** - application-level standards and conventions, such as name registries, URI schemes, library/package formats, and wallet formats.
- A **Meta KIP** describes a process surrounding Kaia or proposes a change to (or an event in) a process. Meta KIPs are like Standard Track KIPs but apply to areas other than the Kaia protocol itself. They may propose an implementation, but not to Kaia&apos;s codebase; they often require community consensus; unlike Informational KIPs, they are more than recommendations, and users are typically not free to ignore them. Examples include procedures, guidelines, changes to the decision-making process, and changes to the tools or environment used in Kaia development. Any Meta KIP is also considered a Process KIP.
- An **Informational KIP** describes a Kaia design issue, or provides general guidelines or information to the Kaia community, but does not propose a new feature. Informational KIPs do not necessarily represent Kaia community consensus or a recommendation, so users and implementers are free to ignore Informational KIPs or follow their advice.

It is highly recommended that a single KIP contains a single key proposal or new idea. The more focused the KIP, the more successful it tends to be. A change to one client doesn&apos;t require a KIP; a change that affects multiple clients, or defines a standard for multiple apps to use, does.

A KIP must meet certain minimum criteria. It must be a clear and complete description of the proposed enhancement. The enhancement must represent a net improvement. The proposed implementation, if applicable, must be solid and must not complicate the protocol unduly.

### Special requirements for Core KIPs

If a **Core** KIP mentions or proposes changes to the KLVM (Kaia Virtual Machine, forked from Byzantium EVM), it should refer to the instructions by their mnemonics and define the opcodes of those mnemonics at least once. A preferred way is the following:
```
REVERT (0xfe)
```

## KIP Work Flow

### Shepherding a KIP

Parties involved in the process are you, the champion or *KIP author*, the [*KIP editors*](#kip-editors), and the Kaia core developers.

Before you begin writing a formal KIP, you should vet your idea. Ask the Kaia community first if an idea is original to avoid wasting time on something that will be be rejected based on prior research. It is thus recommended to open a discussion thread on [the Issues section of this repository](https://github.com/kaiachain/KIPs/issues). 

In addition to making sure your idea is original, it will be your role as the author to make your idea clear to reviewers and interested parties, as well as inviting editors, developers and community to give feedback on the aforementioned channels. You should try and gauge whether the interest in your KIP is commensurate with both the work involved in implementing it and how many parties will have to conform to it. For example, the work required for implementing a Core KIP will be much greater than for others and the KIP will need sufficient interest from the Kaia client teams. Negative community feedback will be taken into consideration and may prevent your KIP from moving past the Draft stage.

### Core KIPs

For Core KIPs, given that they require client implementations to be considered **Final** (see &quot;KIPs Process&quot; below), you will need to either provide an implementation for clients or convince clients to implement your KIP. 

In short, your role as the champion is to write the KIP using the style and format described below, shepherd the discussions in the appropriate forums, and build community consensus around the idea.

### KIP Process 

Following is the process that a successful KIP will move along:

```
[ IDEA ] -&gt; [ DRAFT ] -&gt; [ LAST CALL ] -&gt; [ ACCEPTED ] -&gt; [ FINAL ]
```

Each status change is requested by the KIP author and reviewed by the KIP editors. Use a pull request to update the status. Please include a link to where people should continue discussing your KIP. The KIP editors will process these requests as per the conditions below.

* **Idea** -- Once the champion has asked the Kaia community in [Dev Forum - TBD](https://devforum.kaia.io/c/english/kip/55) whether an idea has any chance of support, they will write a draft KIP as a [pull request](https://github.com/kaiachain/KIPs/pulls). Consider including an implementation if this will aid people in studying the KIP.
  * :arrow_right: Draft -- If agreeable, a KIP editor will assign the KIP a number (generally the issue or PR number related to the KIP) and merge your pull request. The KIP editor will not unreasonably deny a KIP.
  * :x: Draft -- Reasons for denying draft status include being too unfocused, too broad, duplication of effort, being technically unsound, not providing proper motivation or addressing backwards compatibility.
* **Draft** -- Once the first draft has been merged, you may submit follow-up pull requests with further changes to your draft until such point as you believe the KIP to be mature and ready to proceed to the next status. A KIP in draft status must be implemented to be considered for promotion to the next status.
  * :arrow_right: Last Call -- If agreeable, the KIP editor will assign Last Call status and set a review end date (`review-period-end`), normally 14 days later.
  * :x: Last Call -- A request for Last Call status will be denied if material changes are still expected to be made to the draft. We hope that KIPs only enter Last Call once.
* **Last Call** -- This KIP will listed prominently as a pinned issue.
  * :x: -- A Last Call which results in material changes or substantial unaddressed technical complaints will cause the KIP to revert to Draft.
  * :arrow_right: Accepted -- A successful Last Call without material changes or unaddressed technical complaints will become Accepted.
* **Accepted** -- This status signals that material changes are unlikely and Kaia client developers should consider this KIP for inclusion. Their process for deciding whether to encode it into their clients as part of a hard fork is not part of the KIP process.
  * :arrow_right: Draft -- The KIP can be decided to move it back to the Draft status at the discretion. E.g. a major, but correctable, flaw was found in the KIP.
  * :arrow_right: Rejected -- The KIP can be decided to be marked as this KIP as Rejected at their discretion. E.g. a major, but uncorrectable, flaw was found in the KIP.
  * :arrow_right: Final -- Standard Track Core KIPs must be implemented in any of Kaia clients before it can be considered Final. When the implementation is complete and adopted by the community, the status will be changed to “Final”.
* **Final** -- This KIP represents the current state-of-the-art. A Final KIP should only be updated to correct errata.

Other exceptional statuses include:

* **Active** -- Some Informational and Process KIPs may also have a status of “Active” if they are never meant to be completed. E.g. KIP 1 (this KIP).
* **Abandoned** -- This KIP is no longer pursued by the original authors or it may not be a (technically) preferred option anymore.
  * :arrow_right: Draft -- Authors or new champions wishing to pursue this KIP can ask for changing it to Draft status.
* **Rejected** -- A KIP that is fundamentally broken or a Core KIP that was rejected by the Core Devs and will not be implemented. A KIP cannot move on from this state.
* **Superseded** -- A KIP which was previously Final but is no longer considered state-of-the-art. Another KIP will be in Final status and reference the Superseded KIP. A KIP cannot move on from this state.

## What belongs in a successful KIP?

Each KIP should have the following parts:

- Preamble - RFC 822 style headers containing metadata about the KIP, including the KIP number, a short descriptive title (limited to a maximum of 44 characters), and the author details. See [below](#kip-header-preamble) for details.
- Abstract - A short (~200 word) description of the technical issue being addressed.
- Motivation (*optional) - The motivation is critical for KIPs that want to change the Kaia protocol. It should clearly explain why the existing protocol specification is inadequate to address the problem that the KIP solves. KIP submissions without sufficient motivation may be rejected outright.
- Specification - The technical specification should describe the syntax and semantics of any new feature. The specification should be detailed enough to allow competing, interoperable implementations for any of the current Kaia platforms.
- Rationale - The rationale fleshes out the specification by describing what motivated the design and why particular design decisions were made. It should describe alternate designs that were considered and related work, e.g. how the feature is supported in other languages. The rationale may also provide evidence of consensus within the community, and should discuss important objections or concerns raised during discussion.
- Backwards Compatibility - All KIPs that introduce backwards incompatibilities must include a section describing these incompatibilities and their severity. The KIP must explain how the author proposes to deal with these incompatibilities. KIP submissions without a sufficient backwards compatibility treatise may be rejected outright.
- Test Cases - Test cases for an implementation are mandatory for KIPs that are affecting consensus changes. Other KIPs can choose to include links to test cases if applicable.
- Implementations - The implementations must be completed before any KIP is given status “Final”, but it need not be completed before the KIP is merged as draft. While there is merit to the approach of reaching consensus on the specification and rationale before writing code, the principle of “rough consensus and running code” is still useful when it comes to resolving many discussions of API details.
- Copyright Waiver - All KIPs must be in the public domain. See the bottom of this KIP for an example copyright waiver.

## KIP Formats and Templates

KIPs should be written in [markdown](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet) format.
Image files should be included in a subdirectory of the `assets` folder for that KIP as follows: `assets/kip-N` (where **N** is to be replaced with the KIP number). When linking to an image in the KIP, use relative links such as `../assets/kip-1/image.png`.

## KIP Header Preamble

Each KIP must begin with an [RFC 822](https://www.ietf.org/rfc/rfc822.txt) style header preamble, preceded and followed by three hyphens (`---`). This header is also termed [&quot;front matter&quot; by Jekyll](https://jekyllrb.com/docs/front-matter/). The headers must appear in the following order. Headers marked with &quot;*&quot; are optional and are described below. All other headers are required.

` kip:` *KIP number* (this is determined by the KIP editor)

` title:` *KIP title*

` author:` *a list of the author&apos;s or authors&apos; name(s) and/or username(s), or name(s) and email(s). Details are below.*

` * discussions-to:` *a url pointing to the official discussion thread*

` status:` *Draft | Last Call | Accepted | Final | Active | Abandoned | Rejected | Superseded*

` * review-period-end:` *date review period ends*

` type:` *Standards Track | Informational | Meta*

` * category:` *Core | Networking | Storage | Interface | KCT | SDK | Application* (Standards Track KIPs only)

` created:` *date created on*

` * updated:` *comma separated list of dates*

` * requires:` *KIP number(s)*

` * replaces:` *KIP number(s)*

` * superseded-by:` *KIP number(s)*

` * resolution:` *a url pointing to the resolution of this KIP*

Headers that permit lists must separate elements with commas.

Headers requiring dates will always do so in the format of ISO 8601 (yyyy-mm-dd).

#### `author` header

The `author` header optionally lists the names, email addresses or usernames of the authors/owners of the KIP. Those who prefer anonymity may use a username only, or a first name and a username. The format of the author header value must be:

&gt; Random J. User &amp;lt;address@dom.ain&amp;gt;

or

&gt; Random J. User (@username)

if the email address or GitHub username is included, and

&gt; Random J. User

if the email address is not given.

#### `resolution` header

The `resolution` header is required for Standards Track KIPs only. It contains a URL that should point to an email message or other web resource where the pronouncement about the KIP is made.

#### `discussions-to` header

While a KIP is a draft, a `discussions-to` header will indicate the mailing list or URL where the KIP is being discussed. As mentioned above, examples for places to discuss your KIP include an issue in this repo or in a fork of this repo.

No `discussions-to` header is necessary if the KIP is being discussed privately with the author.

As a single exception, `discussions-to` cannot point to GitHub pull requests.

#### `type` header

The `type` header specifies the type of KIP: Standards Track, Meta, or Informational. If the track is Standards Track, please include the subcategory (core, networking, storage, interface, token, SDK or application).

#### `category` header

The `category` header specifies the KIP&apos;s category. This is required for standards-track KIPs only.

#### `created` header

The `created` header records the date that the KIP was assigned a number. Both headers should be in yyyy-mm-dd format, e.g. 2001-08-14.

#### `updated` header

The `updated` header records the date(s) when the KIP was updated with &quot;substantial&quot; changes. This header is only valid for KIPs of Draft and Active status.

#### `requires` header

KIPs may have a `requires` header, indicating the KIP numbers that this KIP depends on.

#### `superseded-by` and `replaces` headers

KIPs may also have a `superseded-by` header indicating that a KIP has been rendered obsolete by a later document; the value is the number of the KIP that replaces the current document. The newer KIP must have a `replaces` header containing the number of the KIP that it rendered obsolete.

## Auxiliary Files

KIPs may include auxiliary files such as diagrams. Such files must be named KIP-XXXX-Y.ext, where “XXXX” is the KIP number, “Y” is a serial number (starting at 1), and “ext” is replaced by the actual file extension (e.g. “png”).

## Transferring KIP Ownership

It occasionally becomes necessary to transfer ownership of KIPs to a new champion. In general, we&apos;d like to retain the original author as a co-author of the transferred KIP, but that&apos;s really up to the original author. A good reason to transfer ownership is because the original author no longer has the time or interest in updating it or following through with the KIP process, or has fallen off the face of the &apos;net (i.e. is unreachable or isn&apos;t responding to email). A bad reason to transfer ownership is because you don&apos;t agree with the direction of the KIP. We try to build consensus around a KIP, but if that&apos;s not possible, you can always submit a competing KIP.

If you are interested in assuming ownership of a KIP, send a message asking to take over, addressed to both the original author and the KIP editor. If the original author doesn&apos;t respond to email in a timely manner, the KIP editor will make a unilateral decision (it&apos;s not like such decisions can&apos;t be reversed :)).

## KIP Editors

The current KIP editors are

` * Kyungup Kim (@KimKyungup)`

` * Junghyun Colin Kim (@kjhman21)`

` * Sangmin Seo (@smseo)`

## KIP Editor Responsibilities

For each new KIP that comes in, an editor does the following:

- Read the KIP to check if it is ready: sound and complete. The ideas must make technical sense, even if they don&apos;t seem likely to get to final status.
- The title should accurately describe the content.
- Check the KIP for language (spelling, grammar, sentence structure, etc.), markup (Github flavored Markdown), code style

If the KIP isn&apos;t ready, the editor will send it back to the author for revision, with specific instructions.

Once the KIP is ready for the repository, the KIP editor will:

- Assign a KIP number (generally the PR number or, if preferred by the author, the Issue # if there was discussion in the Issues section of this repository about this KIP)

- Merge the corresponding pull request

- Send a message back to the KIP author with the next step.

Many KIPs are written and maintained by developers with write access to the Kaia codebase. The KIP editors monitor KIP changes, and correct any structure, grammar, spelling, or markup mistakes we see.

The editors don&apos;t pass judgment on KIPs. We merely do the administrative &amp; editorial part.

## History

This document was derived heavily from [Ethereum&apos;s EIP-1](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1.md) written by Martin Becze, Hudson Jameson, et al.

[Ethereum&apos;s EIP-1](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1.md) was derived heavily from [Bitcoin&apos;s BIP-0001](https://github.com/bitcoin/bips) written by Amir Taaki which in turn was derived from [Python&apos;s PEP-0001](https://www.python.org/dev/peps/). In many places text was simply copied and modified.

The authors of the documents are not responsible for its use in the Kaia Improvement Proposal, and should not be bothered with technical questions specific to Kaia or the KIP. Please direct all comments to the KIP editors.

## Copyright

Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
</description>
        <pubDate>Sun, 10 Nov 2019 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-1</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-1</guid>
      </item>
    
      <item>
        <title>Klaytn Keystore Format v4</title>
        <category>Ecosystem</category>
        
          <comments>https://github.com/klaytn/klaytn/issues/438</comments>
        
        <description>&lt;!--You can leave these HTML comments in your merged KIP and delete the visible duplicate text guides, they will not appear and may be helpful to refer to if you edit it again. This is the suggested template for new KIPs. Note that a KIP number will be assigned by an editor. When opening a pull request to submit your KIP, please use an abbreviated title in the filename, `kip-draft_title_abbrev.md`. The title should be 44 characters or less.--&gt;
# Klaytn Keystore Format v4

## Simple Summary
&lt;!--&quot;If you can&apos;t explain it simply, you don&apos;t understand it well enough.&quot; Provide a simplified and layman-accessible explanation of the KIP.--&gt;
This documentation defines the format of a keystore file which is used to securely store private keys of a Klaytn account.

## Abstract
&lt;!--A short (~200 word) description of the technical issue being addressed.--&gt;
Since a Klaytn account can have one or more private keys, the format can provide a better way of storing multiple private keys securely and collectively instead of storing raw private keys.

## Motivation
&lt;!--The motivation is critical for KIPs that want to change the Klaytn protocol. It should clearly explain why the existing protocol specification is inadequate to address the problem that the KIP solves. KIP submissions without sufficient motivation may be rejected outright.--&gt;
Klaytn improves usability by decoupling addresses and key pairs as well as providing various account key types such as multi-signature and role-based keys.
Due to this change, [keystore v3](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) is insufficient for Klaytn.
Klaytn keystore format v4 is suitable for Klaytn by storing multiple private keys while it is based on keystore v3.

## Specification
&lt;!--The technical specification should describe the syntax and semantics of any new feature. The specification should be detailed enough to allow competing, interoperable implementations for any of the current Klaytn platforms (klaytn). --&gt;

The specification of Klaytn keystore format v4 in JSON for a Klaytn account is shown below.
This format can express various Klaytn account key types such as a single key, multiple keys, and role-based keys. 

```
&lt;keystore&gt; :=
{
  &quot;version&quot;: 4,
  &quot;id&quot;: &lt;a 128-bit UUID in hexstring&gt;,
  &quot;address&quot;: &lt;Klaytn address, a 160-bit hexstring&gt;,
  &quot;keyring&quot;: &lt;keyring&gt;
}

&lt;keyring&gt; := [&lt;key&gt; (, &lt;key&gt;)*] | [&lt;keys&gt; (, &lt;keys&gt;)*]

&lt;keys&gt; := [] | [&lt;key&gt; (, &lt;key&gt;)*]

&lt;key&gt; :=
{
  &quot;cipher&quot;: &quot;aes-128-ctr&quot;,
  &quot;ciphertext&quot;: &lt;a 256-bit hexstring&gt;,
  &quot;cipherparams&quot;: {
    &quot;iv&quot;: &lt;a 128-bit hexstring&gt;
  },
  &quot;kdf&quot;: &quot;scrypt&quot; | &quot;pbkdf2&quot;,
  &quot;kdfparams&quot;: &lt;kdfparams-scrypt&gt; | &lt;kdfparams-pbkdf2&gt;,
  &quot;mac&quot;: &lt;a 256-bit hexstring&gt;
}

&lt;kdfparams-scrypt&gt; :=
{
  &quot;dklen&quot;: &lt;an integer equal or bigger than 32&gt;,
  &quot;salt&quot;: &lt;a 256-bit hexstring&gt;,
  &quot;n&quot;: &lt;an integer&gt;,
  &quot;r&quot;: &lt;an integer&gt;,
  &quot;p&quot;: &lt;an integer&gt;,
}

&lt;kdfparams-pbkdf2&gt; :=
{
  &quot;dklen&quot;: &lt;an integer&gt;,
  &quot;salt&quot;: &lt;a 256-bit hexstring&gt;,
  &quot;prf&quot;: &quot;hmac-sha256&quot;,
  &quot;c&quot;: &lt;an integer&gt;,
}

```

## Examples
Examples are as following:

1. A single key
    ```
    {
        &quot;version&quot;: 4,
        &quot;id&quot;: &quot;b6c6bd8f-e3ba-43c1-af4b-1b7dfe13e5f9&quot;,
        &quot;address&quot;: &quot;0xa8677855999712d23692a4cad77bb9396a1174cd&quot;,
        &quot;keyring&quot;: [
            {
                &quot;ciphertext&quot;: &quot;5cbd3cf235d02718f1cfac84157786d8e36fcf651e15f0c9a4303a2338697126&quot;,
                &quot;cipherparams&quot;: {
                    &quot;iv&quot;: &quot;92a89f3011e020f6a70bb34fddb006c5&quot;
                },
                &quot;cipher&quot;: &quot;aes-128-ctr&quot;,
                &quot;kdf&quot;: &quot;scrypt&quot;,
                &quot;kdfparams&quot;: {
                    &quot;dklen&quot;: 32,
                    &quot;salt&quot;: &quot;aa38c864f4f6f538dc3bb4961c9d5d5772b163d2243aa8472cc4f83338e67cd3&quot;,
                    &quot;n&quot;: 4096,
                    &quot;r&quot;: 8,
                    &quot;p&quot;: 1
                },
                &quot;mac&quot;: &quot;3355abc00f3fb1ae06821ece9c13359a0da1bd284cf29ffc26a534a40d210f28&quot;
            }
        ]
    }
    ```
2. Multiple keys
    ```
    {
        &quot;version&quot;: 4,
        &quot;id&quot;: &quot;b6c6bd8f-e3ba-43c1-af4b-1b7dfe13e5f9&quot;,
        &quot;address&quot;: &quot;0xa8677855999712d23692a4cad77bb9396a1174cd&quot;,
        &quot;keyring&quot;: [
            {
               &quot;ciphertext&quot;: &quot;5cbd3cf235d02718f1cfac84157786d8e36fcf651e15f0c9a4303a2338697126&quot;,
               &quot;cipherparams&quot;: {
                   &quot;iv&quot;: &quot;92a89f3011e020f6a70bb34fddb006c5&quot;
               },
               &quot;cipher&quot;: &quot;aes-128-ctr&quot;,
               &quot;kdf&quot;: &quot;scrypt&quot;,
               &quot;kdfparams&quot;: {
                   &quot;dklen&quot;: 32,
                   &quot;salt&quot;: &quot;aa38c864f4f6f538dc3bb4961c9d5d5772b163d2243aa8472cc4f83338e67cd3&quot;,
                   &quot;n&quot;: 4096,
                   &quot;r&quot;: 8,
                   &quot;p&quot;: 1
                 },
                &quot;mac&quot;: &quot;3355abc00f3fb1ae06821ece9c13359a0da1bd284cf29ffc26a534a40d210f28&quot;
            },
            {
               &quot;ciphertext&quot;: &quot;5e2f95f61d7af3bebf4ff9f5d5813690c80b0b5aaebd6e8b22d0f928ff06776a&quot;,
               &quot;cipherparams&quot;: {
                   &quot;iv&quot;: &quot;92a89f3011e020f6a70bb34fddb006c5&quot;
               },
               &quot;cipher&quot;: &quot;aes-128-ctr&quot;,
               &quot;kdf&quot;: &quot;scrypt&quot;,
               &quot;kdfparams&quot;: {
                   &quot;dklen&quot;: 32,
                   &quot;salt&quot;: &quot;e7c4605ad8200e0d93cd67f9d82fb9971e1a2763b22362017c2927231c2a733a&quot;,
                   &quot;n&quot;: 4096,
                   &quot;r&quot;: 8,
                   &quot;p&quot;: 1,
               },
               &quot;mac&quot;: &quot;fb86255428e24ba701201d5815f2f2114214cbd34fe4bc7a24b948a8ceac9f9b&quot;,
            },
        ]
    }
    ```
3. Role-based keys
    ```
    {
        &quot;version&quot;: 4,
        &quot;id&quot;: &quot;b6c6bd8f-e3ba-43c1-af4b-1b7dfe13e5f9&quot;,
        &quot;address&quot;: &quot;0xa8677855999712d23692a4cad77bb9396a1174cd&quot;,
        &quot;keyring&quot;: [
            [
                {
                &quot;ciphertext&quot;: &quot;5cbd3cf235d02718f1cfac84157786d8e36fcf651e15f0c9a4303a2338697126&quot;,
                &quot;cipherparams&quot;: {
                    &quot;iv&quot;: &quot;92a89f3011e020f6a70bb34fddb006c5&quot;
                },
                &quot;cipher&quot;: &quot;aes-128-ctr&quot;,
                &quot;kdf&quot;: &quot;scrypt&quot;,
                &quot;kdfparams&quot;: {
                    &quot;dklen&quot;: 32,
                    &quot;salt&quot;: &quot;aa38c864f4f6f538dc3bb4961c9d5d5772b163d2243aa8472cc4f83338e67cd3&quot;,
                    &quot;n&quot;: 4096,
                    &quot;r&quot;: 8,
                    &quot;p&quot;: 1
                },
                &quot;mac&quot;: &quot;3355abc00f3fb1ae06821ece9c13359a0da1bd284cf29ffc26a534a40d210f28&quot;
            }],
            [],
            [
                {
                   &quot;ciphertext&quot;: &quot;5e2f95f61d7af3bebf4ff9f5d5813690c80b0b5aaebd6e8b22d0f928ff06776a&quot;,
                   &quot;cipherparams&quot;: {
                       &quot;iv&quot;: &quot;92a89f3011e020f6a70bb34fddb006c5&quot;
                   },
                   &quot;cipher&quot;: &quot;aes-128-ctr&quot;,
                   &quot;kdf&quot;: &quot;scrypt&quot;,
                   &quot;kdfparams&quot;: {
                       &quot;dklen&quot;: 32,
                       &quot;salt&quot;: &quot;e7c4605ad8200e0d93cd67f9d82fb9971e1a2763b22362017c2927231c2a733a&quot;,
                       &quot;n&quot;: 4096,
                       &quot;r&quot;: 8,
                       &quot;p&quot;: 1,
                   },
                   &quot;mac&quot;: &quot;fb86255428e24ba701201d5815f2f2114214cbd34fe4bc7a24b948a8ceac9f9b&quot;,
                },
               {
                   &quot;ciphertext&quot;: &quot;5a17fe2af445e63ed2cdda6834d030a9391998000941c79318ab49bff092b9e7&quot;,
                   &quot;cipherparams&quot;: { 
                     &quot;iv&quot;: &quot;38aa896fc128075425e512f01e4b206c&quot; 
                   },
                   &quot;cipher&quot;: &quot;aes-128-ctr&quot;,
                   &quot;kdf&quot;: &quot;scrypt&quot;,
                   &quot;kdfparams&quot;: {
                       &quot;dklen&quot;: 32,
                       &quot;salt&quot;: &quot;e7c4605ad8200e0d93cd67f9d82fb9971e1a2763b22362017c2927231c2a733a&quot;,
                       &quot;n&quot;: 4096,
                       &quot;r&quot;: 8,
                       &quot;p&quot;: 1,
                   },
                   &quot;mac&quot;: &quot;633f91994f33541fbf1c3c3e973e539c12f1dd98f2757f64e3b63de986f367e0&quot;,
               },
            ]
        ]
    }
    ```

## Rationale
&lt;!--The rationale fleshes out the specification by describing what motivated the design and why particular design decisions were made. It should describe alternate designs that were considered and related work, e.g. how the feature is supported in other languages. The rationale may also provide evidence of consensus within the community, and should discuss important objections or concerns raised during discussion.--&gt;
The presentation of keys can be an array or an object for a role-based key.
In this specification, an array is chosen because of the following reasons:

1. Any format has extensibility. If Klaytn introduces a new role, any format can support the new role.
2. The array format is used when RLP-encoding TxTypeAccountUpdate transactions, so the array format is consistent with this.
3. Using an object with special strings as keys can have possibilities to use misspelled keys (e.g., &quot;roleTransactin&quot;).

## Backwards Compatibility
&lt;!-- All KIPs that introduce backwards incompatibilities must include a section describing these incompatibilities and their severity. The KIP must explain how the author proposes to deal with these incompatibilities. KIP submissions without a sufficient backwards compatibility treatise may be rejected outright. The authors should answer the question: &quot;Does this KIP require a hard fork?&quot; --&gt;
Klaytn keystore format v4 has a field `keyring` while keystore v3 has a field `crypto` to securely store private keys.
This means v4 is not compatible with v3, hence keystore v3 should also be supported along with the Klaytn keystore format.

## Test Cases
&lt;!--Test cases for an implementation are mandatory for KIPs that are affecting consensus changes. Other KIPs can choose to include links to test cases if applicable.--&gt;
1. The proposed examples above should be accepted.
2. The keystore v3 should also be supported. One example is shown below:
```
{
    &quot;version&quot;: 3,
    &quot;id&quot;: &quot;7a0a8557-22a5-4c90-b554-d6f3b13783ea&quot;,
    &quot;address&quot;: &quot;0x86bce8c859f5f304aa30adb89f2f7b6ee5a0d6e2&quot;,
    &quot;crypto&quot;: {
        &quot;ciphertext&quot;: &quot;696d0e8e8bd21ff1f82f7c87b6964f0f17f8bfbd52141069b59f084555f277b7&quot;,
        &quot;cipherparams&quot;: {
          &quot;iv&quot;: &quot;1fd13e0524fa1095c5f80627f1d24cbd&quot; 
        },
        &quot;cipher&quot;: &quot;aes-128-ctr&quot;,
        &quot;kdf&quot;: &quot;scrypt&quot;,
        &quot;kdfparams&quot;: {
            &quot;dklen&quot;: 32,
            &quot;salt&quot;: &quot;7ee980925cef6a60553cda3e91cb8e3c62733f64579f633d0f86ce050c151e26&quot;,
            &quot;n&quot;: 4096,
            &quot;r&quot;: 8,
            &quot;p&quot;: 1,
        },
        &quot;mac&quot;: &quot;8684d8dc4bf17318cd46c85dbd9a9ec5d9b290e04d78d4f6b5be9c413ff30ea4&quot;,
    },
}
``` 

## Implementation
&lt;!--The implementations must be completed before any KIP is given status &quot;Final&quot;, but it need not be completed before the KIP is accepted. While there is merit to the approach of reaching consensus on the specification and rationale before writing code, the principle of &quot;rough consensus and running code&quot; is still useful when it comes to resolving many discussions of API details.--&gt;
- klaytn
    - https://github.com/klaytn/klaytn/pull/439
    - https://github.com/klaytn/klaytn/pull/440
    - https://github.com/klaytn/klaytn/pull/441
    - https://github.com/klaytn/klaytn/pull/442

## Copyright
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).

# Reference
https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition
</description>
        <pubDate>Fri, 03 Jan 2020 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-3</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-3</guid>
      </item>
    
      <item>
        <title>Fungible Token Standard</title>
        <category>Tokenization</category>
        
          <comments>https://github.com/klaytn/kips/issues/9</comments>
        
        <description>&lt;!--You can leave these HTML comments in your merged KIP and delete the visible duplicate text guides, they will not appear and may be helpful to refer to if you edit it again. This is the suggested template for new KIPs. Note that a KIP number will be assigned by an editor. When opening a pull request to submit your KIP, please use an abbreviated title in the filename, `kip-draft_title_abbrev.md`. The title should be 44 characters or less.--&gt;

## Simple Summary
&lt;!--&quot;If you can&apos;t explain it simply, you don&apos;t understand it well enough.&quot; Provide a simplified and layman-accessible explanation of the KIP.--&gt;
A fungible token standard for Klaytn.

## Abstract
&lt;!--A short (~200 word) description of the technical issue being addressed.--&gt;
The following standard allows for the implementation of a standard API for tokens within smart contracts.
This standard provides basic functionality to transfer tokens.

## Motivation
&lt;!--The motivation is critical for KIPs that want to change the Klaytn protocol. It should clearly explain why the existing protocol specification is inadequate to address the problem that the KIP solves. KIP submissions without sufficient motivation may be rejected outright.--&gt;
A standard interface allows any token on Klaytn to be re-used by other applications: from wallets to decentralized exchanges.

## Specification
&lt;!--The technical specification should describe the syntax and semantics of any new feature. The specification should be detailed enough to allow competing, interoperable implementations for any of the current Klaytn platforms (klaytn). --&gt;
This document is heavily derived from [ERC-20](https://eips.ethereum.org/EIPS/eip-20) written by Fabian Vogelsteller and Vitalik Buterin.

The key words &quot;MUST&quot;, &quot;MUST NOT&quot;, &quot;REQUIRED&quot;, &quot;SHALL&quot;, &quot;SHALL NOT&quot;, &quot;SHOULD&quot;, &quot;SHOULD NOT&quot;, &quot;RECOMMENDED&quot;, &quot;MAY&quot;, and &quot;OPTIONAL&quot; in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt).

### Differences with ERC-20
This section describes the differences between KIP-7 and ERC-20. 
- More optional functions are included (e.g. mint, burn and pause extensions).
- Every token transfer/mint/burn MUST be tracked by event logs. This means that a Transfer event MUST be emitted for any action related to transfer/mint/burn.
- The [KIP-13](https://kips.klaytn.com/KIPs/kip-13) interface for each method group MUST be implemented.

### KIP-13 Identifiers
The below table shows KIP-13 identifiers for interfaces defined in this proposal.

|Interface|KIP-13 Identifier|
|---|---|
|[IKIP7](#kip7-interface)|0x65787371|
|[IKIP7TokenReceiver](#wallet-interface)|0x9d188c22|
|[IKIP7Metadata](#metadata-extension)|0xa219a025|
|[IKIP7Mintable](#minting-extension)|0xeab83e20|
|[IKIP7Burnable](#burning-extension)|0x3b5a0bf8|
|[IKIP7Pausable](#pausing-extension)|0x4d5507ff|

### KIP7 Interface
```solidity
/// @title KIP-7 Fungible Token Standard
///  Note: the KIP-13 identifier for this interface is 0x65787371.
interface IKIP7 {
    /// @dev Emitted when `value` tokens are moved from one account (`from`) to
    /// another (`to`) and created (`from` == 0) and destroyed(`to` == 0).
    ///
    /// Note that `value` may be zero.
    event Transfer(address indexed from, address indexed to, uint256 value);

    /// @dev Emitted when the allowance of a `spender` for an `owner` is set by
    /// a call to {approve}. `value` is the new allowance.
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /// @notice Returns the amount of tokens in existence.
    /// @return the total supply of this token.
    function totalSupply() external view returns (uint256);

    /// @notice Returns the amount of tokens owned by `account`.
    /// @param account An address for whom to query the balance
    /// @return the amount of tokens owned by `account.
    function balanceOf(address account) external view returns (uint256);

    /// @notice Moves `amount` tokens from the caller&apos;s account to `recipient`.
    /// @dev Throws if the message caller&apos;s balance does not have enough tokens to spend.
    /// Throws if the contract is pausable and paused.	
    ///
    /// Emits a {Transfer} event.
    /// @param recipient The owner will receive the tokens.
    /// @param amount The token amount will be transferred.
    /// @return A boolean value indicating whether the operation succeeded.
    function transfer(address recipient, uint256 amount) external returns (bool);

    /// @notice Returns the remaining number of tokens that `spender` will be
    /// allowed to spend on behalf of `owner` through {transferFrom}. This is
    /// zero by default.
    /// @dev Throws if the contract is pausable and paused.	
    ///
    /// This value changes when {approve} or {transferFrom} are called.
    /// @param owner The account allowed `spender` to withdraw the tokens from the account.
    /// @param spender The address is approved to withdraw the tokens.
    /// @return An amount of spender&apos;s token approved by owner.
    function allowance(address owner, address spender) external view returns (uint256);

    /// @notice Sets `amount` as the allowance of `spender` over the caller&apos;s tokens.
    /// @dev Throws if the contract is pausable and paused.
    ///
    /// IMPORTANT: Beware that changing an allowance with this method brings the risk
    /// that someone may use both the old and the new allowance by unfortunate
    /// transaction ordering. One possible solution to mitigate this race
    /// condition is to first reduce the spender&apos;s allowance to 0 and set the
    /// desired value afterwards:
    /// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
    ///
    /// Emits an {Approval} event.
    /// @param spender The address is approved to withdraw the tokens.
    /// @param amount The token amount will be approved.
    /// @return a boolean value indicating whether the operation succeeded.
    function approve(address spender, uint256 amount) external returns (bool);

    /// @notice Moves `amount` tokens from `sender` to `recipient` using the
    /// allowance mechanism. `amount` is then deducted from the caller&apos;s
    /// allowance.
    /// @dev Throw unless the `sender` account has deliberately authorized the sender of the message via some mechanism.
    /// Throw if `sender` or `recipient` is the zero address.
    /// Throws if the contract is pausable and paused.	
    ///
    /// Emits a {Transfer} event.
    /// Emits an `Approval` event indicating the updated allowance.
    /// @param sender The current owner of the tokens.
    /// @param recipient The owner will receive the tokens.
    /// @param amount The token amount will be transferred.
    /// @return A boolean value indicating whether the operation succeeded.
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
    
    /// @notice Moves `amount` tokens from the caller&apos;s account to `recipient`.
    /// @dev Throws if the message caller&apos;s balance does not have enough tokens to spend.
    /// Throws if the contract is pausable and paused.	
    /// Throws if `_to` is the zero address. 
    /// When transfer is complete, this function checks if `_to` is a smart 
    /// contract (code size &gt; 0). If so, it calls
    ///  `onKIP7Received` on `_to` and throws if the return value is not
    ///  `bytes4(keccak256(&quot;onKIP7Received(address,address,uint256,bytes)&quot;))`.
    /// @param recipient The owner will receive the tokens.
    /// @param amount The token amount will be transferred.
    /// @param data Additional data with no specified format, sent in call to `_to`
    function safeTransfer(address recipient, uint256 amount, bytes data) external;
    
    
    /// @notice Moves `amount` tokens from the caller&apos;s account to `recipient`.
    /// @dev This works identically to the other function with an extra data parameter,
    ///  except this function just sets data to &quot;&quot;.
    /// @param recipient The owner will receive the tokens.
    /// @param amount The token amount will be transferred.
    function safeTransfer(address recipient, uint256 amount) external;
    
    /// @notice Moves `amount` tokens from `sender` to `recipient` using the
    /// allowance mechanism. `amount` is then deducted from the caller&apos;s
    /// allowance.
    /// @dev Throw unless the `sender` account has deliberately authorized the sender of the message via some mechanism.
    /// Throw if `sender` or `recipient` is the zero address.
    /// Throws if the contract is pausable and paused.
    /// When transfer is complete, this function checks if `_to` is a smart 
    /// contract (code size &gt; 0). If so, it calls
    ///  `onKIP7Received` on `_to` and throws if the return value is not
    ///  `bytes4(keccak256(&quot;onKIP7Received(address,address,uint256,bytes)&quot;))`.
    /// Emits a {Transfer} event.
    /// Emits an `Approval` event indicating the updated allowance.
    /// @param sender The current owner of the tokens.
    /// @param recipient The owner will receive the tokens.
    /// @param amount The token amount will be transferred.
    /// @param data Additional data with no specified format, sent in call to `_to`
    function safeTransferFrom(address sender, address recipient, uint256 amount, bytes data) external;

    /// @notice Moves `amount` tokens from `sender` to `recipient` using the
    /// allowance mechanism. `amount` is then deducted from the caller&apos;s
    /// allowance.
    /// @dev This works identically to the other function with an extra data parameter,
    ///  except this function just sets data to &quot;&quot;.
    /// @param sender The current owner of the tokens.
    /// @param recipient The owner will receive the tokens.
    /// @param amount The token amount will be transferred.
    function safeTransferFrom(address sender, address recipient, uint256 amount) external;
}
```

### Wallet Interface
A wallet/broker/auction application MUST implement the **wallet interface** if it will accept safe transfers.

```solidity
/// @title KIP-7 Fungible Token Standard, optional wallet interface
/// @dev Note: the KIP-13 identifier for this interface is 0x9d188c22.
interface IKIP7TokenReceiver {
    /// @notice Handle the receipt of KIP-7 token
    /// @dev The KIP-7 smart contract calls this function on the recipient
    ///  after a `safeTransfer`. This function MAY throw to revert and reject the
    ///  transfer. Return of other than the magic value MUST result in the
    ///  transaction being reverted.
    ///  Note: the contract address is always the message sender.
    /// @param _operator The address which called `safeTransferFrom` function
    /// @param _from The address which previously owned the token
    /// @param _amount The token amount which is being transferred.
    /// @param _data Additional data with no specified format
    /// @return `bytes4(keccak256(&quot;onKIP7Received(address,address,uint256,bytes)&quot;))`
    ///  unless throwing
    function onKIP7Received(address _operator, address _from, uint256 _amount, bytes _data) external returns(bytes4);
}
```

### Metadata Extension
 
The **metadata extension** is OPTIONAL for KIP-7 smart contracts. 
This allows your smart contract to be interrogated for its name and for details about the assets which your token represent.

```solidity
/// @title KIP-7 Fungible Token Standard, optional metadata extension
///  Note: the KIP-13 identifier for this interface is 0xa219a025.
interface IKIP7Metadata {
    /// @notice Returns the name of the token.
    function name() external view returns (string memory);

    /// @notice Returns the symbol of the token, usually a shorter version of the
    /// name.
    function symbol() external view returns (string memory);

    /// @notice Returns the number of decimals used to get its user representation.
    /// For example, if `decimals` equals `2`, a balance of `505` tokens should
    /// be displayed to a user as `5,05` (`505 / 10 ** 2`).
    ///  Tokens usually opt for a value of 18, imitating the relationship between
    /// KLAY and Peb.
    /// NOTE: This information is only used for _display_ purposes: it in
    /// no way affects any of the arithmetic of the contract, including
    /// `IKIP7.balanceOf` and `IKIP7.transfer`.
    /// @return The number of decimals of this token.
    function decimals() external view returns (uint8);
}
```

### Minting Extension

The **minting extension** is OPTIONAL for KIP-7 smart contracts. This allows your contract to mint tokens.

```solidity
/// @title KIP-7 Fungible Token Standard, optional minting extension
///  Note: the KIP-13 identifier for this interface is 0xeab83e20.
interface IKIP7Mintable {
    /// @notice Creates `amount` tokens and assigns them to `account`, 
    /// increasing the total supply.
    /// @dev Throws if `msg.sender` is not allowed to mint
    /// @param _to The account that will receive the minted token
    /// @param _amount The token amount to mint
    /// @return True if the minting operation is successful, false otherwise
    function mint(address _to, uint256 _amount) external returns (bool);

    /// @notice Check the minting permission
    /// @param _account The account to check the minting permission
    /// @return True if the account has the minting permission, false otherwise
    function isMinter(address _account) external view returns (bool);

    /// @notice Give the minting permission to `_account`
    /// @dev Throws if `msg.sender` is not allowed to mint
    /// @param _account The account to be given the minting permission
    function addMinter(address _account) external;

    /// @notice Renounce the minter permission of `msg.sender`
    /// @dev Throws if `msg.sender` is not allowed to mint
    function renounceMinter() external;
}
```

### Burning Extension

The **burning extension** is OPTIONAL for KIP-7 smart contracts. This allows your contract to burn tokens.

```solidity
/// @title KIP-7 Fungible Token Standard, optional burning extension
///  Note: the KIP-13 identifier for this interface is 0x3b5a0bf8.
interface IKIP7Burnable {
    /// @notice Destroy the specified token
    /// @dev Throws if the message caller&apos;s balance does not have enough 
    /// tokens to burn. 
    /// @param _amount The token amount to be burned
    function burn(uint256 _amount) external;

    /// @notice Destroy the specified token from `sender` using allowance 
    /// mechanism. `_amount` is then deducted from the caller&apos;s allowance.
    /// @dev Throws if the message caller is `0x0.
    ///   Throws unless the `_account` account has deliberately authorized 
    /// the sender of the message via allowance mechanism.
    /// @param _account The account will be deducted is the The token amount to be burned 
    /// @param _amount The token amount to be burned
    function burnFrom(address _account, uint256 _amount) external;
}
```

### Pausing Extension

The **pausing extension** is OPTIONAL for KIP-7 smart contracts. This allows your contract to be suspended from transferring.

```solidity
/// @title KIP-7 Fungible Token Standard, optional pausing extension
///  Note: the KIP-13 identifier for this interface is 0x4d5507ff.
interface IKIP7Pausable {
    /// @dev This emits when the contract is paused
    event Paused(address _account);

    /// @dev This emits when the contract is unpaused
    event Unpaused(address _account);

    /// @notice Check whether the contract is paused
    /// @return True if the contract is paused, false otherwise    
    function paused() external view returns (bool);

    /// @notice Pause actions related to transfer and approve
    /// @dev Throws if `msg.sender` is not allowed to pause.
    ///   Throws if the contract is paused. 
    function pause() external;

    /// @notice Resume from the paused state of the contract
    /// @dev Throws if `msg.sender` is not allowed to unpause.
    ///   Throws if the contract is not paused. 
    function unpause() external;

    /// @notice Check the pausing permission
    /// @param _account The account to check the pausing permission
    /// @return True if the account has pausing permission, false otherwise
    function isPauser(address _account) external view returns (bool);

    /// @notice Give pausing permission to `_account`
    /// @dev Throws if `msg.sender` is not allowed to pause
    /// @param _account The account to be given the pausing permission
    function addPauser(address _account) external;

    /// @notice Renounce the pausing permission of `msg.sender`    
    /// @dev Throws if `msg.sender` is not allowed to pause
    function renouncePauser() external;
}
```

## Rationale
&lt;!--The rationale fleshes out the specification by describing what motivated the design and why particular design decisions were made. It should describe alternate designs that were considered and related work, e.g. how the feature is supported in other languages. The rationale may also provide evidence of consensus within the community, and should discuss important objections or concerns raised during discussion.--&gt;
This token standard is ERC-20 compliant. If you want to use ERC-20 for Klaytn, you can use this token standard.

## Backwards Compatibility
&lt;!-- All KIPs that introduce backwards incompatibilities must include a section describing these incompatibilities and their severity. The KIP must explain how the author proposes to deal with these incompatibilities. KIP submissions without a sufficient backwards compatibility treatise may be rejected outright. The authors should answer the question: &quot;Does this KIP require a hard fork?&quot; --&gt;
There is no compatibility problem.

## Test Cases
&lt;!--Test cases for an implementation are mandatory for KIPs that are affecting consensus changes. Other KIPs can choose to include links to test cases if applicable.--&gt;
Not available.

## Implementation
&lt;!--The implementations must be completed before any KIP is given status &quot;Final&quot;, but it need not be completed before the KIP is accepted. While there is merit to the approach of reaching consensus on the specification and rationale before writing code, the principle of &quot;rough consensus and running code&quot; is still useful when it comes to resolving many discussions of API details.--&gt;
The implementation is not necessary for token standards. The implementation of this will be attached in this section later.

## Copyright
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).

## References
https://eips.ethereum.org/EIPS/eip-20
</description>
        <pubDate>Thu, 20 Feb 2020 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-7</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-7</guid>
      </item>
    
      <item>
        <title>Interface Query Standard</title>
        <category>Core</category>
        
          <comments>https://github.com/klaytn/kips/issues/14</comments>
        
        <description>&lt;!--You can leave these HTML comments in your merged KIP and delete the visible duplicate text guides, they will not appear and may be helpful to refer to if you edit it again. This is the suggested template for new KIPs. Note that a KIP number will be assigned by an editor. When opening a pull request to submit your KIP, please use an abbreviated title in the filename, `kip-draft_title_abbrev.md`. The title should be 44 characters or less.--&gt;

## Simple Summary
&lt;!--&quot;If you can&apos;t explain it simply, you don&apos;t understand it well enough.&quot; Provide a simplified and layman-accessible explanation of the KIP.--&gt;
This KIP defines a method to query whether a contract implements a certain interface or not.

## Abstract
&lt;!--A short (~200 word) description of the technical issue being addressed.--&gt;
This proposal defines:
1. [How interface identifiers are defined](#how-interface-identifiers-are-defined)
2. [How a contract publishes the interfaces it implements](#how-a-contract-publishes-the-interfaces-it-implements)
3. [How to query if a contract implements KIP-13](#how-to-query-if-a-contract-implements-kip-13)
4. [How to query if a contract implements any given interface](#how-to-query-if-a-contract-implements-any-given-interface)

## Motivation
&lt;!--The motivation is critical for KIPs that want to change the Klaytn protocol. It should clearly explain why the existing protocol specification is inadequate to address the problem that the KIP solves. KIP submissions without sufficient motivation may be rejected outright.--&gt;
Since there is no clear way to find what functions are implemented in a contract, this KIP proposes a standard method to define and query interfaces in a contract.
For example, if we define an interface identifier of [KIP-7](https://kips.klaytn.com/KIPs/kip-7), we can easily determine a contract implements [KIP-7](https://kips.klaytn.com/KIPs/kip-7) or not.

## Specification
This document derived heavily from Ethereum&apos;s [ERC-165](https://eips.ethereum.org/EIPS/eip-165) written by Christian Reitwießner, Nick Johnson, Fabian Vogelsteller, Jordi Baylina, Konrad Feldmeier, and William Entriken.

### How Interface Identifiers are Defined

An *interface identifier* is a combination of function selectors of a contract.
A function selector is four bytes of keccak256 hash of the signature of a function
(e.g., `bytes4(keccak256(&apos;supportsInterface(bytes4)&apos;))`).
The signature is defined as the canonical expression of the basic prototype without parameter names and the return type.

We define the interface identifier as the XOR of all function selectors in the interface. This code example below shows how to calculate an interface identifier:

```solidity
pragma solidity ^0.4.24;

interface Solidity101 {
    function hello() external pure;
    function world(int) external pure;
}

contract Selector {
    function calculateSelector() public pure returns (bytes4) {
        Solidity101 i;
        return i.hello.selector ^ i.world.selector;
    }
}
```

Note: interfaces do not permit optional functions, therefore, the interface identifier will not include them.

### How a Contract Publishes the Interfaces it Implements

A contract that is compliant with KIP-13 shall implement the following interface (referred as `InterfaceIdentifier.sol`):

```solidity
pragma solidity ^0.4.24;

interface InterfaceIdentifier {
    /// @notice Query if a contract implements an interface
    /// @param interfaceID The interface identifier, as defined in KIP-13.
    /// @dev Interface identifier is defined in KIP-13. This function
    ///  uses less than 30,000 gas.
    /// @return `true` if the contract implements `interfaceID` and
    ///  `interfaceID` is not 0xffffffff, `false` otherwise.
    function supportsInterface(bytes4 interfaceID) external view returns (bool);
}
```

The interface identifier for this interface is `0x01ffc9a7`.
You can calculate this by running `bytes4(keccak256(&apos;supportsInterface(bytes4)&apos;));` or using the `Selector` contract above.

The implementing contract will have a `supportsInterface` function, and it returns:

- `true` when `interfaceID` is `0x01ffc9a7` (`supportsInterface` itself)
- `false` when `interfaceID` is `0xffffffff`
- `true` for `interfaceID` this contract implements
- `false` for any other `interfaceID`

This function must return a bool and use at most 30,000 gas.

Implementation note: there are several logical ways to implement this function. Please see the example implementations and the discussion on gas usage.

### How to Query if a Contract Implements KIP-13

1. The source contract makes a `STATICCALL` to the destination address with input data: `0x01ffc9a701ffc9a700000000000000000000000000000000000000000000000000000000` and gas 30,000. This corresponds to `contract.supportsInterface(0x01ffc9a7)`.
2. If the call fails or return false, the destination contract does not implement KIP-13.
3. If the call returns true, a second call is made with input data `0x01ffc9a7ffffffff00000000000000000000000000000000000000000000000000000000`.
  This corresponds to `contract.supportsInterface(0xffffffff)`.
4. If the second call fails or returns true, the destination contract does not implement KIP-13.
5. Otherwise it implements KIP-13.

### How to Query if a Contract Implements any Given Interface

1. If you are not sure if the contract implements KIP-13, use the above procedure to confirm.
2. If it does not implement KIP-13, then you will have to see what methods it uses in other way.
3. If it implements KIP-13 then just call `supportsInterface(interfaceID)` to determine if it implements an interface you can use.

## Rationale

We tried to keep this specification as simple as possible. This implementation is also compatible with the current Solidity version.

## Backwards Compatibility

The mechanism described above (with `0xffffffff`) should work with most of the contracts previous to this standard to determine that they do not implement KIP-13.

## Test Cases

Following is a contract that detects which interfaces other contracts implement. From @fulldecent and @jbaylina.

```solidity
pragma solidity ^0.4.24;

contract supportsInterfaceQuery {
    bytes4 constant InvalidID = 0xffffffff;
    bytes4 constant supportsInterfaceID = 0x01ffc9a7;

    function doesContractImplementInterface(address _contract, bytes4 _interfaceId) external view returns (bool) {
        uint256 success;
        uint256 result;

        (success, result) = noThrowCall(_contract, supportsInterfaceID);
        if ((success==0)||(result==0)) {
            return false;
        }

        (success, result) = noThrowCall(_contract, InvalidID);
        if ((success==0)||(result!=0)) {
            return false;
        }

        (success, result) = noThrowCall(_contract, _interfaceId);
        if ((success==1)&amp;&amp;(result==1)) {
            return true;
        }
        return false;
    }

    function noThrowCall(address _contract, bytes4 _interfaceId) constant internal returns (uint256 success, uint256 result) {
        bytes4 id = supportsInterfaceID;

        assembly {
                let x := mload(0x40)               // Find empty storage location using &quot;free memory pointer&quot;
                mstore(x, id)                      // Place signature at beginning of empty storage
                mstore(add(x, 0x04), _interfaceId) // Place first argument directly next to signature

                success := staticcall(
                                    30000,         // 30k gas
                                    _contract,     // To addr
                                    x,             // Inputs are stored at location x
                                    0x24,          // Inputs are 36 bytes long
                                    x,             // Store output over input (saves space)
                                    0x20)          // Outputs are 32 bytes long

                result := mload(x)                 // Load the result
        }
    }
}
```

## Implementation

This approach uses a `view` function implementation of `InterfaceIdentifier`. The execution cost is 586 gas for any input. But contract initialization requires storing each interface (`SSTORE` is 20,000 gas). The `MappingImplementation` contract is generic and reusable.

```solidity
pragma solidity ^0.4.24;

import &quot;./InterfaceIdentifier.sol&quot;;

contract MappingImplementation is InterfaceIdentifier {
    /// @dev You must not set element 0xffffffff to true
    mapping(bytes4 =&gt; bool) internal supportedInterfaces;

    function MappingImplementation() internal {
        supportedInterfaces[this.supportsInterface.selector] = true;
    }

    function supportsInterface(bytes4 interfaceID) external view returns (bool) {
        return supportedInterfaces[interfaceID];
    }
}

interface Simpson {
    function is2D() external returns (bool);
    function skinColor() external returns (string);
}

contract Lisa is MappingImplementation, Simpson {
    function Lisa() public {
        supportedInterfaces[this.is2D.selector ^ this.skinColor.selector] = true;
    }

    function is2D() external returns (bool){}
    function skinColor() external returns (string){}
}
```

Following is a `pure` function implementation of `InterfaceIdentifier`. The worst-case execution cost is 236 gas, but increases linearly with a higher number of supported interfaces.

```solidity
pragma solidity ^0.4.24;

import &quot;./InterfaceIdentifier.sol&quot;;

interface Simpson {
    function is2D() external returns (bool);
    function skinColor() external returns (string);
}

contract Homer is InterfaceIdentifer, Simpson {
    function supportsInterface(bytes4 interfaceID) external view returns (bool) {
        return
          interfaceID == this.supportsInterface.selector || // InterfaceIdentifier
          interfaceID == this.is2D.selector
                         ^ this.skinColor.selector; // Simpson
    }

    function is2D() external returns (bool){}
    function skinColor() external returns (string){}
}
```

With three or more supported interfaces (including KIP-13 itself as a required supported interface), the mapping approach (in every case) costs less gas than the pure approach (at worst case).

## References
- https://eips.ethereum.org/EIPS/eip-165
- https://solidity.readthedocs.io/en/develop/abi-spec.html#function-selector

## Copyright
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
</description>
        <pubDate>Thu, 27 Feb 2020 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-13</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-13</guid>
      </item>
    
      <item>
        <title>Non-fungible Token Standard</title>
        <category>Tokenization</category>
        
          <comments>https://github.com/klaytn/kips/issues/17</comments>
        
        <description>## Simple Summary
A standard interface for non-fungible tokens (NFTs), also known as deeds.

## Abstract
The following standard allows for the implementation of a standard API for NFTs within smart contracts. This standard provides basic functionality to track and transfer NFTs.

We considered use cases of NFTs being owned and transacted by individuals as well as consignment to third party brokers/wallets/auctioneers (&quot;operators&quot;). NFTs can represent ownership over digital or physical assets. We considered a diverse universe of assets, and we know you will dream up many more:

- Physical property — houses, unique artwork
- Virtual collectibles — unique pictures of kittens, collectible cards
- &quot;Negative value&quot; assets — loans, burdens and other responsibilities

In general, all houses are distinct and no two kittens are alike. NFTs are *distinguishable* and you must track the ownership of each one separately.

## Motivation
A standard interface allows wallet/broker/auction applications to work with any NFT on Klaytn. We provide simple KIP-17 smart contracts as well as contracts that track an *arbitrarily large* number of NFTs. Additional applications are discussed below.

## Specification
This document is heavily derived from [ERC-721](https://eips.ethereum.org/EIPS/eip-721) written by William Entriken, Dieter Shirley, Jacob Evans, and Nastassia Sachs.

The key words &quot;MUST&quot;, &quot;MUST NOT&quot;, &quot;REQUIRED&quot;, &quot;SHALL&quot;, &quot;SHALL NOT&quot;, &quot;SHOULD&quot;, &quot;SHOULD NOT&quot;, &quot;RECOMMENDED&quot;, &quot;MAY&quot;, and &quot;OPTIONAL&quot; in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt).

### Differences from ERC-721
This section describes the differences between KIP-17 and ERC-721. 

- Every token transfer/mint/burn MUST be tracked by event logs. This means that a Transfer event MUST be emitted for any action related to transfer/mint/burn.
- KIP-17 also supports the wallet interface of ERC-721 (`IERC721TokenReceiver`) to be compliant with ERC-721.
- More optional extensions are defined (minting extension, minting with URI extension, burning extension, and pausing extension).

### KIP-13 Identifiers
The below table shows KIP-13 identifiers for interfaces defined in this proposal.

|Interface|KIP-13 Identifier|
|---|---|
|[IKIP17](#kip17-interface)|0x80ac58cd|
|[IKIP17TokenReceiver](#wallet-interface)|0x6745782b|
|[IERC721TokenReceiver](#wallet-interface)|0x150b7a02|
|[IKIP17Metadata](#metadata-extension)|0x5b5e139f|
|[IKIP17Enumerable](#enumeration-extension)|0x780e9d63|
|[IKIP17Mintable](#minting-extension)|0xeab83e20|
|[IKIP17MetadataMintable](#minting-with-uri-extension)|0xfac27f46|
|[IKIP17Burnable](#burning-extension)|0x42966c68|
|[IKIP17Pausable](#pausing-extension)|0x4d5507ff|

### KIP17 Interface
```solidity
pragma solidity 0.4.24;

/// @title KIP-17 Non-Fungible Token Standard
///  Note: the KIP-13 identifier for this interface is 0x80ac58cd.
interface IKIP17 {
    /// @dev This emits when ownership of any NFT changes by any mechanism.
    ///  This event emits when NFTs are created (`from` == 0) and destroyed
    ///  (`to` == 0). At the time of any transfer, the approved address for 
    /// that NFT (if any) is reset to none.
    event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);

    /// @dev This emits when the approved address for an NFT is changed or
    ///  reaffirmed. The zero address indicates there is no approved address.
    ///  When a Transfer event emits, this also indicates that the approved
    ///  address for that NFT (if any) is reset to none.
    event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);

    /// @dev This emits when an operator is enabled or disabled for an owner.
    ///  The operator can manage all NFTs of the owner.
    event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);


    /// @notice Count all NFTs assigned to an owner
    /// @dev NFTs assigned to the zero address are considered invalid, and this
    ///  function throws for queries about the zero address.
    /// @param _owner An address for whom to query the balance
    /// @return The number of NFTs owned by `_owner`, possibly zero
    function balanceOf(address _owner) external view returns (uint256);

    /// @notice Find the owner of an NFT
    /// @dev NFTs assigned to zero address are considered invalid, and queries
    ///  about them do throw.
    /// @param _tokenId The identifier for an NFT
    /// @return The address of the owner of the NFT
    function ownerOf(uint256 _tokenId) external view returns (address);

    /// @notice Transfers the ownership of an NFT from one address to another address
    /// @dev Throws unless `msg.sender` is the current owner, an authorized
    ///  operator, or the approved address for this NFT. Throws if `_from` is
    ///  not the current owner. Throws if `_to` is the zero address. Throws if
    ///  `_tokenId` is not a valid NFT. When transfer is complete, this function
    ///  checks if `_to` is a smart contract (code size &gt; 0). If so, it calls
    ///  `onKIP17Received` on `_to` and throws if the return value is not
    ///  `bytes4(keccak256(&quot;onKIP17Received(address,address,uint256,bytes)&quot;))`.
    /// @param _from The current owner of the NFT
    /// @param _to The new owner
    /// @param _tokenId The NFT to transfer
    /// @param _data Additional data with no specified format, sent in call to `_to`
    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes _data) external payable;

    /// @notice Transfers the ownership of an NFT from one address to another address
    /// @dev This works identically to the other function with an extra data parameter,
    ///  except this function just sets data to &quot;&quot;.
    /// @param _from The current owner of the NFT
    /// @param _to The new owner
    /// @param _tokenId The NFT to transfer
    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;

    /// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
    ///  TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE
    ///  THEY MAY BE PERMANENTLY LOST
    /// @dev Throws unless `msg.sender` is the current owner, an authorized
    ///  operator, or the approved address for this NFT. Throws if `_from` is
    ///  not the current owner. Throws if `_to` is the zero address. Throws if
    ///  `_tokenId` is not a valid NFT.
    /// @param _from The current owner of the NFT
    /// @param _to The new owner
    /// @param _tokenId The NFT to transfer
    function transferFrom(address _from, address _to, uint256 _tokenId) external payable;

    /// @notice Change or reaffirm the approved address for an NFT
    /// @dev The zero address indicates there is no approved address.
    ///  Throws unless `msg.sender` is the current NFT owner, or an authorized
    ///  operator of the current owner.
    /// @param _approved The new approved NFT controller
    /// @param _tokenId The NFT to approve
    function approve(address _approved, uint256 _tokenId) external payable;

    /// @notice Enable or disable approval for a third party (&quot;operator&quot;) to manage
    ///  all of `msg.sender`&apos;s assets
    /// @dev Emits the ApprovalForAll event. The contract MUST allow
    ///  multiple operators per owner.
    /// @param _operator Address to add to the set of authorized operators
    /// @param _approved True if the operator is approved, false to revoke approval
    function setApprovalForAll(address _operator, bool _approved) external;

    /// @notice Get the approved address for a single NFT
    /// @dev Throws if `_tokenId` is not a valid NFT.
    /// @param _tokenId The NFT to find the approved address for
    /// @return The approved address for this NFT, or the zero address if there is none
    function getApproved(uint256 _tokenId) external view returns (address);

    /// @notice Query if an address is an authorized operator for another address
    /// @param _owner The address that owns the NFTs
    /// @param _operator The address that acts on behalf of the owner
    /// @return True if `_operator` is an approved operator for `_owner`, false otherwise
    function isApprovedForAll(address _owner, address _operator) external view returns (bool);
}
```

### Wallet Interface
A wallet/broker/auction application MUST implement the **wallet interface** if it will accept safe transfers.

```solidity
pragma solidity 0.4.24;

/// @title KIP-17 Non-Fungible Token Standard, optional wallet interface
/// @dev Note: the KIP-13 identifier for this interface is 0x6745782b.
interface IKIP17TokenReceiver {
    /// @notice Handle the receipt of an NFT
    /// @dev The KIP-17 smart contract calls this function on the recipient
    ///  after a `safeTransfer`. This function MAY throw to revert and reject the
    ///  transfer. Return of other than the magic value MUST result in the
    ///  transaction being reverted.
    ///  Note: the contract address is always the message sender.
    /// @param _operator The address which called `safeTransferFrom` function
    /// @param _from The address which previously owned the token
    /// @param _tokenId The NFT identifier which is being transferred
    /// @param _data Additional data with no specified format
    /// @return `bytes4(keccak256(&quot;onKIP17Received(address,address,uint256,bytes)&quot;))`
    ///  unless throwing
    function onKIP17Received(address _operator, address _from, uint256 _tokenId, bytes _data) external returns(bytes4);
}
```

To be compliant with ERC-721, KIP-17 also supports IERC721TokenReceiver. This makes the current ERC-721 implementation on Ethereum
can be easily migrated on to Klaytn without any modification.
```solidity
pragma solidity 0.4.24;

/// @title KIP-17 Non-Fungible Token Standard, optional ERC-721 wallet interface
/// @dev Note: the KIP-13 identifier for this interface is 0x150b7a02.
interface IERC721TokenReceiver {
    /// @notice Handle the receipt of an NFT
    /// @dev The ERC721 smart contract calls this function on the recipient
    ///  after a `safeTransfer`. This function MAY throw to revert and reject the
    ///  transfer. Return of other than the magic value MUST result in the
    ///  transaction being reverted.
    ///  Note: the contract address is always the message sender.
    /// @param _operator The address which called `safeTransferFrom` function
    /// @param _from The address which previously owned the token
    /// @param _tokenId The NFT identifier which is being transferred
    /// @param _data Additional data with no specified format
    /// @return `bytes4(keccak256(&quot;onERC721Received(address,address,uint256,bytes)&quot;))`
    ///  unless throwing
    function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes _data) external returns(bytes4);
}
```

### Metadata Extension
The **metadata extension** is OPTIONAL for KIP-17 smart contracts (see &quot;caveats&quot;, below). This allows your smart contract to be interrogated for its name and for details about the assets which your NFTs represent.

Note: The function `tokenURI` is useful only when `IKIP17MetadataMintable` is implemented.

```solidity
pragma solidity 0.4.24;

/// @title KIP-17 Non-Fungible Token Standard, optional metadata extension.
///  Note: the KIP-13 identifier for this interface is 0x5b5e139f.
interface IKIP17Metadata {
    /// @notice A descriptive name for a collection of NFTs in this contract
    function name() external view returns (string _name);

    /// @notice An abbreviated name for NFTs in this contract
    function symbol() external view returns (string _symbol);

    /// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
    /// @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC
    ///  3986. The URI may point to a JSON file that conforms to the &quot;KIP17
    ///  Metadata JSON Schema&quot;.
    function tokenURI(uint256 _tokenId) external view returns (string);
}
```

This is the &quot;KIP17 Metadata JSON Schema&quot; referenced above.

```json
{
    &quot;title&quot;: &quot;Asset Metadata&quot;,
    &quot;type&quot;: &quot;object&quot;,
    &quot;properties&quot;: {
        &quot;name&quot;: {
            &quot;type&quot;: &quot;string&quot;,
            &quot;description&quot;: &quot;Identifies the asset to which this NFT represents&quot;
        },
        &quot;description&quot;: {
            &quot;type&quot;: &quot;string&quot;,
            &quot;description&quot;: &quot;Describes the asset to which this NFT represents&quot;
        },
        &quot;image&quot;: {
            &quot;type&quot;: &quot;string&quot;,
            &quot;description&quot;: &quot;A URI pointing to a resource with mime type image/* representing the asset to which this NFT represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive.&quot;
        }
    }
}
```

### Enumeration Extension
The **enumeration extension** is OPTIONAL for KIP-17 smart contracts (see &quot;caveats&quot;, below). This allows your contract to publish its full list of NFTs and make them discoverable.

```solidity
pragma solidity 0.4.24;

/// @title KIP-17 Non-Fungible Token Standard, optional enumeration extension
///  Note: the KIP-13 identifier for this interface is 0x780e9d63.
interface IKIP17Enumerable {
    /// @notice Count NFTs tracked by this contract
    /// @return A count of valid NFTs tracked by this contract, where each one of
    ///  them has an assigned and queryable owner not equal to the zero address
    function totalSupply() external view returns (uint256);

    /// @notice Enumerate valid NFTs
    /// @dev Throws if `_index` &gt;= `totalSupply()`.
    /// @param _index A counter less than `totalSupply()`
    /// @return The token identifier for the `_index`th NFT,
    ///  (sort order not specified)
    function tokenByIndex(uint256 _index) external view returns (uint256);

    /// @notice Enumerate NFTs assigned to an owner
    /// @dev Throws if `_index` &gt;= `balanceOf(_owner)` or if
    ///  `_owner` is the zero address, representing invalid NFTs.
    /// @param _owner An address where we are interested in NFTs owned by them
    /// @param _index A counter less than `balanceOf(_owner)`
    /// @return The token identifier for the `_index`th NFT assigned to `_owner`,
    ///   (sort order not specified)
    function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);
}
```

### Minting Extension
The **minting extension** is OPTIONAL for KIP-17 smart contracts. This allows your contract to mint a new token.

Note: `IKIP17MetadataMintable` is mutually exclusive with `IKIP17Mintable`. If you want to use tokenURI, use `IKIP17MetadataMintable`.

```solidity
pragma solidity 0.4.24;

/// @title KIP-17 Non-Fungible Token Standard, optional minting extension
///  Note: the KIP-13 identifier for this interface is 0xeab83e20.
interface IKIP17Mintable {
    /// @notice Create a new token
    /// @dev Throws if `msg.sender` is not allowed to mint
    /// @param _to The account that will receive the minted token
    /// @param _tokenId The token ID to mint
    /// @return True if the minting operation is successful, false otherwise
    function mint(address _to, uint256 _tokenId) public returns (bool);

    /// @notice Check the minting permission
    /// @param _account The account to check the minting permission
    /// @return True if the account has the minting permission, false otherwise
    function isMinter(address _account) public view returns (bool);

    /// @notice Give the minting permission to `_account`
    /// @dev Throws if `msg.sender` is not allowed to mint
    /// @param _account The account to be given the minting permission
    function addMinter(address _account) public;

    /// @notice Renounce the minter permission of `msg.sender`
    /// @dev Throws if `msg.sender` is not allowed to mint
    function renounceMinter() public;
}
```

### Minting with URI Extension
The **minting with URI extension** is OPTIONAL for KIP-17 smart contracts. This allows your contract to mint a new token with URI.

Note: `IKIP17MetadataMintable` is mutually exclusive with `IKIP17Mintable`. If you want to use tokenURI, use `IKIP17MetadataMintable`.
To query tokenURIs, it is required to implement `IKIP17MetadataMintable` with `IKIP17Metadata`.

```solidity
pragma solidity 0.4.24;

/// @title KIP-17 Non-Fungible Token Standard, optional minting with URI extension
///  Note: the KIP-13 identifier for this interface is 0xfac27f46.
interface IKIP17MetadataMintable {
    /// @notice Create a new token with the specified URI
    /// @dev Throws if `msg.sender` is not allowed to mint
    /// @param _to The account that will receive the minted token
    /// @param _tokenId The token ID to mint
    /// @param _tokenURI the token URI of the newly minted token
    /// @return True if the minting operation is succeeded, false otherwise
    function mintWithTokenURI(address _to, uint256 _tokenId, string memory _tokenURI) public returns (bool);

    /// @notice Check the minting permission
    /// @param _account The account to check the minting permission
    /// @return True if the account has the minting permission, false otherwise
    function isMinter(address _account) public view returns (bool);

    /// @notice Give the minting permission to `_account`
    /// @dev Throws if `msg.sender` is not allowed to mint
    /// @param _account The account to be given the minting permission
    function addMinter(address _account) public;

    /// @notice Renounce the minter permission of `msg.sender`
    /// @dev Throws if `msg.sender` is not allowed to mint
    function renounceMinter() public;
}
```

### Burning Extension
The **burning extension** is OPTIONAL for KIP-17 smart contracts. This allows your contract to burn a token.

```solidity
pragma solidity 0.4.24;

/// @title KIP-17 Non-Fungible Token Standard, optional burning extension
///  Note: KIP-13 identifier for this interface is 0x42966c68.
interface IKIP17Burnable {
    /// @notice Destroy the specified token
    /// @dev Throws unless `msg.sender` is the current owner, an authorized
    ///  operator, or the approved address for this NFT. Throws  `_tokenId`
    ///  is not a valid NFT. 
    /// @param _tokenId The token ID to be burned
    function burn(uint256 _tokenId) public;
}
```


### Pausing Extension
The **pausing extension** is OPTIONAL for KIP-17 smart contracts. This allows your contract to be suspended from transferring.

```solidity
pragma solidity 0.4.24;

/// @title KIP-17 Non-Fungible Token Standard, optional pausing extension
///  Note: KIP-13 identifier for this interface is 0x4d5507ff.
interface IKIP17Pausable {
    /// @dev This emits when the contract is paused
    event Paused(address _account);

    /// @dev This emits when the contract is unpaused
    event Unpaused(address _account);

    /// @notice Check whether the contract is paused
    /// @return True if the contract is paused, false otherwise    
    function paused() public view returns (bool);

    /// @notice Pause actions related to transfer and approve
    /// @dev Throws if `msg.sender` is not allowed to pause.
    ///   Throws if the contract is paused. 
    function pause() public;

    /// @notice Resume from the paused state of the contract
    /// @dev Throws if `msg.sender` is not allowed to unpause.
    ///   Throws if the contract is not paused. 
    function unpause() public;

    /// @notice Check the pausing permission
    /// @param _account The account to check the pausing permission
    /// @return True if the account has pausing permission, false otherwise
    function isPauser(address _account) public view returns (bool);

    /// @notice Give pausing permission to `_account`
    /// @dev Throws if `msg.sender` is not allowed to pause
    /// @param _account The account to be given the pausing permission
    function addPauser(address _account) public;

    /// @notice Renounce the pausing permission of `msg.sender`    
    /// @dev Throws if `msg.sender` is not allowed to pause
    function renouncePauser() public;
}
```

### Caveats

The 0.4.20 Solidity interface grammar is not expressive enough to document the KIP-17 standard. A contract which complies with KIP-17 MUST also abide by the following:

- Solidity issue #3412: The above interfaces include explicit mutability guarantees for each function. Mutability guarantees are, in order weak to strong: `payable`, implicit nonpayable, `view`, and `pure`. Your implementation MUST meet the mutability guarantee in this interface and you MAY meet a stronger guarantee. For example, a `payable` function in this interface may be implemented as nonpayble (no state mutability specified) in your contract. We expect a later Solidity release will allow your stricter contract to inherit from this interface, but a workaround for version 0.4.20 is that you can edit this interface to add stricter mutability before inheriting from your contract.
- Solidity issue #3419: A contract that implements `IKIP17Metadata` or `IKIP17Enumerable` SHALL also implement `IKIP17`. KIP-17 implements the requirements of interface KIP-13.
- Solidity issue #2330: If a function is shown in this specification as `external` then a contract will be compliant if it uses `public` visibility. As a workaround for version 0.4.20, you can edit this interface to switch to `public` before inheriting from your contract.
- Solidity issues #3494, #3544: Use of `this.*.selector` is marked as a warning by Solidity, a future version of Solidity will not mark this as an error.

*If a newer version of Solidity allows the caveats to be expressed in code, then this KIP MAY be updated and the caveats removed, such will be equivalent to the original specification.*


## Rationale

There are many proposed uses of Klaytn smart contracts that depend on tracking distinguishable assets.
It is critical that these items are not &quot;lumped together&quot; as numbers in a ledger, but instead each asset must have its ownership individually and atomically tracked.
Regardless of the nature of these assets, the ecosystem will be stronger if we have a standardized interface that allows for cross-functional asset management and sales platforms.

**&quot;NFT&quot; Word Choice**

&quot;NFT&quot; was satisfactory to nearly everyone surveyed and is widely applicable to a broad universe of distinguishable digital assets. We recognize that &quot;deed&quot; is very descriptive for certain applications of this standard (notably, physical property).

*Alternatives considered: distinguishable asset, title, token, asset, equity, ticket*

**NFT Identifiers**

Every NFT is identified by a unique `uint256` ID inside the KIP-17 smart contract.
This identifying number SHALL NOT change for the life of the contract.
The pair `(contract address, uint256 tokenId)` will then be a globally unique and fully-qualified identifier for a specific asset on a Klaytn chain.
While some KIP-17 smart contracts may find it convenient to start with ID 0 and simply increment by one for each new NFT, callers SHALL NOT assume that ID numbers have any specific pattern to them, and MUST treat the ID as a &quot;black box&quot;.
Also note that a NFTs MAY become invalid (be destroyed). Please see the enumerations functions for a supported enumeration interface.

The choice of `uint256` allows a wide variety of applications because UUIDs and sha3 hashes are directly convertible to `uint256`.

**Transfer Mechanism**

KIP-17 standardizes a safe transfer function `safeTransferFrom` (overloaded with and without a `bytes` parameter) and an unsafe function `transferFrom`. Transfers may be initiated by:

- The owner of an NFT
- The approved account of an NFT
- An authorized operator of the current owner of an NFT

Additionally, an authorized operator may set the approved account for an NFT. This provides a powerful set of tools for wallet, broker and auction applications to quickly use a *large* number of NFTs.

The transfer and accept functions&apos; documentation only specify conditions when the transaction MUST throw. Your implementation MAY also throw in other situations. This allows implementations to achieve interesting results:

- **Disallow transfers if the contract is paused**
- **Blacklist certain address from receiving NFTs**
- **Disallow unsafe transfers** — `transferFrom` throws unless `_to` equals `msg.sender` or `countOf(_to)` is non-zero or was non-zero previously (because such cases are safe)
- **Charge a fee to both parties of a transaction** — require payment when calling `approve` with a non-zero `_approved` if it was previously the zero address, refund payment if calling `approve` with the zero address if it was previously a non-zero address, require payment when calling any transfer function, require transfer parameter `_to` to equal `msg.sender`, require transfer parameter `_to` to be the approved address for the NFT
- **Read only NFT registry** — always throw from `unsafeTransfer`, `transferFrom`, `approve` and `setApprovalForAll`

Failed transactions will throw. KIP-7 defined an `allowance` feature, this caused a problem when called and then later modified to a different amount
In KIP-17, there is no allowance because every NFT is unique, the quantity is none or one.
Therefore, we receive the benefits of KIP-7&apos;s original design without problems that have been later discovered.

Creating of NFTs (&quot;minting&quot;) and destruction NFTs (&quot;burning&quot;) is not included in the specification.
Your contract may implement these by other means.
Please see the `event` documentation for your responsibilities when creating or destroying NFTs.

We questioned if the `operator` parameter on `onKIP17Received` was necessary.
In all cases we could imagine, if the operator was important, the operator could transfer the token to themselves and then send it -- then they would be the `from` address.
This seems contrived because we consider the operator to be a temporary owner of the token (and transferring to themselves is redundant).
When the operator sends the token, it is the operator acting on their own accord, NOT the operator acting on behalf of the token holder.
This is why the operator and the previous token owner are both significant to the token recipient.

*Alternatives considered: only allow two-step KIP-7 style transaction, require that transfer functions never throw, require all functions to return a boolean indicating the success of the operation.*

**KIP-13 Interface**

We chose Interface Query Standard (KIP-13) to expose the interfaces that a KIP-17 smart contract supports.

A future KIP may create a global registry of interfaces for contracts.
We strongly support such a KIP and it would allow your KIP-17 implementation to implement `IKIP17Enumerable`, `IKIP17Metadata`, or other interfaces by delegating to a separate contract.

**Gas and Complexity** (regarding the enumeration extension)

This specification contemplates implementations that manage a few and *arbitrarily large* numbers of NFTs. If your application is able to grow then avoid using for/while loops in your code. These indicate your contract may be unable to scale and gas costs will rise over time without bound.

We have deployed a KIP-17 contract to Testnet which instantiates and tracks 340282366920938463463374607431768211456 different deeds (2^128).
That&apos;s enough to assign every IPV6 address to a Klaytn account owner, or to track ownership of nanobots a few micron in size and in aggregate totalling half the size of Earth.
You can query it from the blockchain.

This illustration makes clear: the KIP-17 standard scales.

*Alternatives considered: remove the asset enumeration function if it requires a for-loop, return a Solidity array type from enumeration functions.*

**Privacy**

Wallets/brokers/auctioneers identified in the motivation section have a strong need to identify which NFTs an owner owns.

It may be interesting to consider a use case where NFTs are not enumerable, such as a private registry of property ownership, or a partially-private registry.
However, privacy cannot be attained because an attacker can simply (!) call `ownerOf` for every possible `tokenId`.

**Metadata Choices** (metadata extension)

We have required `name` and `symbol` functions in the metadata extension.

We remind implementation authors that the empty string is a valid response to `name` and `symbol` if you protest to the usage of this mechanism.
We also remind everyone that any smart contract can use the same name and symbol as *your* contract.
How a client may determine which KIP-17 smart contracts are well-known (canonical) is outside the scope of this standard.

A mechanism is provided to associate NFTs with URIs.
We expect that many implementations will take advantage of this to provide metadata for each NFT.
The image size recommendation is taken from Instagram, they probably know much about image usability.
The URI MAY be mutable (i.e. it changes from time to time).
We considered an NFT representing ownership of a house, in this case metadata about the house (image, occupants, etc.) can naturally change.

Metadata is returned as a string value. Currently this is only usable as calling from SDKs (e.g., caver), not from other contracts. This is acceptable because we have not considered a use case where an on-blockchain application would query such information.

*Alternatives considered: put all metadata for each asset on the blockchain (too expensive), use URL templates to query metadata parts (URL templates do not work with all URL schemes, especially P2P URLs), multiaddr network address (not mature enough)*

## Backwards Compatibility
Not available.

## Test Cases
N/A

## Implementation
This section will be added later if implementation of this proposal appears.

## References
- [https://eips.ethereum.org/EIPS/eip-721](https://eips.ethereum.org/EIPS/eip-721)
- [https://kips.klaytn.com/KIPs/kip-7](https://kips.klaytn.com/KIPs/kip-7)
- [https://kips.klaytn.com/KIPs/kip-13](https://kips.klaytn.com/KIPs/kip-13)

## Copyright
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
</description>
        <pubDate>Wed, 04 Mar 2020 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-17</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-17</guid>
      </item>
    
      <item>
        <title>Klaytn SDK Common Architecture</title>
        <category>Ecosystem</category>
        
          <comments>https://github.com/klaytn/kips/issues/35</comments>
        
        <description># Table of contents

- [Simple Summary](#simple-summary)
- [Abstract](#abstract)
- [Motivation](#motivation)
- [Specification](#specification)
  - [Terminology](#terminology)
  - [Overview of the Common Architecture](#overview-of-the-common-architecture)
  - [Layer Diagram of the Common Architecture](#layer-diagram-of-the-common-architecture)
  - [Account Layer](#account-layer)
  - [Wallet Layer](#wallet-layer)
  - [Transaction Layer](#transaction-layer)
  - [RPC Layer](#rpc-layer)
  - [Contract, ABI, KCT Layer](#contract-abi-kct-layer)
  - [Utils Layer](#utils-layer)
- [Example Usage of the Common Architecture](#example-usage-of-the-common-architecture)
- [Rationale](#rationale)
- [Backward Compatibility](#backward-compatibility)
- [Test Cases](#test-cases)
- [Implementation](#implementation)
- [Copyright](#copyright)

## Simple Summary

A new software architecture for Klaytn development environment which is shared by all Klaytn SDKs (also known as &apos;caver&apos; series).

## Abstract

The following standard allows Klaytn&apos;s SDK, Caver, to implement a common architecture regardless of programming languages.

## Motivation

Klaytn SDKs (caver-js and caver-java) do not share common terminology and software architecture. It makes it difficult to implement blockchain applications using multiple programming languages. For example, if the server program is implemented in Java and the client is implemented in Javascript, the developer should learn two different SDKs that have different terminology and software architecture, even if those programs utilize a single blockchain platform, Klaytn.  
The common architecture is designed to unify the terminology used in the SDKs and easily extend to other languages by establishing a language-independent common architecture.  
With the common architecture, we want to achieve two goals:  
* Developers can easily implement their application using another language if they have implemented it in any Klaytn SDK before.
* An SDK in another programming language can be implemented relatively easily because the software architecture is commonly defined.

## Specification

### Terminology

| Term        | Description |
| ----------- | ----------- |
| Account | A data structure representing a Klaytn Account. It contains information like balance, nonce, public key(s), etc. Note that the public key(s) of the account can be changed in Klaytn because the public key(s) and the account address are decoupled. Please refer to [klaytn account] for more detail.  |
| Account key | A data structure representing various Klaytn account key types. Please refer to [accountkey] for more detail. |
| Keyring | A data structure containing Klaytn&apos;s account address and private key(s). It is used to sign a transaction. |
| Keyring container | A data structure containing multiple keyrings. |
| Wallet | A synonym of the keyring container. A wallet is a data structure containing multiple keyrings. |
| [Transaction](https://docs.klaytn.com/klaytn/design/transactions#transactions-overview) | A data structure sent between nodes that changes the state of the blockchain. Klaytn provides [multiple transaction types] that empower Klaytn with new capabilities and optimizations for memory footprint and performance. |
| Raw transaction | An RLP-encoded string of a transaction. |
| [JSON-RPC](https://www.jsonrpc.org/specification) | A stateless, light-weight remote procedure call (RPC) protocol. It uses JSON as a data format. |
| Contract | A computer program or a transaction protocol which is intended to automatically execute, control or document legally relevant events and actions according to the terms of a contract or an agreement. You can deploy a smart contract or execute a smart contract that has already been deployed to Klaytn through a transaction. |
| ABI | The Application Binary Interface(ABI) to communicate between two binary program modules. |
| [KCT (Klaytn Compatible Token)](http://kips.klaytn.com/token) | A special type of a smart contract that implements token specifications defined in [the KCT section of Klaytn Improvement Proposals](http://kips.klaytn.com/token). |

### Overview of the Common Architecture

This is the overview of the common architecture of Klaytn SDK.

![whole_20210426 (1)](https://user-images.githubusercontent.com/32922423/116016566-f5cb6a00-a677-11eb-9bd3-8d30450711c5.png)

### Layer Diagram of the Common Architecture

Below diagram shows the layers of the common architecture. Components belonging to each layer are represented inside the layer box.

![layerDiagram](https://user-images.githubusercontent.com/32922423/85986440-355d0180-ba27-11ea-8475-afe7638e6ffb.png)

The `KCT` layer allows you to interact with Klaytn compatible token (KCT) contracts (e.g., [KIP-7] or [KIP-17]). This is implemented by extending the `Contract` class in the `Contract` layer.

The `Contract` layer allows you to interact with smart contracts on the Klaytn.

The `ABI` layer provides the functions to encode and decode parameters based on ABI.

The `Transaction` layer contains classes of Klaytn transaction types, `TransactionDecoder`, and `TransactionHasher`. The `TransactionDecoder` class decodes the RLP-encoded transaction string. The `TransactionHasher` class calculates the hash of the transaction.

The `Wallet` layer contains keyring classes (`SingleKeyring`, `MultipleKeyring`, and `RoleBasedKeyring`) and `KeyringContainer` class that contains multiple keyrings. The `KeyringContainer` class acts as an &quot;in-memory wallet&quot; that stores the Keyring instances.

The `Account` layer contains an `Account` class that stores information needed when updating the [AccountKey] of the account in the Klaytn.

Also, the `RPC` layer includes the `Klay` class, which is responsible for rpc calls of the klay namespace, and the `Net` class, which is responsible for rpc calls of the net namespace.

Finally, the `Utils` layer contains the `Utils` class that provides utility functions.

In the next chapter, each layer is described in detail.

### Account Layer

The `Account` layer provides functionality related to updating the [AccountKey] of the Klaytn account.

![accountLayer](https://user-images.githubusercontent.com/32922423/106078110-2de50e80-6156-11eb-8030-04fdc0c34696.png)

`Account` is a class that represents a Klaytn account. It contains information needed to update the [AccountKey] of the account in the Klaytn. It has `address` and `accountKey` as member variables. The `accountKey` can be an instance of `AccountKeyLegacy`, `AccountKeyPublic`, `AccountKeyFail`, `AccountKeyWeightedMultiSig`, and `AccountKeyRoleBased` depending on the key.

An `Account` instance is injected into the `account` field of the AccountUpdate transaction types (`AccountUpdate`, `FeeDelegatedAccountUpdate`, and `FeeDelegatedAccountUpdateWithRatio`). When an AccountUpdate transaction is successfully processed in Klaytn, the account key of the [Klaytn account] is updated with the `accountKey` defined in the `Account` instance.

Interface for AccountKey is defined as `IAccountKey`, and account key classes (`AccountKeyLegacy`, `AccountKeyPublic`, `AccountKeyFail`, `AccountKeyWeightedMultiSig` and `AccountKeyRoleBased`) implement IAccountKey.

`WeightedMultiSigOptions` is for `AccountKeyWeightedMultiSig` where the threshold and the weight of each key are defined.

The `AccountKeyDecoder` class decodes the RLP-encoded string using the decode function implemented in each AccountKey class.

#### IAccountKey

`IAccountKey` is the interface of AccountKey. All AccountKey classes must implement `IAccountKey`.

##### Variables

None

##### Methods

| Method | Description |
| ----------- | ----------- |
| getRLPEncoding(): String | Returns an RLP-encoded string of AccountKey. AccountKey classes below implement `IAccountKey`, so this function must be implemented. |

#### AccountKeyLegacy

`AccountKeyLegacy` is a class representing [AccountKeyLegacy](https://docs.klaytn.com/klaytn/design/accounts#accountkeylegacy). It is used for the account having an address derived from the corresponding key pair.

##### Variables

None

##### Methods

| Method | Description |
| ----------- | ----------- |
| decode(rlpEncodedKey: String): AccountKeyLegacy | Decodes an RLP-encoded string of AccountKeyLegacy and returns an `AccountKeyLegacy` instance. It throws an exception if the decoding is failed. |
| getRLPEncoding(): String | Returns the RLP-encoded string of AccountKeyLegacy. |

#### AccountKeyPublic

`AccountKeyPublic` is a class representing [AccountKeyPublic](https://docs.klaytn.com/klaytn/design/accounts#accountkeypublic). It is used for accounts having one public key.

##### Variables

| Variable | Description |
| ----------- | ----------- |
| publicKey: String | A public key string. |

##### Methods

| Method | Description |
| ----------- | ----------- |
| decode(rlpEncodedKey: String): AccountKeyPublic | Decodes an RLP-encoded string of AccountKeyPublic and returns an `AccountKeyPublic` instance. It throws an exception if the decoding is failed. |
| fromXYPoint(x: String, y: String): AccountKeyPublic | Creates and returns an `AccountKeyPublic` instance from x and y points. It throws an exception if the x and y points are invalid. |
| fromPublicKey(publicKey: String): AccountKeyPublic | Creates and returns an `AccountKeyPublic` instance from a public key string. It throws an exception if the public key is invalid. |
| getXYPoint(): String[] | Returns the x and y points of the public key. |
| getRLPEncoding(): String | Returns the RLP-encoded string of AccountKeyPublic. |

#### AccountKeyFail

`AccountKeyFail` is a class representing [AccountKeyFail](https://docs.klaytn.com/klaytn/design/accounts#accountkeyfail). It is used when all transactions sent from the account are expected to be failed.

##### Variables

None

##### Methods

| Method | Description |
| ----------- | ----------- |
| decode(rlpEncodedKey: String): AccountKeyFail | Decodes an RLP-encoded string of AccountKeyFail and returns an `AccountKeyFail` instance. It throws an exception if the decoding is failed. |
| getRLPEncoding(): String | Returns the RLP-encoded string of AccountKeyFail. |

#### AccountKeyWeightedMultiSig

`AccountKeyWeightedMultiSig` is a class representing [AccountKeyWeightedMultiSig](https://docs.klaytn.com/klaytn/design/accounts#accountkeyweightedmultisig). It is an account key type containing a threshold and WeightedPublicKeys which contains a list whose item is composed of a public key and its weight.

##### Variables

| Variable | Description |
| ----------- | ----------- |
| threshold: int | A validation threshold. To be a valid transaction, the transaction must contain valid signatures equal to or more than the threshold. |
| weightedPublicKey: List&amp;#60;WeightedPublicKey&amp;#62; | A list of weighted public keys. |

##### Methods

| Method | Description |
| ----------- | ----------- |
| decode(rlpEncodedKey: String): AccountKeyWeightedMultiSig | Decodes an RLP-encoded string of AccountKeyWeightedMultiSig and returns an `AccountKeyWeightedMultiSig` instance. It throws an exception if the decoding is failed. |
| fromPublicKeysAndOptions(pubArray: String[], options: WeightedMultiSigOptions): AccountKeyWeightedMultiSig | Creates and returns an `AccountKeyWeightedMultiSig` instance with public key strings in `pubArray` and the options defining the threshold and the weight of each key in `WeightedMultiSigOptions`. It throws an exception if the options or public keys for accountKeyWeightedMultiSig are invalid. |
| getRLPEncoding(): String | Returns the RLP-encoded string of AccountKeyWeightedMultiSig. |

#### WeightedPublicKey

`WeightedPublicKey` is a class storing a public key with its weight. It is used for AccountKeyWeightedMultiSig.

##### Variables

| Variable | Description |
| ----------- | ----------- |
| publicKey: String | A public key string. |
| weight: int | A weight of the public key. |

##### Methods

None

#### WeightedKeyMultiSigOptions

`WeightedKeyMultiSigOptions` is a class that defines the threshold and the weight of each public key for AccountKeyWeightedMultiSig.

##### Variables

| Variable | Description |
| ----------- | ----------- |
| threshold: int | A validation threshold. |
| weighted: List&amp;#60;int&amp;#62; | A list of weights of public keys. |

##### Methods

None

#### AccountKeyRoleBased

`AccountKeyRoleBased` is a class representing [AccountKeyRoleBased](https://docs.klaytn.com/klaytn/design/accounts#accountkeyrolebased). It is used to set different key pairs to different roles in an account.

##### Variables

| Variable | Description |
| ----------- | ----------- |
| accountKeys: List&amp;#60;IAccountKey&amp;#62; | A list of keys to be used for each role. An item of the list represents a role. |

##### Methods

| Method | Description |
| ----------- | ----------- |
| decode(rlpEncodedKey: String): AccountKeyRoleBased | Decodes an RLP-encoded string of AccountKeyRoleBased and returns an `AccountKeyRoleBased` instance. It throws an exception if the decoding is failed. |
| fromRoledPublicKeysAndOptions(pubArray: List&amp;#60;String[]&amp;#62;, options: List&amp;#60;WeightedMultiSigOptions&amp;#62;): AccountKeyRoleBased | Creates and returns an instance of AccountKeyRoleBased with public key strings for each role and the option that defines the threshold and weight of each key. It throws an exception if the public key(s) for each role or options are invalid. |
| fromRoledPublicKeysAndOptions(pubArray: List&amp;#60;String[]&amp;#62;): AccountKeyRoleBased | Creates and returns an instance of AccountKeyRoleBased with public key strings for each role. This function assumes that all the values of the threshold and weights are set to be one. |
| getRLPEncoding(): String | Returns the RLP-encoded string of AccountKeyRoleBased. |

#### AccountKeyDecoder

`AccountKeyDecoder` provides the function to decode RLP-encoded accountKey strings.

##### Variables

None

##### Methods

| Method | Description |
| ----------- | ----------- |
| decode(rlpEncodedKey: String): IAccountKey | Decodes an RLP-encoded string of any class that inherits `IAccountKey` and returns the corresponding account key class. This function is useful when decoding an RLP-encoded string of any account key types. It throws an exception if the decoding is failed. |

#### Account

`Account` is a class that represents a Klaytn account. It contains information needed to update the AccountKey of the account in the Klaytn.

##### Variables

| Variable | Description |
| ----------- | ----------- |
| address: String | The address of an account to be updated. |
| accountKey: IAccountKey | The new accountKey to be used in the given account. This can be an instance of [AccountKeyLegacy](#accountkeylegacy), [AccountKeyPublic](#accountkeypublic), [AccountKeyFail](#accountkeyfail), [AccountKeyWeightedMultiSig](#accountkeyweightedmultisig) and [AccountKeyRoleBased](#accountkeyrolebased). |

##### Methods

| Method | Description |
| ----------- | ----------- |
| create(address: String, publicKey: String): Account | Generates an Account instance with an address and a public key string. |
| create(address: String, pubKeys: String[]): Account | Generates an Account instance with an address and public key strings. A threshold of 1 and a weight of 1 for each key will be used as a default option. |
| create(address: String, pubKeys: String[], options: WeightedMultiSigOptions): Account | Generates an Account instance with an address, public key strings, and the options that define the threshold and the weight of each key. |
| create(address: String, roleBasedPubKeys: List&amp;#60;String[]&amp;#62;): Account | Creates an Account instance with an array of public keys defined for each role. A default option with a threshold of 1 and a weight of 1 for each key will be used for each role. |
| create(address: String, roleBasedPubKeys: List&amp;#60;String[]&amp;#62;, options: List&amp;#60;WeightedMultiSigOptions&amp;#62;): Account | Creates an Account instance with an array of public keys and an array of options defined for each role. |
| createFromRLPEncoding(address: String, rlpEncodedKey: String): Account | Creates and returns an Account instance with an address and RLP-encoded string. It throws an exception if the RLP-encoded string is invalid. |
| createWithAccountKeyLegacy(address: String): Account | Creates and returns an Account instance which has AccountKeyLegacy as an accountKey. It throws an exception if the address string is invalid. |
| createWithAccountKeyPublic(address: String, publicKey: String): Account | Creates and returns an Account instance which has AccountKeyPublic as an accountKey. It throws an exception if the address string or public key string is invalid. |
| createWithAccountKeyFail(address: String): Account | Creates and returns an Account instance which has AccountKeyFail as an accountKey. It throws an exception if the address string is invalid. |
| createWithAccountKeyWeightedMultiSig(address: String, publicKeys: String[]): Account | Creates and returns an Account instance which has AccountKeyWeightedMultiSig as an accountKey. The options required for AccountKeyWeightedMultiSig use default values (threshold:1, weight of each key: 1). It throws an exception if the address string or public key strings are invalid. |
| createWithAccountKeyWeightedMultiSig(address: String, publicKeys: String[], options: WeightedMultiSigOptions): Account | Creates and returns an Account instance which has AccountKeyWeightedMultiSig as an accountKey. It throws an exception if the address string, public key strings, or options are invalid. |
| createWithAccountKeyRoleBased(address: String, roleBasedPubKeys: List&amp;#60;String[]&amp;#62;): Account | Creates and returns an Account instance which has AccountKeyRoleBased as an accountKey. If multiple keys are used among the defined roles, options use the default value (threshold:1, weight of each key: 1). It throws an exception if the address string or public key strings are invalid. |
| createWithAccountKeyRoleBased(address: String, roleBasedPubKeys: List&amp;#60;String[]&amp;#62;, options: List&amp;#60;WeightedMultiSigOptions&amp;#62;): Account | Creates and returns an Account instance which has AccountKeyRoleBased as an accountKey. It throws an exception if the address string, public key strings, or options are invalid. |
| getRLPEncodingAccountKey(): String | Returns the RLP-encoded string of the account key in the Account instance. |

### Wallet Layer

The `Wallet` layer allows the user to sign a message or a transaction through the Caver with a [Klaytn account].

![walletLayer_20210426](https://user-images.githubusercontent.com/32922423/116016574-fb28b480-a677-11eb-9579-31547b332b0b.png)

In the Wallet layer, an interface `IKeyring` is defined, and `SingleKeyring`, `MultipleKeyring` and `RoleBasedKeyring` implements `IKeyring`. The `IKeyring` interface defines methods that Keyring classes must implement.

`SingleKeyring` is a class that uses only one private key, and `MultipleKeyring` is a class that uses multiple private keys. `RoleBasedKeyring` is a class that uses different private key(s) for each role, and the `keys` member variable is a 2D array in which keys to be used for each role are defined.

Each keyring class uses the `PrivateKey` class, which has one private key as a member variable.

`KeyringFactory` provides static methods to create `SingleKeyring`, `MultipleKeyring` or `RoleBasedKeyring`.

`MessageSigned` is a structure that contains the result of signing a message, and it is used as a result of the `signMessage` function.

`SignatureData` is a structure that stores a signature.

`Keystore` is a class that contains encrypted keyring. The internally defined value differs depending on the keystore version, see [KIP-3](/KIPs/kip-3).

`IWallet` is an interface that contains common functions related to signing a transaction.

`KeyringContainer` is an &quot;in-memory wallet&quot; class that implements `IWallet` and manages keyring instances. It manages the keyring instances based on their addresses.

#### PrivateKey

`PrivateKey` is a class that contains a private key string.

##### Variables

| Variable | Description |
| ----------- | ----------- |
| privateKey: String | The private key string. |

##### Methods

| Method | Description |
| ----------- | ----------- |
| sign(txSigHash: String, chainId: String): SignatureData | Signs the transaction based on the transaction hash and chain id and returns the signature. Details of txSigHash are described in [Klaytn Docs](https://docs.klaytn.com/klaytn/design/transactions/basic#rlp-encoding-for-signature-1). |
| sign(txSigHash: String, chainId: int): SignatureData | Signs the transaction based on the transaction hash and chain id and returns the signature. Details of txSigHash are described in [Klaytn Docs](https://docs.klaytn.com/klaytn/design/transactions/basic#rlp-encoding-for-signature-1). |
| signMessage(messageHash: String): SignatureData | Signs the message with the Klaytn-specific prefix and returns the signature. |
| getPublicKey(compressed: Boolean): String | Returns the compressed public key string if compressed is true. It returns the uncompressed public key string otherwise. |
| getDerivedAddress(): String | Returns the derived address from the private key string. |

#### IKeyring

`IKeyring` is an interface of keyring classes. It stores the account address and private key(s). All keyring classes implement the `IKeyring` interface.

##### Variables

None

##### Methods

| Method | Description |
| ----------- | ----------- |
| sign(txSigHash: String, chainId: int, role: int): List&amp;#60;SignatureData&amp;#62; | Signs the transaction using the key(s) specified by the role, and it returns the signature. Keyring classes below implement `IAccountKey`, so this function must be implemented. Details of txSigHash are described in [Klaytn Docs](https://docs.klaytn.com/klaytn/design/transactions/basic#rlp-encoding-for-signature-1). |
| sign(txSigHash: String, chainId: int, role: int, index: int): SignatureData | Signs the transaction using the key(s) specified by the role and the index, and it returns the signature. Keyring classes below implement `IAccountKey`, so this function must be implemented. Details of txSigHash are described in [Klaytn Docs](https://docs.klaytn.com/klaytn/design/transactions/basic#rlp-encoding-for-signature-1). |
| sign(txSigHash: String, chainId: String, role: int): List&amp;#60;SignatureData&amp;#62; | Signs the transaction using the key(s) specified by the role and returns the signatures. Keyring classes below implement `IAccountKey`, so this function must be implemented. Details of txSigHash are described in [Klaytn Docs](https://docs.klaytn.com/klaytn/design/transactions/basic#rlp-encoding-for-signature-1). |
| sign(txSigHash: String, chainId: String, role: int, index: int): SignatureData | Signs the transaction using a key specified by the role and the index, and it returns the signature. Keyring classes below implement `IAccountKey`, so this function must be implemented. Details of txSigHash are described in [Klaytn Docs](https://docs.klaytn.com/klaytn/design/transactions/basic#rlp-encoding-for-signature-1). |
| signMessage(message: String, role: int): MessageSigned | Signs the transaction using the key(s) specified by the role, and it returns the signature. Keyring classes below implement `IAccountKey`, so this function must be implemented. |
| signMessage(message: String, role: int, index: int): MessageSigned | Signs the transaction using the key(s) specified by the role and the index, and it returns the signature. Keyring classes below implement `IAccountKey`, so this function must be implemented. |
| encrypt(password: String): Object | Encrypts a Keyring instance in the keystore v4 format. Keyring classes below implement `IAccountKey`, so this function must be implemented. |
| encrypt(password: String, options: Object): Object | Encrypts a Keyring instance in the [keystore v4 format](http://kips.klaytn.com/KIPs/kip-3). In options, the encrypt method (scrypt, pbkdf2, etc.) and parameters used for encrypting (dklen, salt, etc.) can be defined. Keyring classes below implement `IAccountKey`, so this function must be implemented. |
| encryptV3(password: String): Object | Encrypts a Keyring instance in the keystore v3 format. Keyring classes below implement `IAccountKey`, so this function must be implemented. |
| encryptV3(password: String, options: Object): Object | Encrypts a Keyring instance with keystore v3 format. In options, the encrypt method (scrypt, pbkdf2, etc.) and parameters used for encrypting (dklen, salt, etc.) can be defined. Keyring classes below implement `IAccountKey`, so this function must be implemented. |
| copy(): IKeyring | Duplicates the Keyring instance and returns it. Keyring classes below implement `IAccountKey`, so this function must be implemented. |
| getKlaytnWalletKey(): String | Returns the KlaytnWalletKey string. Keyring classes below implement `IAccountKey`, so this function must be implemented. |
| isDecoupled(): Boolean | Returns true if keyring has a decoupled key. |

#### SingleKeyring

`SingleKeyring` is a class that stores the account address and a private key.

##### Variables

| Variable | Description |
| ----------- | ----------- |
| address: String | The address of the account. |
| key: PrivateKey | An instance of `PrivateKey` containing one private key. |

##### Methods

| Method | Description |
| ----------- | ----------- |
| getPublicKey(): String | Returns the public key string. |
| getPublicKey(compressed: Boolean): String | Returns the compressed public key string if compressed is true. It returns the uncompressed public key string otherwise. |
| getKeyByRole(role: int): PrivateKey | Returns the keys specified by the role. SingleKeyring always returns the same key. |
| toAccount(): Account | Returns an Account instance. |
| sign(txSigHash: String, chainId: int, role: int): List&amp;#60;SignatureData&amp;#62; | Signs the transaction using the key that the SingleKeyring instance has, and it returns the signature. Details of txSigHash are described in [Klaytn Docs](https://docs.klaytn.com/klaytn/design/transactions/basic#rlp-encoding-for-signature-1). |
| sign(txSigHash: String, chainId: int, role: int, index: int): SignatureData | Signs the transaction using a key that the SingleKeyring instance has and the index, and it returns the signature. In the case of SingleKeyring, only one private key is managed, so if the index is greater than 0, an error should be returned. Details of txSigHash are described in [Klaytn Docs](https://docs.klaytn.com/klaytn/design/transactions/basic#rlp-encoding-for-signature-1). |
| sign(txSigHash: String, chainId: String, role: int): List&amp;#60;SignatureData&amp;#62; | Signs the transaction using the key that the SingleKeyring instance has, and it returns the signature. Details of txSigHash are described in [Klaytn Docs](https://docs.klaytn.com/klaytn/design/transactions/basic#rlp-encoding-for-signature-1). |
| sign(txSigHash: String, chainId: String, role: int, index: int): SignatureData | Signs the transaction using a key that the SingleKeyring instance has and the index, and it returns the signature. In the case of SingleKeyring, only one private key is managed, so if the index is greater than 0, an error should be returned. Details of txSigHash are described in [Klaytn Docs](https://docs.klaytn.com/klaytn/design/transactions/basic#rlp-encoding-for-signature-1). |
| signMessage(message: String, role: int): MessageSigned | Signs the transaction using the key that the SingleKeyring instance has, and it returns the signature. |
| signMessage(message: String, role: int, index: int): MessageSigned | Signs the transaction using a key that the SingleKeyring instance has and the index, and it returns the signature. In the case of SingleKeyring, only one private key is managed, so if the index is greater than 0, an error should be returned. |
| encrypt(password: String): Object | Encrypts a SingleKeyring instance in the keystore v4 format. |
| encrypt(password: String, options: Object): Object | Encrypts a SingleKeyring instance in the [keystore v4 format](http://kips.klaytn.com/KIPs/kip-3). In options, the encrypt method (scrypt, pbkdf2, etc.) and parameters used for encrypting (dklen, salt, etc.) can be defined. |
| encryptV3(password: String): Object | Encrypts a SingleKeyring instance in the keystore v3 format. |
| encryptV3(password: String, options: Object): Object | Encrypts a SingleKeyring instance with keystore v3 format. In options, the encrypt method (scrypt, pbkdf2, etc.) and parameters used for encrypting (dklen, salt, etc.) can be defined. |
| copy(): SingleKeyring | Duplicates the SingleKeyring instance and returns it. |
| getKlaytnWalletKey(): String | Returns the KlaytnWalletKey string. |
| isDecoupled(): Boolean | Returns true if keyring has a decoupled key. |

#### MultipleKeyring

`MultipleKeyring` is a class that stores an account address and multiple private keys.

##### Variables

| Variable | Description |
| ----------- | ----------- |
| address: String | The address of the account. |
| keys: List&amp;#60;PrivateKey&amp;#62; | An array of PrivateKey instances containing one private key. `keys` can contain up to ten PrivateKey instances. |

##### Methods

| Method | Description |
| ----------- | ----------- |
| getPublicKey(): List&amp;#60;String&amp;#62; | Returns the public key strings. |
| getPublicKey(compressed: Boolean): List&amp;#60;String&amp;#62; | Returns the compressed public key strings if compressed is true. It returns the uncompressed public key strings otherwise. |
| getKeyByRole(role: int): List&amp;#60;PrivateKey&amp;#62; | Returns the keys specified by the role. MultipleKeyring returns the same keys since it does not have role. |
| toAccount(): Account | Returns an Account instance. A default option with a threshold of 1 and a weight of 1 for each key will be used. |
| toAccount(options: WeightedMultiSigOptions): Account | Returns an Account instance with the given options. It throws an exception if the options is invalid. |
| sign(txSigHash: String, chainId: int, role: int): List&amp;#60;SignatureData&amp;#62; | Signs the transaction using the keys that the MultipleKeyring instance has, and it returns the signature. Details of txSigHash are described in [Klaytn Docs](https://docs.klaytn.com/klaytn/design/transactions/basic#rlp-encoding-for-signature-1). |
| sign(txSigHash: String, chainId: int, role: int, index: int): SignatureData | Signs the transaction using a key that the MultipleKeyring instance has and the index, and it returns the signature. In the case of MultipleKeyring, the index must be smaller than the length of the private keys that MultipleKeyring has. Details of txSigHash are described in [Klaytn Docs](https://docs.klaytn.com/klaytn/design/transactions/basic#rlp-encoding-for-signature-1). |
| sign(txSigHash: String, chainId: String, role: int): List&amp;#60;SignatureData&amp;#62; | Signs the transaction using the keys that the MultipleKeyring instance has and returns the signatures. Details of txSigHash are described in [Klaytn Docs](https://docs.klaytn.com/klaytn/design/transactions/basic#rlp-encoding-for-signature-1). |
| sign(txSigHash: String, chainId: String, role: int, index: int): SignatureData | Signs the transaction using a key that the MultipleKeyring instance has and the index, and it returns the signature. In the case of MultipleKeyring, the index must be smaller than the length of the private keys that MultipleKeyring has. Details of txSigHash are described in [Klaytn Docs](https://docs.klaytn.com/klaytn/design/transactions/basic#rlp-encoding-for-signature-1). |
| signMessage(message: String, role: int): MessageSigned | Signs the transaction using the keys that the MultipleKeyring instance has, and it returns the signature. |
| signMessage(message: String, role: int, index: int): MessageSigned | Signs the transaction using a key that the MultipleKeyring instance has and the index, and it returns the signature. In the case of MultipleKeyring, the index must be smaller than the length of the private keys that MultipleKeyring has. |
| encrypt(password: String): Object | Encrypts a MultipleKeyring instance in the keystore v4 format. |
| encrypt(password: String, options: Object): Object | Encrypts a MultipleKeyring instance in the [keystore v4 format](http://kips.klaytn.com/KIPs/kip-3). In options, the encrypt method (scrypt, pbkdf2, etc.) and parameters used for encrypting (dklen, salt, etc.) can be defined. |
| encryptV3(password: String): Object | Encrypts a MultipleKeyring instance in the keystore v3 format. |
| encryptV3(password: String, options: Object): Object | Encrypts a MultipleKeyring instance with keystore v3 format. In options, the encrypt method (scrypt, pbkdf2, etc.) and parameters used for encrypting (dklen, salt, etc.) can be defined. |
| copy(): MultipleKeyring | Duplicates the MultipleKeyring instance and returns it. |
| getKlaytnWalletKey(): String | Returns the KlaytnWalletKey string. |
| isDecoupled(): Boolean | Returns true if keyring has a decoupled key. |

#### RoleBasedKeyring

`RoleBasedKeyring` is a class that stores the account address and the private keys for each role in the form of an array.
`RoleBasedKeyring` defines keys which are implemented as a two-dimensional array (empty keys looks like [ [], [], [] ]) that can include multiple keys for each role. Each array element defines the private key(s) for roleTransactionKey, roleAccountUpdateKey, and roleFeePayerKey, respectively.

##### Variables

| Variable | Description |
| ----------- | ----------- |
| address: String | The address of the account. |
| keys: List&amp;#60;List&amp;#60;PrivateKey&amp;#62;&amp;#62; | A two-dimensional array that defines the keys used for each role. Each role includes PrivateKey instance(s). Each array element defines the private key(s) for roleTransactionKey, roleAccountUpdateKey, and roleFeePayerKey, respectively. |

##### Methods

| Method | Description |
| ----------- | ----------- |
| getPublicKey(): List&amp;#60;List&amp;#60;String&amp;#62;&amp;#62; | Returns the public key strings for all roles. |
| getPublicKey(compressed: Boolean): List&amp;#60;List&amp;#60;String&amp;#62;&amp;#62; | Returns the compressed public key strings for all roles if compressed is true. It returns the uncompressed public key strings otherwise. |
| getKeyByRole(role: int): List&amp;#60;PrivateKey&amp;#62; | Returns the private keys of the given role. |
| toAccount(): Account | Returns an Account instance. A default option with a threshold of 1 and a weight of 1 for each key will be used for each role. |
| toAccount(options: List&amp;#60;WeightedMultiSigOptions&amp;#62;): Account | Returns an Account instance with the given options. It throws an exception if the options is invalid. |
| sign(txSigHash: String, chainId: int, role: int): List&amp;#60;SignatureData&amp;#62; | Signs the transaction using the key(s) specified by the role, and it returns the signature. Details of txSigHash are described in [Klaytn Docs](https://docs.klaytn.com/klaytn/design/transactions/basic#rlp-encoding-for-signature-1). |
| sign(txSigHash: String, chainId: int, role: int, index: int): SignatureData | Signs the transaction using a key specified by the role and the index, and it returns the signature. In the case of RoleBasedKeyring, the index must be smaller than the length of the private keys defined in the role to be used for signing. Details of txSigHash are described in [Klaytn Docs](https://docs.klaytn.com/klaytn/design/transactions/basic#rlp-encoding-for-signature-1). |
| sign(txSigHash: String, chainId: String, role: int): List&amp;#60;SignatureData&amp;#62; | Signs the transaction using the key(s) specified by the role and returns the signatures. Details of txSigHash are described in [Klaytn Docs](https://docs.klaytn.com/klaytn/design/transactions/basic#rlp-encoding-for-signature-1). |
| sign(txSigHash: String, chainId: String, role: int, index: int): SignatureData | Signs the transaction using a key specified by the role and the index, and it returns the signature. In the case of RoleBasedKeyring, the index must be smaller than the length of the private keys defined in the role to be used for signing. Details of txSigHash are described in [Klaytn Docs](https://docs.klaytn.com/klaytn/design/transactions/basic#rlp-encoding-for-signature-1). |
| signMessage(message: String, role: int): MessageSigned | Signs the transaction using the key(s) specified by the role, and it returns the signature. |
| signMessage(message: String, role: int, index: int): MessageSigned | Signs the transaction using a key specified by the role and the index, and it returns the signature. In the case of RoleBasedKeyring, the index must be smaller than the length of the private keys defined in the role to be used for signing. |
| encrypt(password: String): Object | Encrypts a RoleBasedKeyring instance in the keystore v4 format. |
| encrypt(password: String, options: Object): Object | Encrypts a RoleBasedKeyring instance in the [keystore v4 format](http://kips.klaytn.com/KIPs/kip-3). In options, the encrypt method (scrypt, pbkdf2, etc.) and parameters used for encrypting (dklen, salt, etc.) can be defined. |
| encryptV3(password: String): Object | Encrypts a RoleBasedKeyring instance in the keystore v3 format. |
| encryptV3(password: String, options: Object): Object | Encrypts a RoleBasedKeyring instance with keystore v3 format. In options, the encrypt method (scrypt, pbkdf2, etc.) and parameters used for encrypting (dklen, salt, etc.) can be defined. |
| copy(): RoleBasedKeyring | Duplicates the RoleBasedKeyring instance and returns it. |
| getKlaytnWalletKey(): String | Returns the KlaytnWalletKey string. |
| isDecoupled(): Boolean | Returns true if keyring has a decoupled key. |

#### SignatureData

`SignatureData` is a class that contains a signature string.

##### Variables

| Variable | Description |
| ----------- | ----------- |
| v: String | ECDSA recovery id. |
| r: String | ECDSA signature r. |
| s: String | ECDSA signature s. |

##### Methods

None

#### MessageSigned

`MessageSigned` stores the result of signing a message.

##### Variables

| Variable | Description |
| ----------- | ----------- |
| messageHash: String | The hashed message string. |
| signatureData: List&lt;SignatureData&gt; | An array of signatures. |
| message: String | The original message used for signing. |

##### Methods

None

#### KeyringFactory

`KeyringFactory` provides functions to create Keyring (SingleKeyring, MultipleKeyring, and RoleBasedKeyring) instances.

##### Variables

None

##### Methods

| Method | Description |
| ----------- | ----------- |
| generate(): SingleKeyring | Generates a SingleKeyring instance with a randomly generated private key. |
| generate(entropy: String): SingleKeyring | Generates a SingleKeyring instance with a randomly generated private key based on entropy. |
| generateSingleKey(): String | Generates a private key string. |
| generateSingleKey(entropy: String): String | Generates a private key string based on entropy. |
| generateMultipleKeys(num: int): List&amp;#60;String&amp;#62; | Generates private key strings. |
| generateMultipleKeys(num: int, entropy: String): List&amp;#60;String&amp;#62; | Generates private key strings based on entropy. |
| generateRoleBasedKeys(numArr: List&amp;#60;int&amp;#62;): List&amp;#60;List&amp;#60;String&amp;#62;&amp;#62; | Generates and returns private keys for each role. The number of keys for each role is defined in numArr. |
| generateRoleBasedKeys(numArr: List&amp;#60;int&amp;#62;, entropy: String): List&amp;#60;List&amp;#60;String&amp;#62;&amp;#62; | Generates and returns private keys for each role based on entropy. The number of keys for each role is defined in numArr. |
| create(address: String, key: String): SingleKeyring | Creates and returns a SingleKeyring instance with an address and a private key string. It throws an exception if the address string or private key string is invalid. |
| create(address: String, keys: String[]): MultipleKeyring | Creates a MultipleKeyring instance with an address and private key strings. It throws an exception if the address string or private key strings are invalid. |
| create(address: String, roleBasedKeys: List&amp;#60;String[]&amp;#62;): RoleBasedKeyring | Creates a RoleBasedKeyring instance with an address and private key strings by roles. It throws an exception if the address string or private key strings are invalid. |
| createFromPrivateKey(key: String): SingleKeyring | Creates a SingleKeyring instance from a private key string or a KlaytnWalletKey. It throws an exception if the private key string is invalid. |
| createFromKlaytnWalletKey(klaytnWalletKey: String): SingleKeyring | Creates a SingleKeyring instance from a KlaytnWalletKey string. It throws an exception if the KlaytnWalletKey is invalid. |
| createWithSingleKey(address: String, key: String): SingleKeyring | Creates a SingleKeyring instance from an address and a private key string. It throws an exception if the address string or the private key string is invalid. |
| createWithMultipleKey(address: String, keys: String[]): MultipleKeyring | Creates a MultipleKeyring instance from an address and the private key strings. It throws an exception if the address string or the private key strings are invalid. |
| createWithRoleBasedKey(address: String, roleBasedKeys: List&amp;#60;String[]&amp;#62;): RoleBasedKeyring | Creates a RoleBasedKeyring instance from an address and a 2D array of which each array element contains keys defined for each role. It throws an exception if the address string or the private key strings are invalid. |
| decrypt(keystore: Object, password: String): Keyring | Decrypts a keystore v3 or v4 JSON and returns the decrypted keyring instance. It throws an exception if the decryption is failed. |

#### IWallet

`IWallet` is an interface that contains common functions related to signing a transaction. All Wallet classes must implement `IWallet`.
Caver has a class `KeyringContainer` that implements `IWallet`. If it is a class that implements `IWallet` interface, it can be used instead of `KeyringContainer` to sign a transaction.

##### Variables

None

##### Methods

| Method | Description |
| ----------- | ----------- |
| generate(num: int): List&lt;String&gt; | Generates `num` account instances and stores them in the wallet. |
| sign(address: String, transaction: AbstractTransaction): AbstractTransaction | Signs the `transaction` using the private key of the `address` and appends signatures in the `transaction` object. It throws an exception if the `address` is not found or the `address` and the sender address of `transaction` are not the same. |
| signAsFeePayer(address: String, transaction: AbstractFeeDelegatedTransaction): AbstractFeeDelegatedTransaction | Signs the `transaction` using the private key of the `address` as a fee payer and appends signatures in the transaction object. It throws an exception if the `address` is not found or the `address` and the fee payer of the `transaction` are not the same. |
| isExisted(address: String): Boolean | Returns whether the account corresponding to the address exists. |
| remove(address: String): Boolean | Deletes an account associated with the given address. |

#### KeyringContainer

`KeyringContainer` is a class that can contain SingleKeyring, MultipleKeyring, and RoleBasedKeyring instances based on the address. `KeyringContainer` implements `IWallet`.

##### Variables

| Variable | Description |
| ----------- | ----------- |
| length: int | The number of keyrings in the `KeyringContainer` instance. |
| addressKeyringMap: Map&lt;String, IKeyring&gt; | A Map that has an account address as a key and a Keyring instance corresponding to that address as a value. |

##### Methods

| Method | Description |
| ----------- | ----------- |
| generate(num: int): List&lt;String&gt; | Generates `num` SingleKeyring instances and stores them in the wallet. |
| generate(num: int, entropy: String): List&amp;#60;String&amp;#62; | Generates `num` SingleKeyring instances and stores them in the keyringContainer. The randomness of the private keys is determined by the given entropy. |
| add(keyring: IKeyring): IKeyring | Adds the given keyring instance to the `KeyringContainer` instance. It throws an exception if the address of the given keyring instance already exists in the `KeyringContainer` instance. |
| newKeyring(address: String, privateKeyString: String): IKeyring | Creates a `SingleKeyring` instance with given parameters and adds it to the `KeyringContainer` instance. It returns the newly added keyring instance. It throws an exception if the address or private key string is invalid. |
| newKeyring(address: String, privateKeyArray: List&amp;#60;String&amp;#62;): IKeyring | Creates a `MultipleKeyring` instance with given parameters and adds it to the `KeyringContainer` instance. It returns the newly added keyring instance. It throws an exception if the address string or private key strings are invalid. |
| newKeyring(address: String, roleBasedPrivateKeyArray: List&amp;#60;List&amp;#60;String&amp;#62;&amp;#62;): IKeyring | Creates a `RoleBasedKeyring` instance with given parameters and adds it to the `KeyringContainer` instance. It returns the newly added keyring instance. It throws an exception if the address string or private key strings are invalid. |
| updateKeyring(keyring: IKeyring): IKeyring | Replaces the keyring instance having the same address with the given keyring in the parameter. It throws an exception if the matching keyring is not found. |
| getKeyring(address: String): IKeyring | Returns the keyring instance corresponding to the address. |
| sign(address: String, transaction: AbstractTransaction): AbstractTransaction | Signs the `transaction` using the private key of the `address` and appends signatures in the `transaction` object. This method will use all the private keys. It throws an exception if the `address` is not found or the `address` and the sender address of `transaction` are not the same. |
| sign(address: String, transaction: AbstractTransaction, index: int): AbstractTransaction | Signs the `transaction` using the private key of the `address` and appends signatures in the `transaction` object. This method uses the private key at `index` in the keyring. It throws an exception if the `address` is not found or the `address` and the sender address of `transaction` are not the same. |
| sign(address: String, transaction: AbstractTransaction, hasher: Function): AbstractTransaction | Signs the `transaction` using the private key of the `address` and appends signatures in the `transaction` object. This method will use all the private keys. And when obtaining the transaction hash, `hasher` is used. It throws an exception if the `address` is not found or the `address` and the sender address of `transaction` are not the same. |
| sign(address: String, transaction: AbstractTransaction, index: int, hasher: Function): AbstractTransaction | Signs the `transaction` using the private key of the `address` and appends signatures in the `transaction` object. This method uses the private key at `index` in the keyring. And when obtaining the transaction hash, `hasher` is used. It throws an exception if the `address` is not found or the `address` and the sender address of `transaction` are not the same. |
| signAsFeePayer(address: String, transaction: AbstractFeeDelegatedTransaction): AbstractFeeDelegatedTransaction | Signs the `transaction` using the private key of the `address` as a fee payer and appends signatures in the transaction object. This method will use all the private keys. It throws an exception if the `address` is not found or the `address` and the fee payer of the `transaction` are not the same. |
| signAsFeePayer(address: String, transaction: AbstractFeeDelegatedTransaction, index: int): AbstractFeeDelegatedTransaction | Signs the `transaction` using the private key of the `address` as a fee payer and appends signatures in the transaction object. This method uses the private key at `index` in the keyring. It throws an exception if the `address` is not found or the `address` and the fee payer of the `transaction` are not the same. |
| signAsFeePayer(address: String, transaction: AbstractFeeDelegatedTransaction, hasher: Function): AbstractFeeDelegatedTransaction | Signs the `transaction` using the private key of the `address` as a fee payer and appends signatures in the transaction object. This method will use all the private keys. And when obtaining the transaction hash, `hasher` is used. It throws an exception if the `address` is not found or the `address` and the fee payer of the `transaction` are not the same. |
| signAsFeePayer(address: String, transaction: AbstractFeeDelegatedTransaction, index: int, hasher: Function): AbstractFeeDelegatedTransaction | Signs the `transaction` using the private key of the `address` as a fee payer and appends signatures in the transaction object. This method uses the private key at `index` in the keyring. And when obtaining the transaction hash, `hasher` is used. It throws an exception if the `address` is not found or the `address` and the fee payer of the `transaction` are not the same. |
| signMessage(address: String, data: String, role: int): MessageSigned | Signs the message with the Klaytn-specific prefix using the keyring associated with the given address in the `KeyringContainer` instance. This method will use all the private keys in the keyring. It throws an exception if the keyring to be used for signing cannot be found in the `KeyringContainer` instance. |
| signMessage(address: String, data: String, role: int, index: int): MessageSigned | Signs the message with Klaytn-specific prefix using the keyring associated with the given address in the `KeyringContainer` instance. This method uses the private key at `index` in the keyring. It throws an exception if the keyring to be used for signing cannot be found in the `KeyringContainer` instance. |
| isExisted(address: String): Boolean | Returns whether the account corresponding to the address exists. |
| remove(address: String): Boolean | Deletes an account associated with the given address. |

### Transaction Layer

The `Transaction` layer provides classes related to Klaytn transactions.

![transactionlayer](https://user-images.githubusercontent.com/32922423/115336537-60912700-a1da-11eb-9abc-45c44abd0bb4.png)

Abstract classes (`AbstractTransaction`, `AbstractFeeDelegatedTransaction` and `AbstractFeeDelegatedWithRatioTrasnaction`) are defined in the Transaction layer to declare common attributes and methods.

`AbstractTransaction` defines variables and methods commonly used in all transactions. In addition, abstract methods that must be implemented in classes that extend this class are also defined. `AbstractFeeDelegatedTransaction` extends `AbstractTransaction`, and it defines variables and methods commonly used in fee delegation and partial fee delegation transactions. `AbstractFeeDelegatedWithRatioTransaction` extends `AbstractFeeDelegatedTransaction`, and it defines variables commonly used in partial fee delegation transactions.

Among the transaction types used in Klaytn, [Basic transactions] are implemented by extending `AbstractTransaction`. [Fee delegation transactions] are implemented by extending `AbstractFeeDelegatedTransaction`, [Partial fee delegation transactions] are implemented by extending `AbstractFeeDelegatedWithRatioTransaction`.

The `TransactionDecoder` class decodes the RLP-encoded string using the `decode` method implemented in each transaction class.

`TransactionHasher` is a class that calculates the hash of a transaction. It provides a function that calculates a hash when a transaction is signed as a sender or a fee payer. It is implemented based on [Klaytn Design - Transactions].

#### AbstractTransaction

`AbstractTransaction` is an abstract class that represents transaction types defined in [Basic Transaction](https://docs.klaytn.com/klaytn/design/transactions/basic). All basic transaction classes extend `AbstractTransaction`.

##### Variables

| Variable | Description |
| ----------- | ----------- |
| type: String | The type string of the transaction. |
| from: String | The address of the sender. |
| nonce: String | A value used to uniquely identify a sender’s transaction. If omitted when creating a transaction, [klay_getTransactionCount](https://docs.klaytn.com/bapp/json-rpc/api-references/klay/account#klay_gettransactioncount) will be used to set an appropriate nonce. |
| gas: String | The maximum amount of the transaction fee allowed to use. |
| gasPrice: String | A multiplier to get how much the sender will pay in KLAY. If omitted when creating a transaction, [klay_gasPrice](https://docs.klaytn.com/bapp/json-rpc/api-references/klay/config#klay_gasprice) will be used to set this value. |
| signatures: List&lt;SignatureData&gt; | An array of signatures. The result of signing the transaction is appended to these signatures. When appending a signature, duplicate signatures are not appended. |
| chainId: String | The chain id of the Klaytn network. If omitted when creating a transaction, [klay_chainID](https://docs.klaytn.com/bapp/json-rpc/api-references/klay/config#klay_chainid) will be used to set this value. |

##### Methods

| Method | Description |
| ----------- | ----------- |
| create(tx: Object): AbstractTransaction | Creates an instance of `AbstractTransaction`. It is defined as an abstract method, and must be implemented in all transaction classes that extend `AbstractTransaction`. |
| getRLPEncoding(): String | Returns an RLP-encoded transaction string. It is defined as an abstract method, and must be implemented in all transaction classes that extend `AbstractTransaction`. |
| getCommonRLPEncodingForSignature(): String | Encodes and returns the values needed to sign each transaction. For example, in the case of value transfer transactions, if SigRLP is `encode([encode([type, nonce, gasPrice, gas, to, value, from]), chainid, 0, 0])`, among them, the RLP-encoded values of the transaction required for signing is `encode([type, nonce, gasPrice, gas, to, value, from])`. This function is used in getRLPEncodingForSignature or getRLPEncodingForFeePayerSignature function. It is defined as an abstract method, and must be implemented in all transaction classes that extend `AbstractTransaction`. |
| sign(keyString: String): AbstractTransaction | Signs the transaction as a sender with the private key (or KlaytnWalletKey) and appends signatures to the transaction object. |
| sign(keyString: String, hasher: Function): AbstractTransaction | Signs the transaction as a sender with the private key (or KlaytnWalletKey) and appends signatures to the transaction object. The `hasher` function will be used when getting the hash of a transaction. |
| sign(keyring: IKeyring): AbstractTransaction | Signs the transaction as a sender with all the private keys in the given keyring and appends signatures to the transaction object. |
| sign(keyring: IKeyring, index: int): AbstractTransaction | Signs the transaction as a sender with the private key specified by the given keyring and index. It appends the result signature into the transaction object. |
| sign(keyring: IKeyring, hasher: Function): AbstractTransaction | Signs the transaction as a sender with the private keys in the given keyring. When obtaining the transaction hash, `hasher` is used. |
| sign(keyring: IKeyring, index: int, hasher: Function): AbstractTransaction | Signs the transaction as a sender with the private key specified by the given keyring and index. It appends signatures into the transaction object. The `hasher` function will be used when getting the transaction hash. |
| appendSignatures(sig: SignatureData): void | Appends the given signature to the transaction instance. |
| appendSignatures(sig: List&amp;#60;SignatureData&amp;#62;): void | Appends the given signatures to the transaction instance. |
| combineSignedRawTransactions(rlpEncoded: List&amp;#60;String&amp;#62;): String | Collects signatures in RLP-encoded transaction strings in the given array, combines them into this transaction instance, and returns an RLP-encoded transaction string which includes all signatures. If the contents of the transaction are different from this transaction instance, it fails. |
| getTransactionHash(): String | Returns the transaction hash of this transaction instance. |
| getSenderTxHash(): String | Returns a senderTxHash of this transaction instance. |
| getRawTransaction(): String | Returns a rawTransaction string (an RLP-encoded transaction string). This function is identical to `getRLPEncoding()`. |
| getRLPEncodingForSignature(): String | Returns an RLP-encoded transaction string to make the signature of the sender. |
| fillTransaction(): void | Fills in the optional variables (nonce, gasPrice, and chainId) in transaction. |

#### AbstractFeeDelegatedTransaction

`AbstractFeeDelegatedTransaction` is an abstract class that represents transaction types defined in [fee delegation transaction](https://docs.klaytn.com/klaytn/design/transactions/fee-delegation). `AbstractFeeDelegatedTransaction` is implemented by extending `AbstractTransaction`. All fee delegation transaction classes are implemented by extending `AbstractFeeDelegatedTransaction`.

##### Variables

| Variable | Description |
| ----------- | ----------- |
| feePayer: String | The address of the fee payer. |
| feePayerSignatures: List&lt;SignatureData&gt; | An array of signatures of fee payers. The result of signing the transaction as a fee payer is appended to this array. If the duplicated fee payer signature is added, it fails. |

##### Methods

| Method | Description |
| ----------- | ----------- |
| signAsFeePayer(keyString: String): AbstractFeeDelegatedTransaction | Signs the transaction as a fee payer with a private key string (or KlaytnWalletKey) and appends the fee payer signature into the transaction object. |
| signAsFeePayer(keyString: String, hasher: Function): AbstractFeeDelegatedTransaction | Signs the transaction as a fee payer with a private key string (or KlaytnWalletKey) and appends the fee payer signature in the transaction object. The `hasher` function will be used when getting the hash of the transaction. |
| signAsFeePayer(keyring: IKeyring): AbstractFeeDelegatedTransaction | Signs the transaction as a fee payer with private keys in `keyring` and appends the fee payer signatures in the transaction object. If the fee payer address of this transaction instance differs from the address of `keyring`, it throws an exception. |
| signAsFeePayer(keyring: IKeyring, index: int): AbstractFeeDelegatedTransaction | Signs the transaction as a fee payer with a private key specified by `keyring` and `index`. It appends the fee payer signatures into the transaction object. If the fee payer address of this transaction instance differs from the address of `keyring`, it throws an exception. |
| signAsFeePayer(keyring: IKeyring, hasher: Function): AbstractFeeDelegatedTransaction | Signs the transaction as a fee payer with private keys in `keyring` and appends the fee payer signature into the transaction object. The `hasher` function will be used when getting the hash of the transaction. If the fee payer address of this transaction instance differs from the address of `keyring`, it throws an exception. |
| signAsFeePayer(keyring: IKeyring, index: int, hasher: Function): AbstractFeeDelegatedTransaction | Signs the transaction as a fee payer with a private key specified by `keyring` and `index. It appends the fee payer signature into the transaction object. The `hasher` function will be used when getting the hash of the transaction. If the fee payer address of this transaction instance differs from the address of `keyring`, it throws an exception. |
| appendFeePayerSignatures(sig: SignatureData): void | Appends a fee payer signature to the transaction instance. |
| appendFeePayerSignatures(sig: List&amp;#60;SignatureData&amp;#62;): void | Appends fee payer signatures to the transaction instance. |
| combineSignedRawTransactions(rlpEncoded: List&lt;String&gt;): String | Collects signatures in RLP-encoded transaction strings in the given array, combines them into this transaction instance, and returns an RLP-encoded transaction string which includes all signatures. If the contents of the transaction are different from this transaction instance, it fails. |
| getRLPEncodingForFeePayerSignature(): String | Returns an RLP-encoded transaction string to make the signature of the fee payer. |

#### AbstractFeeDelegatedWithRatioTransaction

`AbstractFeeDelegatedWithRatioTransaction` is an abstract class that represents transaction types defined in [partial fee delegation transaction](https://docs.klaytn.com/klaytn/design/transactions/partial-fee-delegation). `AbstractFeeDelegatedWithRatioTransaction` is implemented by extending `AbstractFeeDelegatedTransaction`. All partial fee delegation transaction classes extend `AbstractFeeDelegatedWithRatioTransaction`.

##### Variables

| Variable | Description |
| ----------- | ----------- |
| feeRatio: String | The ratio that constitutes the proportion of the transaction fee the fee payer will be burdened with. The valid range of this ratio is between 1 and 99. The ratio of negatives, 0, or 100 and above are not allowed. |

##### Methods

None

#### LegacyTransaction

`LegacyTransaction` represents a [legacy transaction](https://docs.klaytn.com/klaytn/design/transactions/basic#txtypelegacytransaction). This class is implemented by extending [AbstractTransaction](#abstracttransaction).

##### Variables

| Variable | Description |
| ----------- | ----------- |
| to: String | The account address that will receive the transferred value or a smart contact address if a legacy transaction executes a smart contract. If a legacy transaction deploys a smart contract, this value should be set to empty or null. |
| input: String | Data attached to the transaction. It is used for smart contract deployment/execution. |
| value: String | The amount of KLAY in peb to be transferred. |

##### Methods

| Method | Description |
| ----------- | ----------- |
| decode(rlpEncoded: String): LegacyTransaction | Decodes an RLP-encoded string of LegacyTransaction and returns a `LegacyTransaction` instance. It throws an exception if the decoding is failed. |
| create(tx: Object): LegacyTransaction | Creates an instance of LegacyTransaction. |
| appendSignatures(sig: SignatureData): void | Appends signature to the transaction. LegacyTransaction can only have one signature. It throws an exception if the transaction instance has a signature already. |
| appendSignatures(sig: List&amp;#60;SignatureData&amp;#62;): void | Appends signature to the transaction. LegacyTransaction can only have one signature. It throws an exception if multiple signatures are given. |
| getRLPEncoding(): String | Returns an RLP-encoded string of the transaction instance. It throws an exception if the variables required for encoding are not defined. |
| getRLPEncodingForSignature(): String | Returns an RLP-encoded transaction string for making the signature of the sender. Since the method of obtaining the RLP-encoding string for the signature of LegacyTransaction is different from other transaction types, getRLPEncodingForSignature should be overridden. It throws an exception if the variables required for encoding are not defined. |

#### ValueTransfer

`ValueTransfer` represents a [value transfer transaction](https://docs.klaytn.com/klaytn/design/transactions/basic#txtypevaluetransfer). This class is implemented by extending [AbstractTransaction](#abstracttransaction).

##### Variables

| Variable | Description |
| ----------- | ----------- |
| to: String | The account address that will receive the transferred value. |
| value: String | The amount of KLAY in peb to be transferred. |

##### Methods

| Method | Description |
| ----------- | ----------- |
| decode(rlpEncoded: String): ValueTransfer | Decodes an RLP-encoded string of ValueTransfer and returns a `ValueTransfer` instance. It throws an exception if the decoding is failed. |
| create(tx: Object): ValueTransfer | Creates an instance of `ValueTransfer`. |
| getRLPEncoding(): String | Returns an RLP-encoded string of the transaction instance. It throws an exception if the variables required for encoding are not defined. |
| getCommonRLPEncodingForSignature(): String | Encodes the values needed to sign the transaction and returns the RLP-encoded string. It throws an exception if the variables required for encoding are not defined. |

#### ValueTransferMemo

`ValueTransferMemo` represents a [value transfer memo transaction](https://docs.klaytn.com/klaytn/design/transactions/basic#txtypevaluetransfermemo). This class is implemented by extending [AbstractTransaction](#abstracttransaction).

##### Variables

| Variable | Description |
| ----------- | ----------- |
| to: String | The account address that will receive the transferred value. |
| value: String | The amount of KLAY in peb to be transferred. |
| input: String | Data attached to the transaction. The message can be passed to this attribute. |

##### Methods

| Method | Description |
| ----------- | ----------- |
| decode(rlpEncoded: String): ValueTransferMemo | Decodes an RLP-encoded string of ValueTransferMemo and returns a `ValueTransferMemo` instance. It throws an exception if the decoding is failed. |
| create(tx: Object): ValueTransferMemo | Creates an instance of `ValueTransferMemo`. |
| getRLPEncoding(): String | Returns an RLP-encoded string of the transaction instance. It throws an exception if the variables required for encoding are not defined. |
| getCommonRLPEncodingForSignature(): String | Encodes the values needed to sign the transaction and returns the RLP-encoded string. It throws an exception if the variables required for encoding are not defined. |

#### AccountUpdate

`AccountUpdate` represents an [account update transaction](https://docs.klaytn.com/klaytn/design/transactions/basic#txtypeaccountupdate). This class is implemented by extending [AbstractTransaction](#abstracttransaction).

##### Variables

| Variable | Description |
| ----------- | ----------- |
| account: Account | An [Account](#account) instance that contains the information needed to update the given account. |

##### Methods

| Method | Description |
| ----------- | ----------- |
| decode(rlpEncoded: String): AccountUpdate | Decodes an RLP-encoded string of AccountUpdate and returns an AccountUpdate instance. It throws an exception if the decoding is failed. |
| create(tx: Object): AccountUpdate | Creates an instance of `AccountUpdate`. |
| getRLPEncoding(): String | Returns an RLP-encoded string of the transaction instance. It throws an exception if the variables required for encoding are not defined. |
| getCommonRLPEncodingForSignature(): String | Encodes the values needed to sign the transaction and returns the RLP-encoded string. It throws an exception if the variables required for encoding are not defined. |

#### SmartContractDeploy

`SmartContractDeploy` represents a [smart contract deploy transaction](https://docs.klaytn.com/klaytn/design/transactions/basic#txtypesmartcontractdeploy). This class is implemented by extending [AbstractTransaction](#abstracttransaction).

##### Variables

| Variable | Description |
| ----------- | ----------- |
| to: String | An Address to which the smart contract is deployed. Currently, this value cannot be defined by user, so it must be defined with the default value &quot;0x&quot;. Specifying the address will be supported in the future. |
| value: String | The amount of KLAY in peb to be transferred. If the value is not defined by user, the value is defined with the default value &quot;0x0&quot;. |
| input: String | Data attached to the transaction. It contains the byte code of the smart contract to be deployed and its arguments. |
| humanReadable: String | This must be &quot;false&quot; since human-readable address is not supported yet. If the value is not defined by user, it is defined with the default value &quot;false&quot;. |
| codeFormat: String | The code format of the smart contract. The supported value, for now, is &quot;EVM&quot; only. If the value is not defined by user, it is defined with the default value &quot;EVM&quot;. This value is converted to a hex string after the assignment (e.g., EVM is converted to 0x0). |

##### Methods

| Method | Description |
| ----------- | ----------- |
| decode(rlpEncoded: String): SmartContractDeploy | Decodes an RLP-encoded string of SmartContractDeploy and returns a `SmartContractDeploy` instance. |
| create(tx: Object): SmartContractDeploy | Creates an instance of `SmartContractDeploy`. |
| getRLPEncoding(): String | Returns an RLP-encoded string of the transaction instance. It throws an exception if the variables required for encoding are not defined. |
| getCommonRLPEncodingForSignature(): String | Encodes the values needed to sign the transaction and returns the RLP-encoded string. It throws an exception if the variables required for encoding are not defined. |

#### SmartContractExecution

`SmartContractExecution` represents a [smart contract execution transaction](https://docs.klaytn.com/klaytn/design/transactions/basic#txtypesmartcontractexecution). This class is implemented by extending [AbstractTransaction](#abstracttransaction).

##### Variables

| Variable | Description |
| ----------- | ----------- |
| to: String | The address of the smart contract account to be executed. |
| value: String | The amount of KLAY in peb to be transferred. If the value is not defined by user, the value is defined with the default value &quot;0x0&quot;. |
| input: String | Data attached to the transaction, used for transaction execution. The input is an encoded string that indicates a function to call and parameters to be passed. |

##### Methods

| Method | Description |
| ----------- | ----------- |
| decode(rlpEncoded: String): SmartContractExecution | Decodes an RLP-encoded string of SmartContractExecution and returns a `SmartContractExecution` instance. It throws an exception if the decoding is failed. |
| create(tx: Object): SmartContractExecution | Creates an instance of `SmartContractExecution`. |
| getRLPEncoding(): String | Returns an RLP-encoded string of the transaction instance. It throws an exception if the variables required for encoding are not defined. |
| getCommonRLPEncodingForSignature(): String | Encodes the values needed to sign the transaction and returns the RLP-encoded string. It throws an exception if the variables required for encoding are not defined. |

#### Cancel

`Cancel` represents a [cancel transaction](https://docs.klaytn.com/klaytn/design/transactions/basic#txtypecancel). This class is implemented by extending [AbstractTransaction](#abstracttransaction).

##### Variables

None

##### Methods

| Method | Description |
| ----------- | ----------- |
| decode(rlpEncoded: String): Cancel | Decodes an RLP-encoded string of Cancel and returns a `Cancel` instance. It throws an exception if the decoding is failed. |
| create(tx: Object): Cancel | Creates an instance of `Cancel`. |
| getRLPEncoding(): String | Returns an RLP-encoded string of the transaction instance. It throws an exception if the variables required for encoding are not defined. |
| getCommonRLPEncodingForSignature(): String | Encodes the values needed to sign the transaction and returns the RLP-encoded string. It throws an exception if the variables required for encoding are not defined. |

#### ChainDataAnchoring

`ChainDataAnchoring` represents a [chain data anchoring transaction](https://docs.klaytn.com/klaytn/design/transactions/basic#txtypechaindataanchoring). This class is implemented by extending [AbstractTransaction](#abstracttransaction).

##### Variables

| Variable | Description |
| ----------- | ----------- |
| input: String | Data to be anchored. |

##### Methods

| Method | Description |
| ----------- | ----------- |
| decode(rlpEncoded: String): ChainDataAnchoring | Decodes an RLP-encoded string of ChainDataAnchoring and returns a `ChainDataAnchoring` instance. It throws an exception if the decoding is failed. |
| create(tx: Object): ChainDataAnchoring | Creates an instance of `ChainDataAnchoring`. |
| getRLPEncoding(): String | Returns an RLP-encoded string of the transaction instance. It throws an exception if the variables required for encoding are not defined. |
| getCommonRLPEncodingForSignature(): String | Encodes the values needed to sign the transaction and returns the RLP-encoded string. It throws an exception if the variables required for encoding are not defined. |

#### FeeDelegatedValueTransfer

`FeeDelegatedValueTransfer` represents a [fee delegated value transfer transaction](https://docs.klaytn.com/klaytn/design/transactions/fee-delegation#txtypefeedelegatedvaluetransfer). This class is implemented by extending [AbstractFeeDelegatedTransaction](#abstractfeedelegatedtransaction).

##### Variables

| Variable | Description |
| ----------- | ----------- |
| to: String | The account address that will receive the transferred value. |
| value: String | The amount of KLAY in peb to be transferred. |

##### Methods

| Method | Description |
| ----------- | ----------- |
| decode(rlpEncoded: String): FeeDelegatedValueTransfer | Decodes an RLP-encoded string of FeeDelegatedValueTransfer and returns a `FeeDelegatedValueTransfer` instance. It throws an exception if the decoding is failed. |
| create(tx: Object): FeeDelegatedValueTransfer | Creates an instance of `FeeDelegatedValueTransfer`. |
| getRLPEncoding(): String | Returns an RLP-encoded string of the transaction instance. It throws an exception if the variables required for encoding are not defined. |
| getCommonRLPEncodingForSignature(): String | Encodes the values needed to sign the transaction and returns the RLP-encoded string. It throws an exception if the variables required for encoding are not defined. |

#### FeeDelegatedValueTransferMemo

`FeeDelegatedValueTransferMemo` represents a [fee delegated value transfer memo transaction](https://docs.klaytn.com/klaytn/design/transactions/fee-delegation#txtypefeedelegatedvaluetransfermemo). This class is implemented by extending [AbstractFeeDelegatedTransaction](#abstractfeedelegatedtransaction).

##### Variables

| Variable | Description |
| ----------- | ----------- |
| to: String | The account address that will receive the transferred value. |
| value: String | The amount of KLAY in peb to be transferred. |
| input: String | Data attached to the transaction. The message can be passed to this attribute. |

##### Methods

| Method | Description |
| ----------- | ----------- |
| decode(rlpEncoded: String): FeeDelegatedValueTransferMemo | Decodes an RLP-encoded string of FeeDelegatedValueTransferMemo and returns a `FeeDelegatedValueTransfer` instance. It throws an exception if the decoding is failed. |
| create(tx: Object): FeeDelegatedValueTransferMemo | Creates an instance of `FeeDelegatedValueTransferMemo`. |
| getRLPEncoding(): Stirng | Returns an RLP-encoded string of the transaction instance. It throws an exception if the variables required for encoding are not defined. |
| getCommonRLPEncodingForSignature(): String | Encodes the values needed to sign the transaction and returns the RLP-encoded string. It throws an exception if the variables required for encoding are not defined. |

#### FeeDelegatedAccountUpdate

`FeeDelegatedAccountUpdate` represents a [fee delegated account update transaction](https://docs.klaytn.com/klaytn/design/transactions/fee-delegation#txtypefeedelegatedaccountupdate). This class is implemented by extending [AbstractFeeDelegatedTransaction](#abstractfeedelegatedtransaction).

##### Variables

| Variable | Description |
| ----------- | ----------- |
| account: Account | An [Account](#account) instance that contains the information needed to update the given account. |

##### Methods

| Method | Description |
| ----------- | ----------- |
| decode(rlpEncoded: String): FeeDelegatedAccountUpdate | Decodes an RLP-encoded string of FeeDelegatedAccountUpdate and returns a `FeeDelegatedAccountUpdate` instance. It throws an exception if the decoding is failed. |
| create(tx: Object): FeeDelegatedAccountUpdate | Creates an instance of `FeeDelegatedAccountUpdate`. |
| getRLPEncoding(): String | Returns an RLP-encoded string of the transaction instance. It throws an exception if the variables required for encoding are not defined. |
| getCommonRLPEncodingForSignature(): String | Encodes the values needed to sign the transaction and returns the RLP-encoded string. It throws an exception if the variables required for encoding are not defined. |

#### FeeDelegatedSmartContractDeploy

`FeeDelegatedSmartContractDeploy` represents a [fee delegated smart contract deploy transaction](https://docs.klaytn.com/klaytn/design/transactions/fee-delegation#txtypefeedelegatedsmartcontractdeploy). This class is implemented by extending [AbstractFeeDelegatedTransaction](#abstractfeedelegatedtransaction).

##### Variables

| Variable | Description |
| ----------- | ----------- |
| to: String | An address to which the smart contract is deployed. Currently, this value cannot be defined by user, so it must be defined with the default value &quot;0x&quot;. Specifying the address will be supported in the future. |
| value: String | The amount of KLAY in peb to be transferred. If the value is not defined by user, the value is defined with the default value &quot;0x0&quot;. |
| input: String | Data attached to the transaction. It contains the byte code of the smart contract to be deployed and its arguments. |
| humanReadable: String | This must be &quot;false&quot; since human-readable address is not supported yet. If the value is not defined by user, it is defined with the default value &quot;false&quot;. |
| codeFormat: String | The code format of the smart contract. The supported value, for now, is &quot;EVM&quot; only. If it is not defined by user, it is defined with the default value &quot;EVM&quot;. This value is converted to a hex string after the assignment(e.g., EVM is converted to 0x0). |

##### Methods

| Method | Description |
| ----------- | ----------- |
| decode(rlpEncoded: String): FeeDelegatedSmartContractDeploy | Decodes an RLP-encoded string of FeeDelegatedSmartContractDeploy and returns a `FeeDelegatedSmartContractDeploy` instance. It throws an exception if the decoding is failed. |
| create(tx: Object): FeeDelegatedSmartContractDeploy | Creates an instance of `FeeDelegatedSmartContractDeploy`. |
| getRLPEncoding(): String | Returns an RLP-encoded string of the transaction instance. It throws an exception if the variables required for encoding are not defined. |
| getCommonRLPEncodingForSignature(): String | Encodes the values needed to sign the transaction and returns the RLP-encoded string. It throws an exception if the variables required for encoding are not defined. |

#### FeeDelegatedSmartContractExecution

`FeeDelegatedSmartContractExecution` represents a [fee delegated smart contract execution transaction](https://docs.klaytn.com/klaytn/design/transactions/fee-delegation#txtypefeedelegatedsmartcontractexecution). This class is implemented by extending [AbstractFeeDelegatedTransaction](#abstractfeedelegatedtransaction).

##### Variables

| Variable | Description |
| ----------- | ----------- |
| to: String | The address of the smart contract account to be executed. |
| value: String | The amount of KLAY in peb to be transferred. If the value is not defined by user, the value is defined with the default value &quot;0x0&quot;. |
| input: String | Data attached to the transaction, used for transaction execution. The input is an encoded string that indicates a function to call and parameters to be passed. |

##### Methods

| Method | Description |
| ----------- | ----------- |
| decode(rlpEncoded: String): FeeDelegatedSmartContractExecution | Decodes an RLP-encoded string of FeeDelegatedSmartContractExecution and returns a `FeeDelegatedSmartContractExecution` instance. It throws an exception if the decoding is failed. |
| create(tx: Object): FeeDelegatedSmartContractExecution | Creates an instance of `FeeDelegatedSmartContractExecution`. |
| getRLPEncoding(): String | Returns an RLP-encoded string of the transaction instance. It throws an exception if the variables required for encoding are not defined. |
| getCommonRLPEncodingForSignature(): String | Encodes the values needed to sign the transaction and returns the RLP-encoded string. It throws an exception if the variables required for encoding are not defined. |

#### FeeDelegatedCancel

`FeeDelegatedCancel` represents a [fee delegated cancel transaction](https://docs.klaytn.com/klaytn/design/transactions/fee-delegation#txtypefeedelegatedcancel). This class is implemented by extending [AbstractFeeDelegatedTransaction](#abstractfeedelegatedtransaction).

##### Variables

None

##### Methods

| Method | Description |
| ----------- | ----------- |
| decode(rlpEncoded: String): FeeDelegatedCancel | Decodes an RLP-encoded string of FeeDelegatedCancel and returns a `FeeDelegatedCancel` instance. It throws an exception if the decoding is failed. |
| create(tx: Object): FeeDelegatedCancel | Creates an instance of `FeeDelegatedCancel`. |
| getRLPEncoding(): String | Returns an RLP-encoded string of the transaction instance. It throws an exception if the variables required for encoding are not defined. |
| getCommonRLPEncodingForSignature(): String | Encodes the values needed to sign the transaction and returns the RLP-encoded string. It throws an exception if the variables required for encoding are not defined. |

#### FeeDelegatedChainDataAnchoring

`FeeDelegatedChainDataAnchoring` represents a [fee delegated chain data anchoring transaction](https://docs.klaytn.com/klaytn/design/transactions/fee-delegation#txtypefeedelegatedchaindataanchoring). This class is implemented by extending [AbstractFeeDelegatedTransaction](#abstractfeedelegatedtransaction).

##### Variables

| Variable | Description |
| ----------- | ----------- |
| input: String | Data to be anchored. |

##### Methods

| Method | Description |
| ----------- | ----------- |
| decode(rlpEncoded: String): FeeDelegatedChainDataAnchoring | Decodes an RLP-encoded string of FeeDelegatedChainDataAnchoring and returns a `FeeDelegatedChainDataAnchoring` instance. It throws an exception if the decoding is failed. |
| create(tx: Object): FeeDelegatedChainDataAnchoring | Creates an instance of `FeeDelegatedChainDataAnchoring`. |
| getRLPEncoding(): String | Returns an RLP-encoded string of the transaction instance. It throws an exception if the variables required for encoding are not defined. |
| getCommonRLPEncodingForSignature(): String | Encodes the values needed to sign the transaction and returns the RLP-encoded string. It throws an exception if the variables required for encoding are not defined. |

#### FeeDelegatedValueTransferWithRatio

`FeeDelegatedValueTransferWithRatio` represents a [fee delegated value transfer with ratio transaction](https://docs.klaytn.com/klaytn/design/transactions/partial-fee-delegation#txtypefeedelegatedvaluetransferwithratio). This class is implemented by extending [AbstractFeeDelegatedWithRatioTransaction](#abstractfeedelegatedwithratiotransaction).

##### Variables

| Variable | Description |
| ----------- | ----------- |
| to: String | The account address that will receive the transferred value. |
| value: String | The amount of KLAY in peb to be transferred. |

##### Methods

| Method | Description |
| ----------- | ----------- |
| decode(rlpEncoded: String): FeeDelegatedValueTransferWithRatio | Decodes an RLP-encoded string of FeeDelegatedValueTransferWithRatio and returns a `FeeDelegatedValueTransferWithRatio` instance. It throws an exception if the decoding is failed. |
| create(tx: Object): FeeDelegatedValueTransferWithRatio | Creates an instance of `FeeDelegatedValueTransferWithRatio`. |
| getRLPEncoding(): String | Returns an RLP-encoded string of the transaction instance. It throws an exception if the variables required for encoding are not defined. |
| getCommonRLPEncodingForSignature(): String | Encodes the values needed to sign the transaction and returns the RLP-encoded string. It throws an exception if the variables required for encoding are not defined. |

#### FeeDelegatedValueTransferMemoWithRatio

`FeeDelegatedValueTransferMemoWithRatio` represents a [fee delegated value transfer memo with ratio transaction](https://docs.klaytn.com/klaytn/design/transactions/partial-fee-delegation#txtypefeedelegatedvaluetransfermemowithratio). This class is implemented by extending [AbstractFeeDelegatedWithRatioTransaction](#abstractfeedelegatedwithratiotransaction).

##### Variables

| Variable | Description |
| ----------- | ----------- |
| to: String | The account address that will receive the transferred value. |
| value: String | The amount of KLAY in peb to be transferred. |
| input: String | Data attached to the transaction. The message can be passed to this attribute. |

##### Methods

| Method | Description |
| ----------- | ----------- |
| decode(rlpEncoded: String): FeeDelegatedValueTransferMemoWithRatio | Decodes an RLP-encoded string of FeeDelegatedValueTransferMemoWithRatio and returns a `FeeDelegatedValueTransferMemoWithRatio` instance. It throws an exception if the decoding is failed. |
| create(tx: Object): FeeDelegatedValueTransferMemoWithRatio | Creates an instance of `FeeDelegatedValueTransferMemoWithRatio`. |
| getRLPEncoding(): Stirng | Returns an RLP-encoded string of the transaction instance. It throws an exception if the variables required for encoding are not defined. |
| getCommonRLPEncodingForSignature(): String | Encodes the values needed to sign the transaction and returns the RLP-encoded string. It throws an exception if the variables required for encoding are not defined. |

#### FeeDelegatedAccountUpdateWithRatio

`FeeDelegatedAccountUpdateWithRatio` represents a [fee delegated account update with ratio transaction](https://docs.klaytn.com/klaytn/design/transactions/partial-fee-delegation#txtypefeedelegatedaccountupdatewithratio). This class is implemented by extending [AbstractFeeDelegatedWithRatioTransaction](#abstractfeedelegatedwithratiotransaction).

##### Variables

| Variable | Description |
| ----------- | ----------- |
| account: Account | An [Account](#account) instance that contains the information needed to update the given account. |

##### Methods

| Method | Description |
| ----------- | ----------- |
| decode(rlpEncoded: String): FeeDelegatedAccountUpdateWithRatio | Decodes an RLP-encoded string of FeeDelegatedAccountUpdateWithRatio and returns a `FeeDelegatedAccountUpdateWithRatio` instance. It throws an exception if the decoding is failed. |
| create(tx: Object): FeeDelegatedAccountUpdateWithRatio | Creates an instance of `FeeDelegatedAccountUpdateWithRatio`. |
| getRLPEncoding(): String | Returns an RLP-encoded string of the transaction instance. It throws an exception if the variables required for encoding are not defined. |
| getCommonRLPEncodingForSignature(): String | Encodes the values needed to sign the transaction and returns the RLP-encoded string. It throws an exception if the variables required for encoding are not defined. |

#### FeeDelegatedSmartContractDeployWithRatio

`FeeDelegatedSmartContractDeployWithRatio` represents a [fee delegated smart contract deploy with ratio transaction](https://docs.klaytn.com/klaytn/design/transactions/partial-fee-delegation#txtypefeedelegatedsmartcontractdeploywithratio). This class is implemented by extending [AbstractFeeDelegatedWithRatioTransaction](#abstractfeedelegatedwithratiotransaction).

##### Variables

| Variable | Description |
| ----------- | ----------- |
| to: String | An address to which the smart contract is deployed. Currently, this value cannot be defined by user, so it must be defined with the default value &quot;0x&quot;. Specifying the address will be supported in the future. |
| value: String | The amount of KLAY in peb to be transferred. If the value is not defined by user, the value must be defined with the default value &quot;0x0&quot;. |
| input: String | Data attached to the transaction. It contains the byte code of the smart contract to be deployed and its arguments. |
| humanReadable: String | This must be &quot;false&quot; since human-readable address is not supported yet. If the value is not defined by user, it is defined with the default value &quot;false&quot;. |
| codeFormat: String | The code format of smart contract code. The supported value, for now, is &quot;EVM&quot; only. If it is not defined by user, it is defined with the default value &quot;EVM&quot;. This value is converted to a hex string after the assignment(e.g., EVM is converted to 0x0). |

##### Methods

| Method | Description |
| ----------- | ----------- |
| decode(rlpEncoded: String): FeeDelegatedSmartContractDeployWithRatio | Decodes an RLP-encoded string of FeeDelegatedSmartContractDeployWithRatio and returns a `FeeDelegatedSmartContractDeployWithRatio` instance. It throws an exception if the decoding is failed. |
| create(tx: Object): FeeDelegatedSmartContractDeployWithRatio | Creates an instance of `FeeDelegatedSmartContractDeployWithRatio`. |
| getRLPEncoding(): String | Returns an RLP-encoded string of the transaction instance. It throws an exception if the variables required for encoding are not defined. |
| getCommonRLPEncodingForSignature(): String | Encodes the values needed to sign the transaction and returns the RLP-encoded string. It throws an exception if the variables required for encoding are not defined. |

#### FeeDelegatedSmartContractExecutionWithRatio

`FeeDelegatedSmartContractExecutionWithRatio` represents a [fee delegated smart contract execution with ratio transaction](https://docs.klaytn.com/klaytn/design/transactions/partial-fee-delegation#txtypefeedelegatedsmartcontractexecutionwithratio). This class is implemented by extending [AbstractFeeDelegatedWithRatioTransaction](#abstractfeedelegatedwithratiotransaction).

##### Variables

| Variable | Description |
| ----------- | ----------- |
| to: String | The address of the smart contract account to be executed. |
| value: String | The amount of KLAY in peb to be transferred. If the value is not defined by user, it is defined with the default value &quot;0x0&quot;. |
| input: String | Data attached to the transaction, used for transaction execution. The input is an encoded string that indicates a function to call and parameters to be passed. |

##### Methods

| Method | Description |
| ----------- | ----------- |
| decode(rlpEncoded: String): FeeDelegatedSmartContractExecutionWithRatio | Decodes an RLP-encoded string of FeeDelegatedSmartContractExecutionWithRatio and returns a `FeeDelegatedSmartContractExecutionWithRatio` instance. It throws an exception if the decoding is failed. |
| create(tx: Object): FeeDelegatedSmartContractExecutionWithRatio | Creates an instance of `FeeDelegatedSmartContractExecutionWithRatio`. |
| getRLPEncoding(): String | Returns an RLP-encoded string of the transaction instance. It throws an exception if the variables required for encoding are not defined. |
| getCommonRLPEncodingForSignature(): String | Encodes the values needed to sign the transaction and returns the RLP-encoded string. It throws an exception if the variables required for encoding are not defined. |

#### FeeDelegatedCancelWithRatio

`FeeDelegatedCancelWithRatio` represents a [fee delegated cancel with ratio transaction](https://docs.klaytn.com/klaytn/design/transactions/partial-fee-delegation#txtypefeedelegatedcancelwithratio). This class is implemented by extending [AbstractFeeDelegatedWithRatioTransaction](#abstractfeedelegatedwithratiotransaction).

##### Variables

None

##### Methods

| Method | Description |
| ----------- | ----------- |
| decode(rlpEncoded: String): FeeDelegatedCancelWithRatio | Decodes an RLP-encoded string of FeeDelegatedCancelWithRatio and returns a `FeeDelegatedCancelWithRatio` instance. It throws an exception if the decoding is failed. |
| create(tx: Object): FeeDelegatedCancelWithRatio | Creates an instance of `FeeDelegatedCancelWithRatio`. |
| getRLPEncoding(): String | Returns an RLP-encoded string of the transaction instance. It throws an exception if the variables required for encoding are not defined. |
| getCommonRLPEncodingForSignature(): String | Encodes the values needed to sign the transaction and returns the RLP-encoded string. It throws an exception if the variables required for encoding are not defined. |

#### FeeDelegatedChainDataAnchoringWithRatio

`FeeDelegatedChainDataAnchoringWithRatio` represents a [fee delegated chain data anchoring with ratio transaction](https://docs.klaytn.com/klaytn/design/transactions/partial-fee-delegation#txtypefeedelegatedchaindataanchoringwithratio). This class is implemented by extending [AbstractFeeDelegatedWithRatioTransaction](#abstractfeedelegatedwithratiotransaction).

##### Variables

| Variable | Description |
| ----------- | ----------- |
| input: String | Data to be anchored. |

##### Methods

| Method | Description |
| ----------- | ----------- |
| decode(rlpEncoded: String): FeeDelegatedChainDataAnchoringWithRatio | Decodes an RLP-encoded string of FeeDelegatedChainDataAnchoringWithRatio and returns a `FeeDelegatedChainDataAnchoringWithRatio` instance. It throws an exception if the decoding is failed. |
| create(tx: Object): FeeDelegatedChainDataAnchoringWithRatio | Creates an instance of `FeeDelegatedChainDataAnchoringWithRatio`. |
| getRLPEncoding(): String | Returns an RLP-encoded string of the transaction instance. It throws an exception if the variables required for encoding are not defined. |
| getCommonRLPEncodingForSignature(): String | Encodes the values needed to sign the transaction and returns the RLP-encoded string. It throws an exception if the variables required for encoding are not defined. |

#### TransactionDecoder

`TransactionDecoder` provides the function to decode an RLP-encoded string of a transaction class.

##### Variables

None

##### Methods

| Method | Description |
| ----------- | ----------- |
| decode(rlpEncoded: String): AbstractTransaction | Decodes an RLP-encoded transaction string and returns a transaction instance. It throws an exception if the decoding is failed. |

#### TransactionHasher

`TransactionHasher` provides the functions to calculate the hash of a transaction for signing.

##### Variables

None

##### Methods

| Method | Description |
| ----------- | ----------- |
| getHashForSignature(transaction: AbstractTransaction): String | Returns the hash of the transaction for the sender to sign. |
| getHashForFeePayerSignature(transaction: AbstractFeeDelegatedTransaction): String | Returns the hash of the transaction for the fee payer to sign. |

### RPC Layer

The `RPC` layer provides the functions to use the Node APIs. The `RPC` is a class that manages the Node API for each namespace. Node APIs currently provided by Caver are [klay] and [net].

![0811RPC](https://user-images.githubusercontent.com/32922423/89860609-d6220d80-dbde-11ea-85f6-ea3fc6f47991.png)

`Klay` is a class that provides [klay namespace of the Node API]. `Net` is a class that provides [net namespace of the Node API]. The result value received from a Klaytn Node is returned to the user. For more information about each API and the returned result, refer to [JSON-RPC APIs].

#### Klay

`Klay` provides JSON-RPC call with &quot;klay&quot; namespace.

##### Variables

None

##### Methods

| Method | Description |
| ----------- | ----------- |
| accountCreated(address: String): Boolean | Call `klay_accountCreated` JSON-RPC. |
| accountCreated(address: String, blockNumber: int): Boolean | Call `klay_accountCreated` JSON-RPC. |
| accountCreated(address: String, blockTag: String): Boolean | Call `klay_accountCreated` JSON-RPC. |
| getAccounts(): List&amp;#60;String&amp;#62; | Call `klay_accounts` JSON-RPC. |
| encodeAccountKey(accountKey: AccountKey): String | Call `klay_encodeAccountKey` JSON-RPC. |
| decodeAccountKey(encodedAccountKey: String): Object | Call `klay_decodeAccountKey` JSON-RPC. |
| getAccount(address: String): Object | Call `klay_getAccount` JSON-RPC. |
| getAccount(address: String, blockNumber: int): Object | Call `klay_getAccount` JSON-RPC. |
| getAccount(address: String, blockTag: String): Object | Call `klay_getAccount` JSON-RPC. |
| getAccountKey(address: String): Object | Call `klay_getAccountKey` JSON-RPC. |
| getAccountKey(address: String, blockNumber: int): Object | Call `klay_getAccountKey` JSON-RPC. |
| getAccountKey(address: String, blockTag: String): Object | Call `klay_getAccountKey` JSON-RPC. |
| getBalance(address: String): String | Call `klay_getBalance` JSON-RPC. |
| getBalance(address: String, blockNumber: int): String | Call `klay_getBalance` JSON-RPC. |
| getBalance(address: String, blockTag: String): String | Call `klay_getBalance` JSON-RPC. |
| getCode(address: String): String | Call `klay_getCode` JSON-RPC. |
| getCode(address: String, blockNumber: int): String | Call `klay_getCode` JSON-RPC. |
| getCode(address: String, blockTag: String): String | Call `klay_getCode` JSON-RPC. |
| getTransactionCount(address: String): String | Call `klay_getTransactionCount` JSON-RPC. |
| getTransactionCount(address: String, blockNumber: int): String | Call `klay_getTransactionCount` JSON-RPC. |
| getTransactionCount(address: String, blockTag: String): String | Call `klay_getTransactionCount` JSON-RPC. |
| isContractAccount(address: String): Boolean | Call `klay_isContractAccount` JSON-RPC. |
| isContractAccount(address: String, blockNumber: int): Boolean | Call `klay_isContractAccount` JSON-RPC. |
| isContractAccount(address: String, blockTag: String): Boolean | Call `klay_isContractAccount` JSON-RPC. |
| sign(address: String, message: String): String | Call `klay_sign` JSON-RPC. |
| getBlockNumber(): String | Call `klay_blockNumber` JSON-RPC. |
| getBlockByNumber(blockNumber: int): Object | Call `klay_getBlockByNumber` JSON-RPC. |
| getBlockByNumber(blockNumber: int, fullTxs: Boolean): Object | Call `klay_getBlockByNumber` JSON-RPC. |
| getBlockByNumber(blockTag: String): Object | Call `klay_getBlockByNumber` JSON-RPC. |
| getBlockByNumber(blockTag: String, fullTxs: Boolean): Object | Call `klay_getBlockByNumber` JSON-RPC. |
| getBlockByHash(blockHash: String): Object | Call `klay_getBlockByHash` JSON-RPC. |
| getBlockByHash(blockHash: String, fullTxs: Boolean): Object | Call `klay_getBlockByHash` JSON-RPC. |
| getBlockReceipts(blockHash: String): Object | Call `klay_getBlockReceipts` JSON-RPC. |
| getBlockTransactionCountByNumber(blockNumber: int): Object | Call `klay_getBlockTransactionCountByNumber` JSON-RPC. |
| getBlockTransactionCountByNumber(blockTag: String): Object | Call `klay_getBlockTransactionCountByNumber` JSON-RPC. |
| getBlockTransactionCountByHash(blockHash: String): Object | Call `klay_getBlockTransactionCountByHash` JSON-RPC. |
| getBlockWithConsensusInfoByHash(blockHash: String): Object | Call `klay_getBlockWithConsensusInfoByHash` JSON-RPC. |
| getBlockWithConsensusInfoByNumber(blockNumber: int): Object | Call `klay_getBlockWithConsensusInfoByNumber` JSON-RPC. |
| getBlockWithConsensusInfoByNumber(blockTag: String): Object | Call `klay_getBlockWithConsensusInfoByNumber` JSON-RPC. |
| getCommittee(): List&amp;#60;String&amp;#62; | Call `klay_getCommittee` JSON-RPC. |
| getCommittee(blockNumber: int): List&amp;#60;String&amp;#62; | Call `klay_getCommittee` JSON-RPC. |
| getCommittee(blockTag: String): List&amp;#60;String&amp;#62; | Call `klay_getCommittee` JSON-RPC. |
| getCommitteeSize(): int | Call `klay_getCommitteeSize` JSON-RPC. |
| getCommitteeSize(blockNumber: int): int | Call `klay_getCommitteeSize` JSON-RPC. |
| getCommitteeSize(blockTag: String): int | Call `klay_getCommitteeSize` JSON-RPC. |
| getCouncil(): List&amp;#60;String&amp;#62; | Call `klay_getCouncil` JSON-RPC. |
| getCouncil(blockNumber: int): List&amp;#60;String&amp;#62; | Call `klay_getCouncil` JSON-RPC. |
| getCouncil(blockTag: String): List&amp;#60;String&amp;#62; | Call `klay_getCouncil` JSON-RPC. |
| getCouncilSize(): int | Call `klay_getCouncilSize` JSON-RPC. |
| getCouncilSize(blockNumber: int): int | Call `klay_getCouncilSize` JSON-RPC. |
| getCouncilSize(blockTag: String): int | Call `klay_getCouncilSize` JSON-RPC. |
| getStorageAt(address: String, position: int, blockNumber: int): String | Call `klay_getStorageAt` JSON-RPC. |
| getStorageAt(address: String, position: int, blockTag: String): String | Call `klay_getStorageAt` JSON-RPC. |
| isSyncing(): Object | Call `klay_syncing` JSON-RPC. |
| call(callObject: Object): String | Call `klay_call` JSON-RPC. |
| call(callObject: Object, blockNumber: int): String | Call `klay_call` JSON-RPC. |
| call(callObject: Object, blockTag: String): String | Call `klay_call` JSON-RPC. |
| estimateGas(callObject: Object): String | Call `klay_estimateGas` JSON-RPC. |
| estimateComputationCost(callObject: Object): String | Call `klay_estimateComputationCost` JSON-RPC. |
| estimateComputationCost(callObject: Object, blockNumber: int): String | Call `klay_estimateComputationCost` JSON-RPC. |
| estimateComputationCost(callObject: Object, blockTag: String): String | Call `klay_estimateComputationCost` JSON-RPC. |
| getTransactionByBlockHashAndIndex(hash: String, index: int): Object | Call `klay_getTransactionByBlockHashAndIndex` JSON-RPC. |
| getTransactionByBlockNumberAndIndex(blockNumber: int, index: int): Object | Call `klay_getTransactionByBlockHashAndIndex` JSON-RPC. |
| getTransactionByBlockNumberAndIndex(blockTag: String, index: int): Object | Call `klay_getTransactionByBlockHashAndIndex` JSON-RPC. |
| getTransactionByHash(hash: String): Object | Call `klay_getTransactionByHash` JSON-RPC. |
| getTransactionBySenderTxHash(senderTxHash: String): Object | Call `klay_getTransactionBySenderTxHash` JSON-RPC. |
| getTransactionReceipt(hash: String): Object | Call `klay_getTransactionReceipt` JSON-RPC. |
| getTransactionReceiptBySenderTxHash(senderTxHash: String): Object | Call `klay_getTransactionReceiptBySenderTxHash` JSON-RPC. |
| sendRawTransaction(rawTransaction: String): Object | Call `klay_sendRawTransaction` JSON-RPC. |
| sendTransaction(tx: AbstractTransaction): String | Call `klay_sendTransaction` JSON-RPC. |
| sendTransactionAsFeePayer(tx: AbstractFeeDelegatedTransaction): String | Call `klay_sendTransactionAsFeePayer` JSON-RPC. |
| signTransaction(tx: AbstractTransaction): Object | Call `klay_signTransaction` JSON-RPC. |
| signTransactionAsFeePayer(tx: AbstractFeeDelegatedTransaction): Object | Call `klay_signTransactionAsFeePayer` JSON-RPC. |
| getDecodedAnchoringTransaction(hash: String): Object | Call `klay_getDecodedAnchoringTransactionByHash` JSON-RPC. |
| getChainId(): String | Call `klay_chainID` JSON-RPC. |
| getClientVersion(): String | Call `klay_clientVersion` JSON-RPC. |
| getGasPrice(): String | Call `klay_gasPrice` JSON-RPC. |
| getGasPriceAt(): String | Call `klay_gasPriceAt` JSON-RPC. |
| getGasPriceAt(blockNumber: int): String | Call `klay_gasPriceAt` JSON-RPC. |
| isParallelDBWrite(): Boolean | Call `klay_isParallelDBWrite` JSON-RPC. |
| isSenderTxHashIndexingEnabled(): Boolean | Call `klay_isSenderTxHashIndexingEnabled` JSON-RPC. |
| getProtocolVersion(): String | Call `klay_protocolVersion` JSON-RPC. |
| getRewardbase(): String | Call `klay_rewardbase` JSON-RPC. |
| writeThroughCaching(): Boolean | Call `klay_writeThroughCaching` JSON-RPC. |
| getFilterChanges(filterId: String): List&amp;#60;Object&amp;#62; | Call `klay_getFilterChanges` JSON-RPC. |
| getFilterLogs(filterId: String): List&amp;#60;Object&amp;#62; | Call `klay_getFilterLogs` JSON-RPC. |
| getLogs(filterOption: Object): List&amp;#60;Object&amp;#62; | Call `klay_getLogs` JSON-RPC. |
| newBlockFilter(): String | Call `klay_newBlockFilter` JSON-RPC. |
| newFilter(filterOptions: Object): String | Call `klay_newFilter` JSON-RPC. |
| newPendingTransactionFilter(): String | Call `klay_newPendingTransactionFilter` JSON-RPC. |
| uninstallFilter(filterId: String): Boolean | Call `klay_uninstallFilter` JSON-RPC. |
| sha3(data: String): String | Call `klay_sha3` JSON-RPC. |

#### Net

`Net` provides JSON-RPC call with &quot;net&quot; namespace.

##### Variables

None

##### Methods

| Method | Description |
| ----------- | ----------- |
| getNetworkID(): String | Call `net_networkID` JSON-RPC. |
| isListening(): Boolean | Call `net_listening` JSON-RPC. |
| getPeerCount(): String | Call `net_peerCount` JSON-RPC. |
| getPeerCountByType(): Object | Call `net_peerCountByType` JSON-RPC. |

### Contract, ABI, KCT Layer

The `Contract` layer provides the functions to interact with smart contracts on Klaytn. This Contract layer uses the function of the `ABI` layer that provides the functions to encode and decode parameters with the ABI (Application Binary Interface). `KCT` is a layer that provides the functions to interact with KCT token contracts (i.e., [KIP-7] or [KIP-17]) on Klaytn.

![contractkctabi](https://user-images.githubusercontent.com/32922423/115336517-5a02af80-a1da-11eb-8d82-ea6ec32b6bfc.png)

The `Contract` class makes it easy to interact with smart contracts based on ABI. If you have the byte code and constructor parameters, you can use the `Contract` instance to deploy the smart contract to Klaytn. The class can process the ABI so that the user can easily call the smart contract function through a member variable called `methods`.

The `ABI` class provides functions to encode and decode parameters. The `Contract` class encodes and decodes the parameters required for smart contract deployment and execution using the functions provided by the ABI. To create a transaction to deploy or execute a smart contract, the required data can be filled with `ABI`.

The `KIP7` class provides the functions to interact with [KIP-7] token contracts on Klaytn. This class allows users to easily
deploy and execute [KIP-7] token contracts on Klaytn. `KIP7` maps all functions defined in [KIP-7] and provides them as class methods.

The `KIP17` class provides the functions to interact with [KIP-17] token contracts on Klaytn. This class allows users to easily
deploy and execute [KIP-17] token contracts on Klaytn. `KIP17` maps all functions defined in [KIP-17] and provides them as class methods.

More token standards defined in [KCT](http://kips.klaytn.com/token) can be implemented here. For other KCT implementations can be found in the SDK references ([caver-js](https://docs.klaytn.com/bapp/sdk/caver-js/api-references/caver.kct), [caver-java](https://javadoc.io/doc/com.klaytn.caver/core/latest/index.html)).

#### Contract

`Contract` is a class that allows users to easily interact with smart contracts on Klaytn. It can deploy a smart contract to Klaytn or execute a smart contract deployed on Klaytn.

##### Variables

| Variable | Description |
| ----------- | ----------- |
| address: String | The address of the smart contract to call. If the smart contract has already been deployed to Klaytn, users can specify the address of the smart contract to be called. This value is set after the contract deployment is successfully performed. |
| abi: List&amp;#60;Object&amp;#62; | The abi of the smart contract to interact with. |
| methods: Map&amp;#60;String:[ContractMethod](#contractmethod)&amp;#62; | The methods of the smart contract. When a contract receives an abi from the user, it parses the abi, makes functions that can be called into `ContractMethod`, and stores them into this variable. |
| events: Map&amp;#60;String:[ContractEvent](#contractevent)&amp;#62; | The events of the smart contract. When a contract receives an abi from the user, it parses the abi, makes events that can be fired into `ContractEvent`, and stores them into this variable. |
| defaultSendOptions: SendOptions | An object that contains information to be used as default values when a user sends a transaction that changes the state of a smart contract. The values (`from`, `gas`, and `value`) can be optionally defined in SendOptions. When a user calls a method to send a transaction, the user can optionally define sendOptions. If the user defines sendOptions separately when calling the function, the parameter in the function call has the higher priority. The conflicting attributes in this variable will be ignored in that case. |

##### Methods

| Method | Description |
| ----------- | ----------- |
| create(String abi): Contract | Creates an instance of `Contract`. |
| create(String abi, String contractAddress): Contract | Creates an instance of `Contract` with the given deployed address. |
| deploy(deployParam: [ContractDeployParams](#contractdeployparams), options: SendOptions): Contract | Deploys the contract to Klaytn. |
| once(event: String, callback: Function): void | Subscribes to an event and unsubscribes immediately after the first event or error. |
| once(event: String, options: Object, callback: Function): void | Subscribes to an event and unsubscribes immediately after the first event or error. The options object should define `filter` or `topics`. |
| getPastEvent(event: String): List&amp;#60;Object&amp;#62; | Gets past events for this contract. |
| getPastEvent(event: String, options: Object): List&amp;#60;Object&amp;#62; | Gets past events for this contract. The options object can define the filter options needed when calling [klay_logs]. See [klay_logs] for more details about options. |
| getPastEvent(event: String, callback: Function): List&amp;#60;Object&amp;#62; | Gets past events for this contract. |
| getPastEvent(event: String, options: Object, callback: Function): List&amp;#60;Object&amp;#62; | Gets past events for this contract. The options object can define the filter options needed when calling [klay_logs]. See [klay_logs] for more details about options. |

#### ContractMethod

`ContractMethod` is a class that contains abi information of a smart contract function.

##### Variables

| Variable | Description |
| ----------- | ----------- |
| name: String | The name of the function in a smart contract. |
| inputs: List&amp;#60;[ContractIOType](#contractiotype)&amp;#62; | The input values of the function. In the list, each parameter of the function is defined as ContractIOType. When the `call` or `send` function is called, this is used to encode the parameter to create the input field of a transaction. |
| outputs: List&amp;#60;[ContractIOType](#contractiotype)&amp;#62; | The output values of the function. This is used to decode the value returned as the result of executing the function. |
| signature: String | The [function signature](https://docs.klaytn.com/bapp/sdk/caver-js/api-references/caver.contract#cf-function-signature-function-selector) (function selector). The first four bytes of the input data for specifying the function to be called (or executed). It is the first (left, high-order in big-endian) four bytes of the Keccak-256 (SHA-3) hash of the signature of the function. |
| nextMethods: List&amp;#60;ContractMethod&amp;#62; | nextMethods stores functions with the same name implemented in a smart contract. If the parameter passed by the user is different from the input of this contractMethod, it traverses the contractMethods defined in nextMethods to find the contractMethod to be called. |

##### Methods

| Method | Description |
| ----------- | ----------- |
| call(arguments: List&amp;#60;any&amp;#62;, callObject: Object): any | Calls a &quot;constant&quot; method and execute its smart contract method in the Klaytn Virtual Machine without sending any transaction. See [klay_call] for more details about callObject. |
| send(arguments: List&amp;#60;any&amp;#62;, options: SendOptions): Object | Sends a transaction to the smart contract and executes its method. This can alter the smart contract state. It sends a transaction using the value defined in defaultSendOptions of the Contract. It throws an exception if the value required for transaction (i.e `from` or `gas`) is not defined in defaultSendOptions. |
| send(arguments: List&amp;#60;any&amp;#62;): Object | Send a transaction to the smart contract and execute its method. This can alter the smart contract state. |
| encodeABI(arguments: List&amp;#60;any&amp;#62;): String | Generates data to be filled in the `input` field of a transaction. This can be used to send a transaction, call a method, or pass it into another smart contract method as arguments. |
| estimateGas(arguments: List&amp;#60;any&amp;#62;, callObject: Object): String | Estimates the gas that a method execution will take when executed in the Klaytn Virtual Machine. See [klay_call] for more details about callObject. |

#### ContractEvent

`ContractEvent` is a class that contains abi information of a smart contract event.

##### Variables

| Variable | Description |
| ----------- | ----------- |
| name: String | The name of the event in the smart contract. |
| inputs: List&amp;#60;[ContractIOType](#contractiotype)&amp;#62; | The input values of the event. In the list, each input of the event is defined as ContractIOType. This value is used to convert the parameter to a topic. |
| signature: String | The event signature which is the sha3 hash of the event name including input parameter types. |

##### Methods

None

#### ContractIOType

`ContractIOType` is a class used when defining the input and output of the smart contract.

##### Variables

| Variable | Description |
| ----------- | ----------- |
| name: String | The name of the value. |
| type: String | The type of the value. |
| indexed: Boolean | Whether indexed or not, the input values of the contract event are separately defined as indexed. If indexed is not separately used, there is no need to define this. |

##### Methods

None

#### SendOptions

`SendOptions` is a class that defines the values required when sending a transaction. When executing a method that triggers a transaction, the user can use it to define from, gas or value.

##### Variables

| Variable | Description |
| ----------- | ----------- |
| from: String | The address of the sender. |
| gas: String | The maximum amount of transaction fee that the transaction is allowed to use. |
| value: String | The value in peb to be transferred to the address of the smart contract by this transaction. |

##### Methods

None

#### ContractDeployParams

`ContractDeployParams` is a class that defines the byte code and constructor parameters required when deploying a smart contract.

##### Variables

| Variable | Description |
| ----------- | ----------- |
| byteCode: String | The byte code of the contract. |
| args: List&amp;#60;any&amp;#62; | The arguments that get passed to the constructor on deployment. |

##### Methods

None

#### ABI

`ABI` provides the functions to encode/decode parameters with ABI.

##### Variables

None

##### Methods

| Method | Description |
| ----------- | ----------- |
| encodeFunctionSignature(method: ContractMethod): String | Encodes the function signature to its ABI signature, which are the first 4 bytes of the sha3 hash of the function name with its parameter types. |
| encodeFunctionSignature(functionString: String): String | Encodes the function signature to its ABI signature, which are the first 4 bytes of the sha3 hash of the function name with its parameter types. |
| encodeEventSignature(event: ContractEvent): String | Encodes the event signature to its ABI signature, which is the sha3 hash of the event name with its parameter types. |
| encodeEventSignature(eventString: String): String | Encodes the event signature to its ABI signature, which is the sha3 hash of the event name with its parameter types. |
| encodeParameter(type: String, param: any): String | Encodes a parameter based on its type to its ABI representation. It throws an exception if the param is invalid. |
| encodeParameters(types: List&amp;#60;String&amp;#62;, params: List&amp;#60;any&amp;#62;): String | Encodes parameters based on its type to its ABI representation. It throws an exception if the params are invalid. |
| encodeFunctionCall(method: ContractMethod, params: List&amp;#60;any&amp;#62;): String | Encodes a function call using its JSON interface object and given parameters. It throws an exception if the params are invalid. |
| decodeParameter(type: String, encoded: String): String | Decodes an ABI-encoded parameter. It throws an exception if the decoding is failed. |
| decodeParameters(types: List&amp;#60;String&amp;#62;, encoded: String): List&amp;#60;String&amp;#62; | Decodes ABI-encoded parameters. It throws an exception if the decoding is failed. |
| decodeParameters(method: ContractMethod, encoded: String): List&amp;#60;String&amp;#62; | Decodes ABI-encoded parameters. It throws an exception if the decoding is failed. |
| decodeLog(inputs: List&amp;#60;ContractIOType&amp;#62;, data: String, topics: List&amp;#60;String&amp;#62;): JSONObject | Decodes ABI-encoded log data and indexed topic data. It throws an exception if the decoding is failed. |
| encodeContractDeploy(constructor: ContractMethod, byteCode: String, params: List&amp;#60;any&amp;#62;): String | Encodes smart contract bytecode with the arguments of the constructor for smart contract deployment. |

#### KIP7

`KIP7` is a class to easily interact with Klaytn&apos;s KIP-7 token contract. This is implemented by extending [Contract](#contract).

##### Variables

None

##### Methods

| Method | Description |
| ----------- | ----------- |
| deploy(tokenInfo: [KIP7DeployParams](#kip7deployparams), deployer: String): KIP7 | Deploys the KIP-7 token contract to the Klaytn. |
| create(): KIP7 | Creates an instance of `KIP7`. |
| create(String contractAddress): KIP7 | Creates an instance of `KIP7` with the given deployed address. |
| clone(): KIP7 | Clones the current KIP7 instance. |
| clone(tokenAddress: address): KIP7 | Clones the current KIP7 instance and sets the address of the new instance to `tokenAddress` . |
| supportInterface(interfaceid: String): Boolean | Returns true if this contract implements the interface defined by `interfaceId`. |
| name(): String | Returns the name of the token. |
| symbol(): String | Returns the symbol of the token. |
| decimals(): int | Returns the number of decimal places the token uses. |
| totalSupply(): BigInteger | Returns the total token supply. |
| balanceOf(account: String): BigInteger | Returns the balance of the given account address. |
| allowance(owner: String, spender: String): BigInteger | Returns the number of tokens that `spender` is allowed to withdraw tokens of `owner`. |
| isMinter(account: String): Boolean | Returns true if the given account is a minter who can issue new KIP7 tokens. |
| isPauser(account: String): Boolean | Returns true if the given account is a pauser who can suspend transferring tokens. |
| paused(): Boolean | Returns true if the contract is paused, and false otherwise. |
| approve(spender: String, amount: BigInteger): Object | Sets the amount of the tokens of the token owner to be spent by the spender. |
| approve(spender: String, amount: BigInteger, sendParam: SendOptions): Object | Sets the amount of the tokens of the token owner to be spent by the spender. The transaction is formed based on `sendParam`. |
| transfer(recipient: String, amount: BigInteger): Object | Transfers the given amount of the token from the token owner&apos;s balance to the recipient. |
| transfer(recipient: String, amount: BigInteger, sendParam: SendOptions): Object | Transfers the given amount of the token from the token owner&apos;s balance to the recipient. The transaction is formed based on `sendParam`. |
| transferFrom(sender: String, recipient: String, amount: BigInteger): Object | Transfers the given amount of the token from the token owner&apos;s balance to the recipient. The address who was approved to send the token owner&apos;s tokens is expected to execute this token transferring transaction. |
| transferFrom(sender: String, recipient: String, amount: BigInteger, sendParam: SendOptions): Object | Transfers the given amount of the token from the token owner&apos;s balance to the recipient. The address who was approved to send the token owner&apos;s tokens is expected to execute this token transferring transaction. The transaction is formed based on `sendParam`. |
| safeTransfer(recipient: String, amount: BigInteger): Object | Safely transfers the given amount of the token from the token owner&apos;s balance to the recipient. |
| safeTransfer(recipient: String, amount: BigInteger, sendParam: SendOptions): Object | Safely transfers the given amount of the token from the token owner&apos;s balance to the recipient. The transaction is formed based on `sendParam`. |
| safeTransfer(recipient: String, amount: BigInteger, data: String): Object | Safely transfers the given amount of the token from the token owner&apos;s balance to the recipient with additional data. |
| safeTransfer(recipient: String, amount: BigInteger, data: String, sendParam: SendOptions): Object | Safely transfers the given amount of the token from the token owner&apos;s balance to the recipient with additional data. The transaction is formed based on `sendParam`. |
| safeTransferFrom(sender: String, recipient: String, amount: BigInteger): Object | Safely transfers the given amount of the token from the token owner&apos;s balance to the recipient. The address who was approved to send the token owner&apos;s tokens is expected to execute this token transferring transaction. |
| safeTransferFrom(sender: String, recipient: String, amount: BigInteger, sendParam: SendOptions): Object | Safely transfers the given amount of the token from the token owner&apos;s balance to the recipient. The address who was approved to send the token owner&apos;s tokens is expected to execute this token transferring transaction. The transaction is formed based on `sendParam`. |
| safeTransferFrom(sender: String, recipient: String, amount: BigInteger, data: String): Object | Safely transfers the given amount of the token from the token owner&apos;s balance to the recipient with additional data. The address who was approved to send the token owner&apos;s tokens is expected to execute this token transferring transaction. |
| safeTransferFrom(sender: String, recipient: String, amount: BigInteger, data: String, sendParam: SendOptions): Object | Safely transfers the given amount of the token from the token owner&apos;s balance to the recipient with additional data. The address who was approved to send the token owner&apos;s tokens is expected to execute this token transferring transaction. The transaction is formed based on `sendParam`. |
| mint(account: String, amount: BigInteger): Object | Creates the number of tokens and issues them to the account, increasing the total supply of tokens. |
| mint(account: String, amount: BigInteger, sendParam: SendOptions): Object | Creates the number of tokens and issues them to the account, increasing the total supply of tokens. The transaction is formed based on `sendParam`. |
| addMinter(account: String): Object | Adds an account as a minter, who are permitted to mint tokens. |
| addMinter(account: String, sendParam: SendOptions): Object | Adds an account as a minter, who are permitted to mint tokens. The transaction is formed based on `sendParam`. |
| renounceMinter(): Object | Renounces the right to mint tokens. Only a minter can renounce the minting right. |
| renounceMinter(sendParam: SendOptions): Object | Renounces the right to mint tokens. Only a minter can renounce the minting right. The transaction is formed based on `sendParam`. |
| burn(amount: BigInteger): Object | Destroys the number of tokens in the sender&apos;s balance. |
| burn(amount: BigInteger, sendParam: SendOptions): Object | Destroys the number of tokens in the sender&apos;s balance. The transaction is formed based on `sendParam`. |
| burnFrom(account: String, amount: BigInteger): Object | Destroys the given number of tokens from `account`. The address who was approved to use the token owner&apos;s tokens is expected to execute this token burning transaction. |
| burnFrom(account: String, amount: BigInteger, sendParam: SendOptions): Object | Destroys the given number of tokens from `account`. The address who was approved to use the token owner&apos;s tokens is expected to execute this token burning transaction. The transaction is formed based on `sendParam`. |
| addPauser(account: String): Object | Adds an account as a pauser that has the right to suspend the contract. |
| addPauser(account: String, sendParam: SendOptions): Object | Adds an account as a pauser that has the right to suspend the contract. The transaction is formed based on `sendParam`. |
| pause(): Object | Suspends functions related to sending tokens. |
| pause(sendParam: SendOptions): Object | Suspends functions related to sending tokens. The transaction is formed based on `sendParam`. |
| unpause(): Object | Resumes the paused contract. |
| unpause(sendParam: SendOptions): Object | Resumes the paused contract. The transaction is formed based on `sendParam`. |
| renouncePauser(): Object | Renounces the right to pause the contract. Only a pauser address can renounce the pausing right. |
| renouncePauser(sendParam: SendOptions): Object | Renounces the right to pause the contract. Only a pauser address can renounce the pausing right. The transaction is formed based on `sendParam`. |

#### KIP7DeployParams

`KIP7DeployParams` is a class that defines the token information required when deploying a KIP-7 token contract.

##### Variables

| Variable | Description |
| ----------- | ----------- |
| name: String | The name of the token. |
| symbol: String | The symbol of the token. |
| decimals: int | The number of decimal places the token uses. |
| initialSupply: BigInteger | The total amount of token to be supplied initially. |

##### Methods

None

#### KIP17

`KIP17` is a class to easily interact with Klaytn&apos;s KIP-17 token contract. This is implemented by extending [Contract](#contract).

##### Variables

None

##### Methods

| Method | Description |
| ----------- | ----------- |
| deploy(tokenInfo: KIP17DeployParams, deployer: String): KIP17 | Deploys the KIP-17 token contract to Klaytn. |
| create(): KIP17 | Creates an instance of `KIP17`. |
| create(String contractAddress): KIP17 | Creates an instance of `KIP17` with the given deployed address. |
| clone(): KIP17 | Clones the current KIP17 instance. |
| clone(tokenAddress: String): KIP17 | Clones the current KIP17 instance and sets address of the new contract instance to `tokenAddress` . |
| supportInterface(interfaceId: String): Boolean | Returns true if this contract implements the interface defined by `interfaceId`. |
| name(): String | Returns the name of the token. |
| symbol(): String | Returns the symbol of the token. |
| tokenURI(tokenId: String): String | Returns the URI for a given token id. |
| totalSupply(): BigInteger | Returns the total number of tokens minted by the contract. |
| tokenOwnerByIndex(owner: String, index: BigInteger): BigInteger | Searches the owner&apos;s token list for the given index, and returns the token id of a token positioned at the matched index in the list if there is a match. |
| tokenByIndex(index: BigInteger): BigInteger | Searches the list of all tokens in this contract for the given index, and returns the token id of a token positioned at the matched index in the list if there is a match. |
| balanceOf(account: String): BigInteger | Returns the balance of the given account address. |
| ownerOf(tokenId: BigInteger): String | Returns the address of the owner of the specified token id. |
| getApproved(tokenId: BigInteger): Boolean | Returns the address who was permitted to transfer this token, or &apos;zero&apos; address, if no address was approved. |
| isApprovedForAll(owner: String, operator: String): Boolean | Returns true if an operator is approved to transfer all tokens that belong to the owner. |
| isMinter(account: String): Boolean | Returns true if the given account is a minter who can issue new tokens in the current contract conforming to KIP-17. |
| paused(): Boolean | Returns true if the contract is paused. It returns false otherwise. |
| isPauser(account: String): Boolean | Returns true if the given account is a pauser who can suspend transferring tokens. |
| approve(to: String, tokenId: BigInteger): Object | Approves another address to transfer a token of the given token id. |
| approve(to: String, tokenId: BigInteger, sendParam: SendOptions): Object | Approves another address to transfer a token of the given token id. The transaction is formed based on `sendParam`. |
| setApprovalForAll(to: String, approved: Boolean): Object | Approves or disallows the given operator to transfer all tokens of the owner based on `approved`. |
| setApprovalForAll(to: String, approved: Boolean, sendParam: SendOptions): Object | Approves or disallows the given operator to transfer all tokens of the owner based on `approved`. The transaction is formed based on `sendParam`. |
| transferFrom(from: String, to: String, tokenId: BigInteger): Object | Transfers the token specified by `tokenId` from the owner to `to`. The address who was approved to send the token owner&apos;s token (the operator) or the token owner itself is expected to execute this token transferring transaction. |
| transferFrom(from: String, to: String, tokenId: BigInteger, sendParam: SendOptions): Object | Transfers the token specified by `tokenId` from the owner to `to`. The address who was approved to send the token owner&apos;s token (the operator) or the token owner itself is expected to execute this token transferring transaction. The transaction is formed based on `sendParam`. |
| safeTransferFrom(from: String, to: String, tokenId: BigInteger): Object | Safely transfers the token specified by `tokenId` from the owner to `to`. The address who was approved to send the token owner&apos;s token (the operator) or the token owner itself is expected to execute this token transferring transaction. |
| safeTransferFrom(from: String, to: String, tokenId: BigInteger, sendParam: SendOptions): Object | Safely transfers the token specified by `tokenId` from the owner to `to`. The address who was approved to send the token owner&apos;s token (the operator) or the token owner itself is expected to execute this token transferring transaction. The transaction is formed based on `sendParam`. |
| safeTransferFrom(from: String, to: String, tokenId: BigInteger, data: String): Object | Safely transfers the token specified by `tokenId` from the owner to `to` with additional `data`. The address who was approved to send the token owner&apos;s token (the operator) or the token owner itself is expected to execute this token transferring transaction. |
| safeTransferFrom(from: String, to: String, tokenId: BigInteger, data: String, sendParam: SendOptions): Object | Safely transfers the token specified by `tokenId` from the owner to `to` with additional `data`. The address who was approved to send the token owner&apos;s token (the operator) or the token owner itself is expected to execute this token transferring transaction. The transaction is formed based on `sendParam`. |
| addMinter(account: String): Object | Adds an account as a minter, who are permitted to mint tokens. |
| addMinter(account: String, sendParam: SendOptions): Object | Adds an account as a minter, who are permitted to mint tokens. The transaction is formed based on `sendParam`. |
| renounceMinter(): Object | Renounces the right to mint tokens. Only a minter address can renounce the minting right. |
| renounceMinter(sendParam: SendOptions): Object | Renounces the right to mint tokens. Only a minter address can renounce the minting right. The transaction is formed based on `sendParam`. |
| mint(to: String, tokenId: BigInteger): Object | Creates a token and assigns it to the given account `to`. This method increases the total supply of the contract. |
| mint(to: String, tokenId: BigInteger, sendParam: SendOptions): Object | Creates a token and assigns it to the given account `to`. This method increases the total supply of the contract. The transaction is formed based on `sendParam`. |
| mintWithTokenURI(to: String, tokenId: BigInteger, tokenURI: String): Object | Creates a token with the given uri and assigns it to the given account `to`. This method increases the total supply of the contract. |
| mintWithTokenURI(to: String, tokenId: BigInteger, tokenURI: String, sendParam: SendOptions): Object | jCreates a token with the given uri and assigns it to the given account `to`. This method increases the total supply of the contract. The transaction is formed based on `sendParam`. |
| burn(tokenId: BigInteger): Object | Destroys the token of the given token id. |
| burn(tokenId: BigInteger, sendParam: SendOptions): Object | Destroys the token of the given token id. The transaction is formed based on `sendParam`. |
| pause(): Object | Suspends functions related to sending tokens. |
| pause(sendParam: SendOptions): Object | Suspends functions related to sending tokens. The transaction is formed based on `sendParam`. |
| unpause(): Object | Resumes the paused contract. |
| unpause(sendParam: SendOptions): Object | Resumes the paused contract. The transaction is formed based on `sendParam`. |
| addPauser(account: String): Object | Adds an account as a pauser that has the right to suspend the contract. |
| addPauser(account: String, sendParam: SendOptions): Object | Adds an account as a pauser that has the right to suspend the contract. The transaction is formed based on `sendParam`. |
| renouncePauser(): Object | Renounces the right to pause the contract. |
| renouncePauser(sendParam: SendOptions): Object | Renounces the right to pause the contract. The transaction is formed based on `sendParam`. |

#### KIP17DeployParams

`KIP17DeployParams` is a class that defines the token information required when deploying a KIP-17 token contract.

##### Variables

| Variable | Description |
| ----------- | ----------- |
| name: String | The name of the token. |
| symbol: String | The symbol of the token. |

##### Methods

None

### Utils Layer

The Utils layer provides utility functions.

![utils](https://user-images.githubusercontent.com/32922423/106681271-85c2c000-6603-11eb-9e17-db218c1c9bb3.png)

The Utils class provides basic utility functions required when using Caver, and it also provides converting functions based on `KlayUnit`.

#### Utils

`Utils` provides utility functions.

##### Variables

| Variable | Description |
| ----------- | ----------- |
| klayUnit: KlayUnit | Unit of KLAY used in Klaytn. |

##### Methods

| Method | Description |
| ----------- | ----------- |
| isAddress(address: String): Boolean | Checks if a given string is a valid Klaytn address. It will also check the checksum if the address has upper and lowercase letters. |
| isValidPrivateKey(key: String): Boolean | Returns true if the given string is a valid private key. Otherwise, it returns false. |
| isKlaytnWalletKey(key: String): Boolean | Returns true if the given string is a valid KlaytnWalletKey. Otherwise, it returns false. |
| isValidPublicKey(key: String): Boolean | Returns true if the given string is a valid public key. Otherwise, it returns false. |
| compressPublicKey(key: String): String | Compresses the uncompressed public key and returns the compressed public key. |
| decompressPublicKey(key: String): String | Decompresses the compressed public key and returns the uncompressed public key. |
| hashMessage(message: String): String | Hashes the given message with the Klaytn-specific prefix: `keccak256(&quot;\x19Klaytn Signed Message:\n&quot; + len(message) + message))` |
| parseKlaytnWalletKey(key: String): String[] | Parses the given KlaytnWalletKey string to an array which includes &quot;private key&quot;, &quot;type&quot;, &quot;address&quot;. |
| isHex(str: String): boolean | Checks if a given string is a hex string. |
| isHexStrict(str: String): boolean | Checks if a given string is a hex string. The difference to `isHex` is that it expects the string to be prefixed with 0x. |
| addHexPrefix(str: String): String | Returns a 0x-prefixed hex string. If the input is already 0x-prefixed or a non-hex string, the input value is returned as-is. |
| stripHexPrefix(str: String): String | Returns the result with 0x prefix stripped from input. |
| convertToPeb(num: String, unit: String): String | Converts any KLAY value into peb. |
| convertToPeb(num: String, unit: KlayUnit): String | Converts any KLAY value into peb. |
| convertFromPeb(num: String, unit: String): String | Converts any KLAY value from peb. |
| convertFromPeb(num: String, unit: KlayUnit): String | Converts any KLAY value from peb. |
| recover(message: String, signature: SignatureData): String | Recovers the Klaytn address that was used to sign the given data. |
| recover(message: String, signature: SignatureData, preFixed: Boolean): String | Recovers the Klaytn address that was used to sign the given data. |

#### KlayUnit

`KlayUnit` is defined as the [unit used in Klaytn](https://docs.klaytn.com/klaytn/design/klaytn-native-coin-klay#units-of-klay) as an enumerated type. Each unit defines the unit&apos;s `name` and `pebFactor`. `pebFactor` is used when converting the value to peb.

##### Variables

| Variable | Description |
| ----------- | ----------- |
| peb: Object | unit: &apos;peb&apos;, pebFactor: 0 |
| kpeb: Object | unit: &apos;kpeb&apos;, pebFactor: 3 |
| Mpeb: Object | unit: &apos;Mpeb&apos;, pebFactor: 6 |
| Gpeb: Object | unit: &apos;Gpeb&apos;, pebFactor: 9 |
| ston: Object | unit: &apos;ston&apos;, pebFactor: 9 |
| uKLAY: Object | unit: &apos;uKLAY&apos;, pebFactor: 12 |
| mKLAY: Object | unit: &apos;mKLAY&apos;, pebFactor: 15 |
| KLAY: Object | unit: &apos;KLAY&apos;, pebFactor: 18 |
| kKLAY: Object | unit: &apos;kKLAY&apos;, pebFactor: 21 |
| MKLAY: Object | unit: &apos;MKLAY&apos;, pebFactor: 24 |
| GKLAY: Object | unit: &apos;GKLAY&apos;, pebFactor: 27 |
| TKLAY: Object | unit: &apos;TKLAY&apos;, pebFactor: 30 |

### Example Usage of the Common Architecture

In this chapter, the pseudocode for sending a transaction using the SDK that implements the common architecture is explained.

```
input: keystore.json, password, from, to, value, gas
output: receipt of value transfer transaction

// Read keystore json file and decrypt keystore
keystore &lt;- readFile(&apos;./keystore.json&apos;)
keyring &lt;- decryptKeystore(keystore, password)

// Add the keyring to the in-memory wallet
addToTheWallet(keyring)

// Create a value transfer transaction
vt &lt;- createValueTransferTransaction(from, to, value, gas)

// Sign the transaction
signed &lt;- sign(from, vt)

// Send the transaction to Klaytn
receipt &lt;- sendRawTransaction(signed)
print receipt
```

## Rationale

While designing the common architecture, we tried to use the concept used in Klaytn as much as possible.

## Backward Compatibility

The backward compatibility is preserved. That is, the previous implementation using Klaytn SDKs will work without any changes.

## Test Cases

1. All of the functions defined in the above common architecture diagram should be provided.
2. The existing functions in caver-js and caver-java can be used.

## Implementation

PRs related to common architecture are linked to the issues below. The common architecture is implemented in caver-js v1.5.0 and caver-java v1.5.0.

- caver-js
  - https://github.com/klaytn/caver-js/issues/249
- caver-java - https://github.com/klaytn/caver-java/issues/97

## Copyright

Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).

[accountkey]: https://docs.klaytn.com/klaytn/design/accounts#account-key
[klaytn&apos;s account]: https://docs.klaytn.com/klaytn/design/accounts#klaytn-accounts
[klaytn account]: https://docs.klaytn.com/klaytn/design/accounts#klaytn-accounts
[multiple transaction types]: https://docs.klaytn.com/klaytn/design/transactions#klaytn-transactions
[basic transactions]: https://docs.klaytn.com/klaytn/design/transactions/basic
[fee delegation transactions]: https://docs.klaytn.com/klaytn/design/transactions/fee-delegation
[partial fee delegation transactions]: https://docs.klaytn.com/klaytn/design/transactions/partial-fee-delegation
[klaytn design - transactions]: https://docs.klaytn.com/klaytn/design/transactions

[kip-7]: /KIPs/kip-7

[kip-17]: /KIPs/kip-17
[klay]: https://docs.klaytn.com/bapp/json-rpc/api-references/klay
[klay namespace of the node api]: https://docs.klaytn.com/bapp/json-rpc/api-references/klay
[net]: https://docs.klaytn.com/bapp/json-rpc/api-references/network
[net namespace of the node api]: https://docs.klaytn.com/bapp/json-rpc/api-references/network
[json-rpc apis]: https://docs.klaytn.com/bapp/json-rpc/api-references
[klay_logs]: https://docs.klaytn.com/bapp/json-rpc/api-references/klay/filter#klay_getlogs
[klay_call]: https://docs.klaytn.com/bapp/json-rpc/api-references/klay/transaction#klay_call
</description>
        <pubDate>Thu, 02 Jul 2020 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-34</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-34</guid>
      </item>
    
      <item>
        <title>Token Standard</title>
        <category>Tokenization</category>
        
          <comments>https://github.com/klaytn/kips/issues/37</comments>
        
        <description>## Simple Summary

A standard interface for contracts that manage multiple token types. A single deployed contract may include any combination of fungible tokens, non-fungible tokens or other configurations (e.g. semi-fungible tokens).

## Abstract

This standard outlines a smart contract interface that can represent any number of fungible and non-fungible token types. Existing standards such as KIP-7 require deployment of separate contracts per token type. The KIP-17 standard’s token ID is a single non-fungible index and the group of these non-fungibles is deployed as a single contract with settings for the entire collection. In contrast, the KIP-37 Multi Token Standard allows for each token ID to represent a new configurable token type, which may have its own metadata, supply and other attributes.

The `_id` argument contained in each function’s argument set indicates a specific token or token type in a transaction.

## Motivation

Tokens standards like KIP-7 and KIP-17 require a separate contract to be deployed for each token type or collection. This places a lot of redundant bytecode on the Klaytn blockchain and limits certain functionality by the nature of separating each token contract into its own permissioned address. With the rise of blockchain games and platforms like Enjin Coin, game developers may be creating thousands of token types, and a new type of token standard is needed to support them. However, KIP-37 is not specific to games and many other applications can benefit from this flexibility.

New functionality is possible with this design such as transferring multiple token types at once, saving on transaction costs. Trading (escrow / atomic swaps) of multiple tokens can be built on top of this standard and it removes the need to “approve” individual token contracts separately. It is also easy to describe and mix multiple fungible or non-fungible token types in a single contract.

## Specification

This document is heavily derived from [ERC-1155](https://eips.ethereum.org/EIPS/eip-1155) written by Witek Radomski, Andrew Cooke, Philippe Castonguay, James Therien, Eric Binet, and Ronan Sandford.

The key words &quot;MUST&quot;, &quot;MUST NOT&quot;, &quot;REQUIRED&quot;, &quot;SHALL&quot;, &quot;SHALL NOT&quot;, &quot;SHOULD&quot;, &quot;SHOULD NOT&quot;, &quot;RECOMMENDED&quot;, &quot;MAY&quot;, and &quot;OPTIONAL&quot; in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt).

### KIP37 Interface

Smart contracts implementing the KIP-37 standard MUST implement all of the functions in the KIP37 interface.

Smart contracts implementing the KIP-37 standard MUST implement the KIP-13 supportsInterface function and MUST return the constant value true if 0x6433ca1f is passed through the interfaceID argument.

```solidity
pragma solidity 0.5.6;

/**
    @title KIP-37 Multi Token Standard
    @dev See https://kips.klaytn.com/KIPs/kip-37
    Note: The KIP-13 identifier for this interface is 0x6433ca1f.
 */
interface KIP37 /* is KIP13 */ {
    /**
        @dev Either `TransferSingle` or `TransferBatch` MUST emit when tokens are transferred, including zero value transfers as well as minting or burning (see &quot;Safe Transfer Rules&quot; section of the standard).
        The `_operator` argument MUST be the address of an account/contract that is approved to make the transfer (SHOULD be msg.sender).
        The `_from` argument MUST be the address of the holder whose balance is decreased.
        The `_to` argument MUST be the address of the recipient whose balance is increased.
        The `_id` argument MUST be the token type being transferred.
        The `_value` argument MUST be the number of tokens the holder balance is decreased by and match what the recipient balance is increased by.
        When minting/creating tokens, the `_from` argument MUST be set to `0x0` (i.e. zero address).
        When burning/destroying tokens, the `_to` argument MUST be set to `0x0` (i.e. zero address).
    */
    event TransferSingle(address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value);

    /**
        @dev Either `TransferSingle` or `TransferBatch` MUST emit when tokens are transferred, including zero value transfers as well as minting or burning (see &quot;Safe Transfer Rules&quot; section of the standard).
        The `_operator` argument MUST be the address of an account/contract that is approved to make the transfer (SHOULD be msg.sender).
        The `_from` argument MUST be the address of the holder whose balance is decreased.
        The `_to` argument MUST be the address of the recipient whose balance is increased.
        The `_ids` argument MUST be the list of tokens being transferred.
        The `_values` argument MUST be the list of the number of tokens (matching the list and order of tokens specified in _ids) the holder balance is decreased by and match what the recipient balance is increased by.
        When minting/creating tokens, the `_from` argument MUST be set to `0x0` (i.e. zero address).
        When burning/destroying tokens, the `_to` argument MUST be set to `0x0` (i.e. zero address).
    */
    event TransferBatch(address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values);

    /**
        @dev MUST emit when approval for a second party/operator address to manage all tokens for an owner address is enabled or disabled (absence of an event assumes disabled).
    */
    event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);

    /**
        @dev MUST emit when the URI is updated for a token ID.
        URIs are defined in RFC 3986.
        The URI MUST point to a JSON file that conforms to the &quot;KIP-37 Metadata URI JSON Schema&quot;.
    */
    event URI(string _value, uint256 indexed _id);

    /**
        @notice Transfers `_value` amount of an `_id` from the `_from` address to the `_to` address specified (with safety call).
        @dev Caller must be approved to manage the tokens being transferred out of the `_from` account (see &quot;Approval&quot; section of the standard).
        MUST revert if `_to` is the zero address.
        MUST revert if the balance of the holder `_from` for the token `_id` is lower than the `_value` sent.
        MUST revert on any other error.
        MUST emit the `TransferSingle` event to reflect the balance change (see &quot;Safe Transfer Rules&quot; section of the standard).
        After the above conditions are met, this function MUST check if `_to` is a smart contract (e.g. code size &gt; 0). If so, it MUST call `onKIP37Received` on `_to` and act appropriately (see &quot;Safe Transfer Rules&quot; section of the standard).
        @param _from    Source address
        @param _to      Target address
        @param _id      ID of the token type
        @param _value   Transfer amount
        @param _data    Additional data with no specified format, MUST be sent unaltered in call to `onKIP37Received` on `_to`
    */
    function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external;

    /**
        @notice Transfers `_values` amount(s) of `_ids` from the `_from` address to the `_to` address specified (with safety call).
        @dev Caller must be approved to manage the tokens being transferred out of the `_from` account (see &quot;Approval&quot; section of the standard).
        MUST revert if `_to` is the zero address.
        MUST revert if the length of `_ids` is not the same as the length of `_values`.
        MUST revert if any of the balance(s) of the holder(s) for token(s) in `_ids` is lower than the respective amount(s) in `_values` sent to the recipient.
        MUST revert on any other error.
        MUST emit `TransferSingle` or `TransferBatch` event(s) such that all the balance changes are reflected (see &quot;Safe Transfer Rules&quot; section of the standard).
        Balance changes and events MUST follow the ordering of the arrays (_ids[0]/_values[0] before _ids[1]/_values[1], etc).
        After the above conditions for the transfer(s) in the batch are met, this function MUST check if `_to` is a smart contract (e.g. code size &gt; 0). If so, it MUST call the relevant `KIP37TokenReceiver` hook(s) on `_to` and act appropriately (see &quot;Safe Transfer Rules&quot; section of the standard).
        @param _from    Source address
        @param _to      Target address
        @param _ids     IDs of each token type (order and length must match _values array)
        @param _values  Transfer amounts per token type (order and length must match _ids array)
        @param _data    Additional data with no specified format, MUST be sent unaltered in call to the `KIP37TokenReceiver` hook(s) on `_to`
    */
    function safeBatchTransferFrom(address _from, address _to, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) external;

    /**
        @notice Get the balance of an account&apos;s tokens.
        @param _owner  The address of the token holder
        @param _id     ID of the token
        @return        The _owner&apos;s balance of the token type requested
     */
    function balanceOf(address _owner, uint256 _id) external view returns (uint256);

    /**
        @notice Get the balance of multiple account/token pairs
        @param _owners The addresses of the token holders
        @param _ids    ID of the tokens
        @return        The _owner&apos;s balance of the token types requested (i.e. balance for each (owner, id) pair)
     */
    function balanceOfBatch(address[] calldata _owners, uint256[] calldata _ids) external view returns (uint256[] memory);

    /**
        @notice Enable or disable approval for a third party (&quot;operator&quot;) to manage all of the caller&apos;s tokens.
        @dev MUST emit the ApprovalForAll event on success.
        @param _operator  Address to add to the set of authorized operators
        @param _approved  True if the operator is approved, false to revoke approval
    */
    function setApprovalForAll(address _operator, bool _approved) external;

    /**
        @notice Queries the approval status of an operator for a given owner.
        @param _owner     The owner of the tokens
        @param _operator  Address of authorized operator
        @return           True if the operator is approved, false if not
    */
    function isApprovedForAll(address _owner, address _operator) external view returns (bool);

    /**
        @notice Get the total supply of the token type requested.
        @param _id      ID of the token
        @return         The supply of the token type requested
    */
    function totalSupply(uint256 _id) external view returns (uint256);
}
```

### Differences from ERC-1155

This section describes the differences between KIP-37 and ERC-1155.

- KIP-37 also supports the wallet interface of ERC-1155 (`IERC1155TokenReceiver`) to be compliant with ERC-1155.
- More optional extensions are defined (minting extension, burning extension, and pausing extension).
- `totalSupply` is added to obtain the amount of tokens in existence for each token id.

### KIP-13 Identifiers

The below table shows KIP-13 identifiers for interfaces defined in this proposal.

| Interface                                       | KIP-13 Identifier |
| ----------------------------------------------- | ----------------- |
| [IKIP37](#kip37-interface)                      | 0x6433ca1f        |
| [IKIP37TokenReceiver](#kip-37-token-receiver)   | 0x7cc2d017        |
| [IERC1155TokenReceiver](#kip-37-token-receiver) | 0x4e2312e0        |
| [IKIP37Metadata](#metadata-extension)           | 0x0e89341c        |
| [IKIP37Mintable](#minting-extension)            | 0xdfd9d9ec        |
| [IKIP37Burnable](#burning-extension)            | 0x9e094e9e        |
| [IKIP37Pausable](#pausing-extension)            | 0x0e8ffdb7        |

### KIP-37 Token Receiver

Smart contracts MUST implement all of the functions in the KIP37TokenReceiver interface to accept transfers. See [Safe Transfer Rules](#safe-transfer-rules) for further detail.

Smart contracts MUST implement the KIP-13 supportsInterface function and signify support for the KIP37TokenReceiver interface to accept transfers. See [KIP37TokenReceiver KIP-13 rules](#kip37tokenreceiver-kip-13-rules) for further detail.

```solidity
pragma solidity 0.5.6;

/**
    Note: The KIP-13 identifier for this interface is 0x7cc2d017.
*/
interface KIP37TokenReceiver {
    /**
        @notice Handle the receipt of a single KIP37 token type.
        @dev A KIP37-compliant smart contract MUST call this function on the token recipient contract, at the end of a `safeTransferFrom` after the balance has been updated.
        This function MUST return `bytes4(keccak256(&quot;onKIP37Received(address,address,uint256,uint256,bytes)&quot;))` (i.e. 0xe78b3325) if it accepts the transfer.
        This function MUST revert if it rejects the transfer.
        Return of any other value than the prescribed keccak256 generated value MUST result in the transaction being reverted by the caller.
        @param _operator  The address which initiated the transfer (i.e. msg.sender)
        @param _from      The address which previously owned the token
        @param _id        The ID of the token being transferred
        @param _value     The amount of tokens being transferred
        @param _data      Additional data with no specified format
        @return           `bytes4(keccak256(&quot;onKIP37Received(address,address,uint256,uint256,bytes)&quot;))`
    */
    function onKIP37Received(address _operator, address _from, uint256 _id, uint256 _value, bytes calldata _data) external returns(bytes4);

    /**
        @notice Handle the receipt of multiple KIP37 token types.
        @dev A KIP37-compliant smart contract MUST call this function on the token recipient contract, at the end of a `safeBatchTransferFrom` after the balances have been updated.
        This function MUST return `bytes4(keccak256(&quot;onKIP37BatchReceived(address,address,uint256[],uint256[],bytes)&quot;))` (i.e. 0x9b49e332) if it accepts the transfer(s).
        This function MUST revert if it rejects the transfer(s).
        Return of any other value than the prescribed keccak256 generated value MUST result in the transaction being reverted by the caller.
        @param _operator  The address which initiated the batch transfer (i.e. msg.sender)
        @param _from      The address which previously owned the token
        @param _ids       An array containing ids of each token being transferred (order and length must match _values array)
        @param _values    An array containing amounts of each token being transferred (order and length must match _ids array)
        @param _data      Additional data with no specified format
        @return           `bytes4(keccak256(&quot;onKIP37BatchReceived(address,address,uint256[],uint256[],bytes)&quot;))`
    */
    function onKIP37BatchReceived(address _operator, address _from, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) external returns(bytes4);
}
```

To be compliant with ERC-1155, KIP-37 also supports ERC1155TokenReceiver. This makes the current ERC-1155 implementation on Ethereum can be easily migrated on to Klaytn without any modification.

```solidity
pragma solidity 0.5.6;

/**
    Note: The KIP-13 identifier for this interface is 0x4e2312e0.
*/
interface ERC1155TokenReceiver {
    /**
        @notice Handle the receipt of a single ERC1155 token type.
        @dev An ERC1155-compliant smart contract MUST call this function on the token recipient contract, at the end of a `safeTransferFrom` after the balance has been updated.
        This function MUST return `bytes4(keccak256(&quot;onERC1155Received(address,address,uint256,uint256,bytes)&quot;))` (i.e. 0xf23a6e61) if it accepts the transfer.
        This function MUST revert if it rejects the transfer.
        Return of any other value than the prescribed keccak256 generated value MUST result in the transaction being reverted by the caller.
        @param _operator  The address which initiated the transfer (i.e. msg.sender)
        @param _from      The address which previously owned the token
        @param _id        The ID of the token being transferred
        @param _value     The amount of tokens being transferred
        @param _data      Additional data with no specified format
        @return           `bytes4(keccak256(&quot;onERC1155Received(address,address,uint256,uint256,bytes)&quot;))`
    */
    function onERC1155Received(address _operator, address _from, uint256 _id, uint256 _value, bytes calldata _data) external returns(bytes4);

    /**
        @notice Handle the receipt of multiple ERC1155 token types.
        @dev An ERC1155-compliant smart contract MUST call this function on the token recipient contract, at the end of a `safeBatchTransferFrom` after the balances have been updated.
        This function MUST return `bytes4(keccak256(&quot;onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)&quot;))` (i.e. 0xbc197c81) if it accepts the transfer(s).
        This function MUST revert if it rejects the transfer(s).
        Return of any other value than the prescribed keccak256 generated value MUST result in the transaction being reverted by the caller.
        @param _operator  The address which initiated the batch transfer (i.e. msg.sender)
        @param _from      The address which previously owned the token
        @param _ids       An array containing ids of each token being transferred (order and length must match _values array)
        @param _values    An array containing amounts of each token being transferred (order and length must match _ids array)
        @param _data      Additional data with no specified format
        @return           `bytes4(keccak256(&quot;onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)&quot;))`
    */
    function onERC1155BatchReceived(address _operator, address _from, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) external returns(bytes4);
}
```

### Safe Transfer Rules

To be more explicit about how the standard `safeTransferFrom` and `safeBatchTransferFrom` functions MUST operate with respect to the `KIP37TokenReceiver` or `ERC1155TokenReceiver` hook functions, a list of scenarios and rules follows.

#### Scenarios

**_Scenario#1 :_** The recipient is not a contract.

- `onKIP37Received` and `onKIP37BatchReceived` MUST NOT be called on an EOA (Externally Owned Account).

**_Scenario#2 :_** The transaction is not a mint/transfer of a token.

- `onKIP37Received` and `onKIP37BatchReceived` MUST NOT be called outside of a mint or transfer process.

**_Scenario#3 :_** The receiver does not implement the necessary `KIP37TokenReceiver` or `ERC1155TokenReceiver` interface function(s).

- The transfer MUST be reverted with the one caveat below. - If the token(s) being sent are part of a hybrid implementation of another standard, that particular standard’s rules on sending to a contract MAY now be followed instead. See [Backwards Compatibility](#backwards-compatibility) section.

**_Scenario#4 :_** The receiver implements the necessary `KIP37TokenReceiver` or `ERC1155TokenReceiver` interface function(s) but returns an unknown value.

- The transfer MUST be reverted.

**_Scenario#5 :_** The receiver implements the necessary `KIP37TokenReceiver` or `ERC1155TokenReceiver` interface function(s) but throws an error.

- The transfer MUST be reverted.

**_Scenario#6 :_** The receiver implements the `KIP37TokenReceiver` or `ERC1155TokenReceiver` interface and is the recipient of one and only one balance change (e.g. `safeTransferFrom` called).

- The balances for the transfer MUST have been updated before the `KIP37TokenReceiver` or `ERC1155TokenReceiver` hook is called on a recipient contract.
- The transfer event MUST have been emitted to reflect the balance changes before the `KIP37TokenReceiver` or `ERC1155TokenReceiver` hook is called on the recipient contract.
- One of `onKIP37Received`, `onKIP37BatchReceived`, `onERC1155Received` or `onERC1155BatchReceived` MUST be called on the recipient contract.
- The `onKIP37Received` or `onERC1155Received` hook SHOULD be called on the recipient contract and its rules followed.
  - See [onKIP37Received rules](#onkip37received-rules) for further rules that MUST be followed.
- The `onKIP37BatchReceived` or `onERC1155BatchReceived` hook MAY be called on the recipient contract and its rules followed.
  - See [onKIP37BatchReceived rules](#onkip37batchreceived-rules) for further rules that MUST be followed.

**_Scenario#7 :_** The receiver implements the `KIP37TokenReceiver` or `ERC1155TokenReceiver` interface and is the recipient of more than one balance change (e.g. `safeBatchTransferFrom` called).

- All balance transfers that are referenced in a call to a `KIP37TokenReceiver` or `ERC1155TokenReceiver` hook MUST be updated before the `KIP37TokenReceiver` or `ERC1155TokenReceiver` hook is called on the recipient contract.
- All transfer events MUST have been emitted to reflect current balance changes before a `KIP37TokenReceiver` or `ERC1155TokenReceiver` hook is called on the recipient contract.
- `onKIP37Received`, `onKIP37BatchReceived`, `onERC1155Received` or `onERC1155BatchReceived` MUST be called on the recipient as many times as necessary such that every balance change for the recipient in the scenario is accounted for.
  - The return magic value for every hook call MUST be checked and acted upon as per [onKIP37Received rules](#onkip37received-rules) and [onKIP37BatchReceived rules](#onkip37batchreceived-rules).
- The `onKIP37BatchReceived` or `onERC1155BatchReceived` hook SHOULD be called on the recipient contract and its rules followed.
  - See [onKIP37BatchReceived rules](#onkip37batchreceived-rules) for further rules that MUST be followed.
- The `onKIP37Received` or `onERC1155Received` hook MAY be called on the recipient contract and its rules followed.
  - See [onKIP37Received rules](#onkip37received-rules) for further rules that MUST be followed.

**_Scenario#8 :_** You are the creator of a contract that implements the `KIP37TokenReceiver` interface and you forward the token(s) onto another address in one or both of `onKIP37Received` and `onKIP37BatchReceived`.

- Forwarding should be considered acceptance and then initiating a new `safeTransferFrom` or `safeBatchTransferFrom` in a new context.
  - The prescribed keccak256 acceptance value magic for the receiver hook being called MUST be returned after forwarding is successful.
- The `_data` argument MAY be re-purposed for the new context.
- If forwarding fails the transaction MAY be reverted.
  - If the contract logic wishes to keep the ownership of the token(s) itself in this case it MAY do so.

**_Scenario#9 :_** You are transferring tokens via a non-standard API call i.e. an implementation specific API and NOT `safeTransferFrom` or `safeBatchTransferFrom`.

- In this scenario, all balance updates and events output rules are the same as if a standard transfer function had been called.
  - i.e. an external viewer MUST still be able to query the balance via a standard function and it MUST be identical to the balance as determined by `TransferSingle` and `TransferBatch` events alone.
- If the receiver is a contract the `KIP37TokenReceiver` or `ERC1155TokenReceiver` hooks still need to be called on it and the return values respected the same as if a standard transfer function had been called.
  - However while the `safeTransferFrom` or `safeBatchTransferFrom` functions MUST revert if a receiving contract does not implement the KIP37TokenReceiver or ERC1155TokenReceiver interface, a non-standard function MAY proceed with the transfer.
  - See [Implementation specific transfer API rules](#implementation-specific-transfer-api-rules).

#### Rules

##### **_safeTransferFrom rules:_**

- Caller must be approved to manage the tokens being transferred out of the `_from` account (see “Approval” section).
- MUST revert if `_to` is the zero address.
- MUST revert if balance of holder for token `_id` is lower than the `_value` sent to the recipient.
- MUST revert on any other error.
- MUST emit the `TransferSingle` event to reflect the balance change (see [TransferSingle and TransferBatch event rules](#transfersingle-and-transferbatch-event-rules) section).
- After the above conditions are met, this function MUST check if `_to` is a smart contract (e.g. code size &gt; 0). If so, it MUST call `onKIP37Received` or `onERC1155Received` on `_to` and act appropriately (see [onKIP37Received rules](#onkip37received-rules) section).
  - The `_data` argument provided by the sender for the transfer MUST be passed with its contents unaltered to the `onKIP37Received` or `onERC1155Received` hook function via its `_data` argument.

##### **_safeBatchTransferFrom rules:_**

- Caller must be approved to manage all the tokens being transferred out of the `_from` account (see “Approval” section).
- MUST revert if `_to` is the zero address.
- MUST revert if the length of `_ids` is not the same as the length of `_values`.
- MUST revert if any of the balance(s) of the holder(s) for token(s) in `_ids` is lower than the respective amount(s) in `_values` sent to the recipient.
- MUST revert on any other error.
- MUST emit `TransferSingle` or `TransferBatch` event(s) such that all the balance changes are reflected (see [TransferSingle and TransferBatch event rules](#transfersingle-and-transferbatch-event-rules) section).
- The balance changes and events MUST occur in the array order they were submitted (\_ids[0]/\_values[0] before \_ids[1]/\_values[1], etc).
- After the above conditions are met, this function MUST check if `_to` is a smart contract (e.g. code size &gt; 0). If so, it MUST call `onKIP37Received`, `onKIP37BatchReceived`, `onERC1155Received` or `onERC1155BatchReceived` on `_to` and act appropriately (see [onKIP37Received rules](#onkip37received-rules) and [onKIP37BatchReceived rules](#onkip37batchreceived-rules) section).
  - The `_data` argument provided by the sender for the transfer MUST be passed with its contents unaltered to the `KIP37TokenReceiver` or `ERC1155TokenReceiver` hook function(s) via their `_data` argument.

##### **_TransferSingle and TransferBatch event rules:_**

- `TransferSingle` SHOULD be used to indicate a single balance transfer has occurred between a `_from` and `_to` pair.
  - It MAY be emitted multiple times to indicate multiple balance changes in the transaction, but note that `TransferBatch` is designed for this to reduce gas consumption.
  - The `_operator` argument MUST be the address of an account/contract that is approved to make the transfer (SHOULD be msg.sender).
  - The `_from` argument MUST be the address of the holder whose balance is decreased.
  - The `_to` argument MUST be the address of the recipient whose balance is increased.
  - The `_id` argument MUST be the token type being transferred.
  - The `_value` argument MUST be the number of tokens the holder balance is decreased by and match what the recipient balance is increased by.
  - When minting/creating tokens, the `_from` argument MUST be set to `0x0` (i.e. zero address). See [Minting-creating and burning-destroying rules](#minting-creating-and-burning-destroying-rules).
  - When burning/destroying tokens, the `_to` argument MUST be set to `0x0` (i.e. zero address). See [Minting-creating and burning-destroying rules](#minting-creating-and-burning-destroying-rules).
- `TransferBatch` SHOULD be used to indicate multiple balance transfers have occurred between a `_from` and `_to` pair.
  - It MAY be emitted with a single element in the list to indicate a singular balance change in the transaction, but note that `TransferSingle` is designed for this to reduce gas consumption.
  - The `_operator` argument MUST be the address of an account/contract that is approved to make the transfer (SHOULD be msg.sender).
  - The `_from` argument MUST be the address of the holder whose balance is decreased for each entry pair in `_ids` and `_values`.
  - The `_to` argument MUST be the address of the recipient whose balance is increased for each entry pair in `_ids` and `_values`.
  - The `_ids` array argument MUST contain the ids of the tokens being transferred.
  - The `_values` array argument MUST contain the number of token to be transferred for each corresponding entry in `_ids`.
  - `_ids` and `_values` MUST have the same length.
  - When minting/creating tokens, the `_from` argument MUST be set to `0x0` (i.e. zero address). See [Minting-creating and burning-destroying rules](#minting-creating-and-burning-destroying-rules).
  - When burning/destroying tokens, the `_to` argument MUST be set to `0x0` (i.e. zero address). See [Minting-creating and burning-destroying rules](#minting-creating-and-burning-destroying-rules).
- The total value transferred from address `0x0` minus the total value transferred to `0x0` observed via the `TransferSingle` and `TransferBatch` events MAY be used by clients and exchanges to determine the “circulating supply” for a given token ID.
- To broadcast the existence of a token ID with no initial balance, the contract SHOULD emit the `TransferSingle` event from `0x0` to `0x0`, with the token creator as `_operator`, and a `_value` of 0.
- All `TransferSingle` and `TransferBatch` events MUST be emitted to reflect all the balance changes that have occurred before any call(s) to `onKIP37Received`, `onKIP37BatchReceived`, `onERC1155Received` or `onERC1155BatchReceived`.
  - To make sure event order is correct in the case of valid re-entry (e.g. if a receiver contract forwards tokens on receipt) state balance and events balance MUST match before calling an external contract.

##### **_onKIP37Received rules:_**

- The `_operator` argument MUST be the address of an account/contract that is approved to make the transfer (SHOULD be msg.sender).

* The `_from` argument MUST be the address of the holder whose balance is decreased.
  - `_from` MUST be 0x0 for a mint.
* The `_id` argument MUST be the token type being transferred.
* The `_value` argument MUST be the number of tokens the holder balance is decreased by and match what the recipient balance is increased by.
* The `_data` argument MUST contain the information provided by the sender for the transfer with its contents unaltered.
  - i.e. it MUST pass on the unaltered `_data` argument sent via the `safeTransferFrom` or `safeBatchTransferFrom` call for this transfer.
* The recipient contract MAY accept an increase of its balance by returning the acceptance magic value `bytes4(keccak256(&quot;onKIP37Received(address,address,uint256,uint256,bytes)&quot;))`
  - If the return value is `bytes4(keccak256(&quot;onKIP37Received(address,address,uint256,uint256,bytes)&quot;))` the transfer MUST be completed or MUST revert if any other conditions are not met for success.
* The recipient contract MAY reject an increase of its balance by calling revert.
  - If the recipient contract throws/reverts the transaction MUST be reverted.
* If the return value is anything other than `bytes4(keccak256(&quot;onKIP37Received(address,address,uint256,uint256,bytes)&quot;))` the transaction MUST be reverted.
* `onKIP37Received` (and/or `onKIP37BatchReceived`) MAY be called multiple times in a single transaction and the following requirements must be met:
  - All callbacks represent mutually exclusive balance changes.
  - The set of all calls to `onKIP37Received` and `onKIP37BatchReceived` describes all balance changes that occurred during the transaction in the order submitted.
* A contract MAY skip calling the `onKIP37Received` hook function if the transfer operation is transferring the token to itself.

##### **_onKIP37BatchReceived rules:_**

- The `_operator` argument MUST be the address of an account/contract that is approved to make the transfer (SHOULD be msg.sender).

* The `_from` argument MUST be the address of the holder whose balance is decreased.
  - `_from` MUST be 0x0 for a mint.
* The `_ids` argument MUST be the list of tokens being transferred.
* The `_values` argument MUST be the list of number of tokens (matching the list and order of tokens specified in `_ids`) the holder balance is decreased by and match what the recipient balance is increased by.
* The `_data` argument MUST contain the information provided by the sender for the transfer with its contents unaltered.
  - i.e. it MUST pass on the unaltered `_data` argument sent via the `safeBatchTransferFrom` call for this transfer.
* The recipient contract MAY accept an increase of its balance by returning the acceptance magic value `bytes4(keccak256(&quot;onKIP37BatchReceived(address,address,uint256[],uint256[],bytes)&quot;))`
  - If the return value is `bytes4(keccak256(&quot;onKIP37BatchReceived(address,address,uint256[],uint256[],bytes)&quot;))` the transfer MUST be completed or MUST revert if any other conditions are not met for success.
* The recipient contract MAY reject an increase of its balance by calling revert.
  - If the recipient contract throws/reverts the transaction MUST be reverted.
* If the return value is anything other than `bytes4(keccak256(&quot;onKIP37BatchReceived(address,address,uint256[],uint256[],bytes)&quot;))` the transaction MUST be reverted.
* `onKIP37BatchReceived` (and/or `onKIP37Received`) MAY be called multiple times in a single transaction and the following requirements must be met:
  - All callbacks represent mutually exclusive balance changes.
  - The set of all calls to `onKIP37Received` and `onKIP37BatchReceived` describes all balance changes that occurred during the transaction in the order submitted.
* A contract MAY skip calling the `onKIP37BatchReceived` hook function if the transfer operation is transferring the token(s) to itself.

##### **_KIP37TokenReceiver KIP-13 rules:_**

- The implementation of the KIP-13 `supportsInterface` function SHOULD be as follows:
  ```solidity
  function supportsInterface(bytes4 interfaceID) external view returns (bool) {
      return  interfaceID == 0x01ffc9a7 ||    // KIP-13 support (i.e. `bytes4(keccak256(&apos;supportsInterface(bytes4)&apos;))`).
              interfaceID == 0x7cc2d017;      // KIP-37 `KIP37TokenReceiver` support (i.e. `bytes4(keccak256(&quot;onKIP37Received(address,address,uint256,uint256,bytes)&quot;)) ^ bytes4(keccak256(&quot;onKIP37BatchReceived(address,address,uint256[],uint256[],bytes)&quot;))`).
  }
  ```
- The implementation MAY differ from the above but:
  - It MUST return the constant value `true` if `0x01ffc9a7` is passed through the `interfaceID` argument. This signifies KIP-13 support.
  - It MUST return the constant value `true` if `0x7cc2d017` is passed through the `interfaceID` argument. This signifies KIP-37 `KIP37TokenReceiver` support.
  - It MUST NOT consume more than 10,000 gas.
    - This keeps it below the KIP-13 requirement of 30,000 gas, reduces the gas reserve needs and minimises possible side-effects of gas exhaustion during the call.

##### **_Implementation specific transfer API rules:_**

- If an implementation specific API function is used to transfer KIP-37 token(s) to a contract, the `safeTransferFrom` or `safeBatchTransferFrom` (as appropriate) rules MUST still be followed if the receiver implements the `KIP37TokenReceiver` interface. If it does not the non-standard implementation SHOULD revert but MAY proceed.
- An example:
  1. An approved user calls a function such as `function myTransferFrom(address _from, address _to, uint256[] calldata _ids, uint256[] calldata _values);`.
  2. `myTransferFrom` updates the balances for `_from` and `_to` addresses for all `_ids` and `_values`.
  3. `myTransferFrom` emits `TransferBatch` with the details of what was transferred from address `_from` to address `_to`.
  4. `myTransferFrom` checks if `_to` is a contract address and determines that it is so (if not, then the transfer can be considered successful).
  5. `myTransferFrom` calls `onKIP37BatchReceived` on `_to` and it reverts or returns an unknown value (if it had returned `bytes4(keccak256(&quot;onKIP37BatchReceived(address,address,uint256[],uint256[],bytes)&quot;))` the transfer can be considered successful).
  6. At this point `myTransferFrom` SHOULD revert the transaction immediately as receipt of the token(s) was not explicitly accepted by the `onKIP37BatchReceived` function.
  7. If however `myTransferFrom` wishes to continue it MUST call `supportsInterface(0x7cc2d017)` on `_to` and if it returns the constant value `true` the transaction MUST be reverted, as it is now known to be a valid receiver and the previous acceptance step failed.
     - NOTE: You could have called `supportsInterface(0x7cc2d017)` at a previous step if you wanted to gather and act upon that information earlier, such as in a hybrid standards scenario.
  8. If the above call to `supportsInterface(0x7cc2d017)` on `_to` reverts or returns a value other than the constant value `true` the `myTransferFrom` function MAY consider this transfer successful.
     - **NOTE**: this MAY result in unrecoverable tokens if sent to an address that does not expect to receive KIP-37 tokens.
- The above example is not exhaustive but illustrates the major points (and shows that most are shared with `safeTransferFrom` and `safeBatchTransferFrom`):
  - Balances that are updated MUST have equivalent transfer events emitted.
  - A receiver address has to be checked if it is a contract and if so relevant `KIP37TokenReceiver` hook function(s) have to be called on it.
  - Balances (and events associated) that are referenced in a call to an `KIP37TokenReceiver` hook MUST be updated (and emitted) before the `KIP37TokenReceiver` hook is called.
  - The return values of the `KIP37TokenReceiver` hook functions that are called MUST be respected if they are implemented.
  - Only non-standard transfer functions MAY allow tokens to be sent to a recipient contract that does NOT implement the necessary `KIP37TokenReceiver` hook functions. `safeTransferFrom` and `safeBatchTransferFrom` MUST revert in that case (unless it is a hybrid standards implementation see &quot;Backwards Compatibility&quot;).

##### **_Minting-creating and burning-destroying rules:_**

- &apos;create&apos; means creating a new kind of token by assigning a new token ID.
- &apos;mint&apos; means issuing additional tokens that have already been created.
- When creating tokens, the total supply of the token ID must be increased by initial supply.
- When minting tokens, the total supply of the token ID must be increased by minted quantity.
- When burning tokens, the total supply of the token ID must be decreased by burned quantity.
- A mint/create operation is essentially a specialized transfer and MUST follow these rules:
  - To broadcast the existence of a token ID with no initial balance, the contract SHOULD emit the `TransferSingle` event from `0x0` to `0x0`, with the token creator as `_operator`, and a `_value` of 0.
  - The [TransferSingle and TransferBatch event rules](#transfersingle-and-transferbatch-event-rules) MUST be followed as appropriate for the mint(s) (i.e., singles or batches) however the `_from` argument MUST be set to `0x0` (i.e., zero address) to flag the transfer as a mint to contract observers.
    - **NOTE:** This includes tokens that are given an initial balance in the contract. The balance of the contract MUST also be able to be determined by events alone meaning initial contract balances (e.g., in construction) MUST emit events to reflect those balances too.
- A burn/destroy operation is essentially a specialized transfer and MUST follow these rules:
  - The [TransferSingle and TransferBatch event rules](#transfersingle-and-transferbatch-event-rules) MUST be followed as appropriate for the burn(s) (i.e. singles or batches) however the `_to` argument MUST be set to `0x0` (i.e., zero address) to flag the transfer as a burn to contract observers.
  - When burning/destroying, you do not have to actually transfer to `0x0` (that is implementation specific), only the `_to` argument in the event MUST be set to `0x0` as above.
- The total value transferred from address `0x0` minus the total value transferred to `0x0` observed via the `TransferSingle` and `TransferBatch` events MAY be used by clients and exchanges to determine the &quot;circulating supply&quot; for a given token ID.
- As mentioned above mint/create and burn/destroy operations are specialized transfers and so will likely be accomplished with custom transfer functions rather than `safeTransferFrom` or `safeBatchTransferFrom`. If so the [Implementation specific transfer API rules](#implementation-specific-transfer-api-rules) section would be appropriate.
  - Even in a non-safe API and/or hybrid standards case the above event rules MUST still be adhered to when minting/creating or burning/destroying.
- A contract MAY skip calling the `KIP37TokenReceiver` hook function(s) if the mint operation is transferring the token(s) to itself. In all other cases the `KIP37TokenReceiver` rules MUST be followed as appropriate for the implementation (i.e., safe, custom and/or hybrid).

##### magic value calculation

- the keccak256 generated constants for the various magic values (these MAY be used by implementation):

```solidity
bytes4 constant public KIP37_KIP13 = 0x6433ca1f; // KIP-13 identifier for the main token standard.
bytes4 constant public KIP37_KIP13_TOKENRECEIVER = 0x7cc2d017; // KIP-13 identifier for the `KIP37TokenReceiver` support (i.e. `bytes4(keccak256(&quot;onKIP37Received(address,address,uint256,uint256,bytes)&quot;)) ^ bytes4(keccak256(&quot;onKIP37BatchReceived(address,address,uint256[],uint256[],bytes)&quot;))`).
bytes4 constant public KIP37_ACCEPTED = 0xe78b3325; // Return value from `onKIP37Received` call if a contract accepts receipt (i.e `bytes4(keccak256(&quot;onKIP37Received(address,address,uint256,uint256,bytes)&quot;))`).
bytes4 constant public KIP37_BATCH_ACCEPTED = 0x9b49e332; // Return value from `onKIP37BatchReceived` call if a contract accepts receipt (i.e `bytes4(keccak256(&quot;onKIP37BatchReceived(address,address,uint256[],uint256[],bytes)&quot;))`).
```

### Metadata

The URI value allows for ID substitution by clients. If the string `{id}` exists in any URI, clients MUST replace this with the actual token ID in hexadecimal form. This allows for a large number of tokens to use the same on-chain string by defining a URI once, for that large number of tokens.

- The string format of the substituted hexadecimal ID MUST be lowercase alphanumeric: `[0-9a-f]` with no 0x prefix.
- The string format of the substituted hexadecimal ID MUST be leading zero padded to 64 hex characters length if necessary.

Example of such a URI: `https://token-cdn-domain/{id}.json` would be replaced with `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json` if the client is referring to token ID 314592/0x4CCE0.

#### Metadata Extension

The optional `KIP37Metadata_URI` extension can be identified with the (KIP-13 Standard Interface Detection)[https://kips.klaytn.com/KIPs/kip-13].

If the optional `KIP37Metadata_URI` extension is included:

- The KIP-13 `supportsInterface` function MUST return the constant value `true` if `0x0e89341c` is passed through the `interfaceID` argument.
- _Changes_ to the URI MUST emit the `URI` event if the change can be expressed with an event (i.e. it isn&apos;t dynamic/programmatic).
  - An implementation MUST emit the `URI` event during a creation operation if `URI` of the new token type should be propagated to observers.
  - An implementation MAY emit the `URI` event during a mint operation but it is NOT mandatory. An observer MAY fetch the metadata uri at mint time from the `uri` function if it was not emitted.
- The `uri` function SHOULD be used to retrieve values if no event was emitted.
- The `uri` function MUST return the same value as the latest event for an `_id` if it was emitted.
- The `uri` function MUST NOT be used to check for the existence of a token as it is possible for an implementation to return a valid string even if the token does not exist.

```solidity
pragma solidity 0.5.6;

/**
    Note: The KIP-13 identifier for this interface is 0x0e89341c.
*/
interface KIP37Metadata_URI {
    /**
        @notice A distinct Uniform Resource Identifier (URI) for a given token.
        @dev URIs are defined in RFC 3986.
        The URI MUST point to a JSON file that conforms to the &quot;KIP-37 Metadata URI JSON Schema&quot;.
        @return URI string
    */
    function uri(uint256 _id) external view returns (string memory);
}
```

#### KIP-37 Metadata URI JSON Schema

This JSON schema is loosely based on the &quot;KIP-17 Metadata JSON Schema&quot;, but includes optional formatting to allow for ID substitution by clients. If the string `{id}` exists in any JSON value, it MUST be replaced with the actual token ID, by all client software that follows this standard.

- The string format of the substituted hexadecimal ID MUST be lowercase alphanumeric: `[0-9a-f]` with no 0x prefix.
- The string format of the substituted hexadecimal ID MUST be leading zero padded to 64 hex characters length if necessary.

```json
{
  &quot;title&quot;: &quot;Token Metadata&quot;,
  &quot;type&quot;: &quot;object&quot;,
  &quot;properties&quot;: {
    &quot;name&quot;: {
      &quot;type&quot;: &quot;string&quot;,
      &quot;description&quot;: &quot;Identifies the asset to which this token represents&quot;
    },
    &quot;decimals&quot;: {
      &quot;type&quot;: &quot;integer&quot;,
      &quot;description&quot;: &quot;The number of decimal places that the token amount should display - e.g. 18, means to divide the token amount by 1000000000000000000 to get its user representation.&quot;
    },
    &quot;description&quot;: {
      &quot;type&quot;: &quot;string&quot;,
      &quot;description&quot;: &quot;Describes the asset to which this token represents&quot;
    },
    &quot;image&quot;: {
      &quot;type&quot;: &quot;string&quot;,
      &quot;description&quot;: &quot;A URI pointing to a resource with mime type image/* representing the asset to which this token represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive.&quot;
    },
    &quot;properties&quot;: {
      &quot;type&quot;: &quot;object&quot;,
      &quot;description&quot;: &quot;Arbitrary properties. Values may be strings, numbers, object or arrays.&quot;
    }
  }
}
```

An example of a KIP-37 Metadata JSON file follows. The properties array proposes some SUGGESTED formatting for token-specific display properties and metadata.

```json
{
  &quot;name&quot;: &quot;Asset Name&quot;,
  &quot;decimals&quot;: 18,
  &quot;description&quot;: &quot;Lorem ipsum...&quot;,
  &quot;image&quot;: &quot;https://s3.amazonaws.com/your-bucket/images/{id}.png&quot;,
  &quot;properties&quot;: {
    &quot;simple_property&quot;: &quot;example value&quot;,
    &quot;rich_property&quot;: {
      &quot;name&quot;: &quot;Name&quot;,
      &quot;value&quot;: &quot;123&quot;,
      &quot;display_value&quot;: &quot;123 Example Value&quot;,
      &quot;class&quot;: &quot;emphasis&quot;,
      &quot;css&quot;: {
        &quot;color&quot;: &quot;#ffffff&quot;,
        &quot;font-weight&quot;: &quot;bold&quot;,
        &quot;text-decoration&quot;: &quot;underline&quot;
      }
    },
    &quot;array_property&quot;: {
      &quot;name&quot;: &quot;Name&quot;,
      &quot;value&quot;: [1, 2, 3, 4],
      &quot;class&quot;: &quot;emphasis&quot;
    }
  }
}
```

##### Localization

Metadata localization should be standardized to increase presentation uniformity across all languages. As such, a simple overlay method is proposed to enable localization. If the metadata JSON file contains a `localization` attribute, its content MAY be used to provide localized values for fields that need it. The `localization` attribute should be a sub-object with three attributes: `uri`, `default` and `locales`. If the string `{locale}` exists in any URI, it MUST be replaced with the chosen locale by all client software.

##### JSON Schema

```json
{
  &quot;title&quot;: &quot;Token Metadata&quot;,
  &quot;type&quot;: &quot;object&quot;,
  &quot;properties&quot;: {
    &quot;name&quot;: {
      &quot;type&quot;: &quot;string&quot;,
      &quot;description&quot;: &quot;Identifies the asset to which this token represents&quot;
    },
    &quot;decimals&quot;: {
      &quot;type&quot;: &quot;integer&quot;,
      &quot;description&quot;: &quot;The number of decimal places that the token amount should display - e.g. 18, means to divide the token amount by 1000000000000000000 to get its user representation.&quot;
    },
    &quot;description&quot;: {
      &quot;type&quot;: &quot;string&quot;,
      &quot;description&quot;: &quot;Describes the asset to which this token represents&quot;
    },
    &quot;image&quot;: {
      &quot;type&quot;: &quot;string&quot;,
      &quot;description&quot;: &quot;A URI pointing to a resource with mime type image/* representing the asset to which this token represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive.&quot;
    },
    &quot;properties&quot;: {
      &quot;type&quot;: &quot;object&quot;,
      &quot;description&quot;: &quot;Arbitrary properties. Values may be strings, numbers, object or arrays.&quot;
    },
    &quot;localization&quot;: {
      &quot;type&quot;: &quot;object&quot;,
      &quot;required&quot;: [&quot;uri&quot;, &quot;default&quot;, &quot;locales&quot;],
      &quot;properties&quot;: {
        &quot;uri&quot;: {
          &quot;type&quot;: &quot;string&quot;,
          &quot;description&quot;: &quot;The URI pattern to fetch localized data from. This URI should contain the substring `{locale}` which will be replaced with the appropriate locale value before sending the request.&quot;
        },
        &quot;default&quot;: {
          &quot;type&quot;: &quot;string&quot;,
          &quot;description&quot;: &quot;The locale of the default data within the base JSON&quot;
        },
        &quot;locales&quot;: {
          &quot;type&quot;: &quot;array&quot;,
          &quot;description&quot;: &quot;The list of locales for which data is available. These locales should conform to those defined in the Unicode Common Locale Data Repository (http://cldr.unicode.org/).&quot;
        }
      }
    }
  }
}
```

##### Localized Sample

Base URI:

```json
{
  &quot;name&quot;: &quot;Advertising Space&quot;,
  &quot;description&quot;: &quot;Each token represents a unique Ad space in the city.&quot;,
  &quot;localization&quot;: {
    &quot;uri&quot;: &quot;ipfs://QmWS1VAdMD353A6SDk9wNyvkT14kyCiZrNDYAad4w1tKqT/{locale}.json&quot;,
    &quot;default&quot;: &quot;en&quot;,
    &quot;locales&quot;: [&quot;en&quot;, &quot;es&quot;, &quot;fr&quot;]
  }
}
```

es.json:

```json
{
  &quot;name&quot;: &quot;Espacio Publicitario&quot;,
  &quot;description&quot;: &quot;Cada token representa un espacio publicitario único en la ciudad.&quot;
}
```

fr.json:

```json
{
  &quot;name&quot;: &quot;Espace Publicitaire&quot;,
  &quot;description&quot;: &quot;Chaque jeton représente un espace publicitaire unique dans la ville.&quot;
}
```

### Minting Extension

The **minting extension** is OPTIONAL for KIP-37 smart contracts. This allows your contract to create and mint a new token. Here, &apos;create&apos; means creating a new kind of token by assigning a new token ID, and &apos;mint&apos; means issuing additional tokens that have already been created.

The optional `KIP37Mintable` extension can be identified with the (KIP-13 Standard Interface Detection)[https://kips.klaytn.com/KIPs/kip-13].

If the optional `KIP37Mintable` extension is included:

- The KIP-13 `supportsInterface` function MUST return the constant value `true` if `0xdfd9d9ec` is passed through the `interfaceID` argument.
- The `create` function is used to create a new token allocated with a new token id.
  - An implementation MUST emit the `URI` event during a create operation if the created token has its own metadata.
- When creating tokens, the total supply of the token ID must be increased by initial supply.
- When minting tokens, the total supply of the token ID must be increased by minted quantity.
- When minting/creating tokens, the `_from` argument MUST be set to `0x0` (i.e. zero address). See [Minting-creating and burning-destroying rules](#minting-creating-and-burning-destroying-rules).

```solidity
pragma solidity 0.5.6;

/// @title KIP-37 Multi Token Standard, optional minting extension
///  Note: the KIP-13 identifier for this interface is 0xdfd9d9ec.
interface IKIP37Mintable {
    /// @notice Creates a new token type and assigns _initialSupply to the minter.
    /// @dev Throws if `msg.sender` is not allowed to create.
    ///   Throws if the token id is already used.
    /// @param _id The token id to create.
    /// @param _initialSupply The amount of tokens being minted.
    /// @param _uri The token URI of the created token.
    /// @return A boolean that indicates if the operation was successful.
    function create(
        uint256 _id,
        uint256 _initialSupply,
        string calldata _uri
    ) external returns (bool);

    /// @notice Mints tokens of the specific token type `_id` and assigns the tokens according to the variables `_to` and `_value`.
    /// @dev Throws if `msg.sender` is not allowed to mint.
    ///   MUST emit an event `TransferSingle`.
    /// @param _id The token id to mint.
    /// @param _to The address that will receive the minted tokens.
    /// @param _value The quantity of tokens being minted.
    function mint(
        uint256 _id,
        address _to,
        uint256 _value
    ) external;

    /// @notice Mints tokens of the specific token type `_id` in a batch and assigns the tokens according to the variables `_toList` and `_values`.
    /// @dev Throws if `msg.sender` is not allowed to mint.
    ///   MUST emit one or more `TransferSingle` events.
    ///   MUST revert if the length of `_toList` is not the same as the length of `_values`.
    /// @param _id The token id to mint.
    /// @param _toList The list of addresses that will receive the minted tokens.
    /// @param _values The list of quantities of tokens being minted.
    function mint(
        uint256 _id,
        address[] calldata _toList,
        uint256[] calldata _values
    ) external;

    /// @notice Mints multiple KIP-37 tokens of the specific token types `_ids` in a batch and assigns the tokens according to the variables `_to` and `_values`.
    /// @dev Throws if `msg.sender` is not allowed to mint.
    ///   MUST emit one or more `TransferSingle` events or a single `TransferBatch` event.
    ///   MUST revert if the length of `_ids` is not the same as the length of `_values`.
    /// @param _to The address that will receive the minted tokens.
    /// @param _ids The list of the token ids to mint.
    /// @param _values The list of quantities of tokens being minted.
    function mintBatch(
        address _to,
        uint256[] calldata _ids,
        uint256[] calldata _values
    ) external;
}
```

### Burning Extension

The **burning extension** is OPTIONAL for KIP-37 smart contracts. This allows your contract to burn tokens.

The optional `KIP37Burnable` extension can be identified with the (KIP-13 Standard Interface Detection)[https://kips.klaytn.com/KIPs/kip-13].

If the optional `KIP37Burnable` extension is included, the following features MUST be implemented:

- The KIP-13 `supportsInterface` function MUST return the constant value `true` if `0x9e094e9e` is passed through the `interfaceID` argument.
- When burning tokens, the total supply of the token ID must be decreased by burned quantity.
- When minting/creating tokens, the `_from` argument MUST be set to `0x0` (i.e. zero address). See [Minting-creating and burning-destroying rules](#minting-creating-and-burning-destroying-rules).

```solidity
pragma solidity 0.5.6;

/// @title KIP-37 Multi Token Standard, optional burning extension
///  Note: the KIP-13 identifier for this interface is 0x9e094e9e.
interface IKIP37Burnable {
    /// @notice Burns specific KIP37 tokens.
    /// @dev Throws if `msg.sender` is not allowed to burn the token.
    ///   Throws if `_account` does not have `_value` tokens for `_id`.
    /// @param _account The account that owns tokens.
    /// @param _id The token id to burn.
    /// @param _value The token amount to burn.
    function burn(
        address _account,
        uint256 _id,
        uint256 _value
    ) external;

    /// @notice Burns multiple KIP37 tokens.
    /// @dev Throws if `msg.sender` is not allowed to burn the tokens.
    ///   Throws if `_account` does not have tokens to be burnt.
    /// @param _account The account that owns tokens.
    /// @param _ids The list of the token ids to burn.
    /// @param _values The list of the token amounts to burn.
    function burnBatch(
        address _account,
        uint256[] calldata _ids,
        uint256[] calldata _values
    ) external;
}
```

### Pausing Extension

The **pausing extension** is OPTIONAL for KIP-37 smart contracts. This allows your contract or specific token to be suspended from transferring.

The optional `KIP37Pausable` extension can be identified with the (KIP-13 Standard Interface Detection)[https://kips.klaytn.com/KIPs/kip-13].

If the optional `KIP37Pausable` extension is included:

- The KIP-13 `supportsInterface` function MUST return the constant value `true` if `0x0e8ffdb7` is passed through the `interfaceID` argument.

```solidity
pragma solidity 0.5.6;

/// @title KIP-37 Multi Token Standard, optional pausing extension
///  Note: the KIP-13 identifier for this interface is 0x0e8ffdb7.
interface IKIP37Pausable {
    /// @notice Checks whether the whole contract is paused.
    /// @return True if the contract is paused, false otherwise.
    function paused() external view returns (bool);

    /// @notice Pauses actions related to transfer and approval in the contract.
    /// @dev Throws if `msg.sender` is not allowed to pause.
    ///   Throws if the contract is paused.
    function pause() external;

    /// @notice Resumes from the paused state of the contract.
    /// @dev Throws if `msg.sender` is not allowed to unpause.
    ///   Throws if the contract is not paused.
    function unpause() external;

    /// @notice Checks whether the specific token is paused.
    /// @return True if the specific token is paused, false otherwise
    function paused(uint256 _id) external view returns (bool);

    /// @notice Pauses actions related to transfer and approval of the specific token.
    /// @dev Throws if `msg.sender` is not allowed to pause.
    ///   Throws if the specific token is paused.
    function pause(uint256 _id) external;

    /// @notice Resumes from the paused state of the specific token.
    /// @dev Throws if `msg.sender` is not allowed to unpause.
    ///   Throws if the specific token is not paused.
    function unpause(uint256 _id) external;
}
```

### Approval

The function `setApprovalForAll` allows an operator to manage one&apos;s entire set of tokens on behalf of the approver.
The counterpart `isApprovedForAll` provides introspection into any status set by `setApprovalForAll`.

An owner SHOULD be assumed to always be able to operate on their own tokens regardless of approval status, so SHOULD NOT have to call `setApprovalForAll` to approve themselves as an operator before they can operate on them.

## Rationale

### Metadata Choices

The `symbol` function (found in the KIP-7 and KIP-17 standards) was not included as we do not believe this is a globally useful piece of data to identify a generic virtual item / asset and are also prone to collisions. Short-hand symbols are used in tickers and currency trading, but they aren&apos;t as useful outside of that space.

The `name` function (for human-readable asset names, on-chain) was removed from the standard to allow the Metadata JSON to be the definitive asset name and reduce duplication of data. This also allows localization for names, which would otherwise be prohibitively expensive if each language string was stored on-chain, not to mention bloating the standard interface. While this decision may add a small burden on implementers to host a JSON file containing metadata, we believe any serious implementation of KIP-37 will already utilize JSON Metadata.

### Upgrades

The requirement to emit `TransferSingle` or `TransferBatch` on balance change implies that a valid implementation of KIP-37 redeploying to a new contract address MUST emit events from the new contract address to replicate the deprecated contract final state. It is valid to only emit a minimal number of events to reflect only the final balance and omit all the transactions that led to that state. The event emit requirement is to ensure that the current state of the contract can always be traced only through events. To alleviate the need to emit events when changing contract address, consider using the proxy pattern. This will also have the added benefit of providing a stable contract address for users.

### Design decision: Supporting non-batch

The standard supports `safeTransferFrom` and `onKIP37Received` functions because they are significantly cheaper for single token-type transfers, which is arguably a common use case.

### Design decision: Safe transfers only

The standard only supports safe-style transfers, making it possible for receiver contracts to depend on `onKIP37Received` or `onKIP37BatchReceived` function to be always called at the end of a transfer.

### Guaranteed log trace

The KIP-37 standard guarantees that event logs emitted by the smart contract will provide enough data to create an accurate record of all current token balances. A database or explorer may listen to events and be able to provide indexed and categorized searches of every KIP-37 token in the contract.

### Approval

The function `setApprovalForAll` allows an operator to manage one&apos;s entire set of tokens on behalf of the approver. It enables frictionless interaction with exchange and trade contracts.

Restricting approval to a certain set of token IDs, quantities or other rules MAY be done with an additional interface or an external contract. The rationale is to keep the KIP-37 standard as generic as possible for all use-cases without imposing a specific approval scheme on implementations that may not need it.

## Backwards Compatibility

There have been requirements during the design discussions to have this standard be compatible with existing standards when sending to contract addresses, specifically KIP-17 at time of writing.
To cater for this scenario, there is some leeway with the revert logic should a contract not implement the `KIP37TokenReceiver` as per [Safe Transfer Rules](#safe-transfer-rules) section above, specifically &quot;Scenario#3 : The receiver does not implement the necessary `KIP37TokenReceiver` interface function(s)&quot;.

Hence in a hybrid KIP-37 contract implementation an extra call MUST be made on the recipient contract and checked before any hook calls to `onKIP37Received` or `onKIP37BatchReceived` are made.
The order of operation MUST therefore be:

1. The implementation MUST call the function `supportsInterface(0x7cc2d017)` on the recipient contract, providing at least 10,000 gas.
2. If the function call succeeds and the return value is the constant value `true` the implementation proceeds as a regular KIP-37 implementation, with the call(s) to the `onKIP37Received` or `onKIP37BatchReceived` hooks and rules associated.
3. If the function call fails or the return value is NOT the constant value `true` the implementation can assume the recipient contract is not a `KIP37TokenReceiver` and follow its other standard&apos;s rules for transfers.

## Usage

This standard can be used to represent multiple token types for an entire domain. Both fungible and non-fungible tokens can be stored in the same smart-contract.

### Batch Transfers

The `safeBatchTransferFrom` function allows for batch transfers of multiple token IDs and values. The design of KIP-37 makes batch transfers possible without the need for a wrapper contract, as with existing token standards. This reduces gas costs when more than one token type is included in a batch transfer, as compared to single transfers with multiple transactions.

Another advantage of standardized batch transfers is the ability for a smart contract to respond to the batch transfer in a single operation using `onKIP37BatchReceived`.

It is RECOMMENDED that clients and wallets sort the token IDs and associated values (in ascending order) when posting a batch transfer, as some KIP-37 implementations offer significant gas cost savings when IDs are sorted. See [Horizon Games - Multi-Token Standard](https://github.com/horizon-games/multi-token-standard) &quot;packed balance&quot; implementation for an example of this.

### Batch Balance

The `balanceOfBatch` function allows clients to retrieve balances of multiple owners and token IDs with a single call.

### Enumerating from events

In order to keep storage requirements light for contracts implementing KIP-37, enumeration (discovering the IDs and values of tokens) must be done using event logs. It is RECOMMENDED that clients such as exchanges and blockchain explorers maintain a local database containing the token ID, Supply, and URI at the minimum. This can be built from each TransferSingle, TransferBatch, and URI event, starting from the block the smart contract was deployed until the latest block.

KIP-37 contracts must therefore carefully emit `TransferSingle` or `TransferBatch` events in any instance where tokens are created, minted, transferred or destroyed.

### Non-Fungible Tokens

The following strategies are examples of how you MAY mix fungible and non-fungible tokens together in the same contract. The standard does NOT mandate how an implementation must do this.

##### Split ID bits

The top 128 bits of the uint256 `_id` parameter in any KIP-37 function MAY represent the base token ID, while the bottom 128 bits MAY represent the index of the non-fungible to make it unique.

Non-fungible tokens can be interacted with using an index based accessor into the contract/token data set. Therefore to access a particular token set within a mixed data contract and a particular non-fungible within that set, `_id` could be passed as `&lt;uint128: base token id&gt;&lt;uint128: index of non-fungible&gt;`.

To identify a non-fungible set/category as a whole (or a fungible) you COULD just pass in the base id via the `_id` argument as `&lt;uint128: base token id&gt;&lt;uint128: zero&gt;`. If your implementation uses this technique this naturally means the index of a non-fungible SHOULD be 1-based.

Inside the contract code the two pieces of data needed to access the individual non-fungible can be extracted with uint128(~0) and the same mask shifted by 128.

```solidity
uint256 baseTokenNFT = 12345 &lt;&lt; 128;
uint128 indexNFT = 50;

uint256 baseTokenFT = 54321 &lt;&lt; 128;

balanceOf(baseTokenNFT, msg.sender); // Get balance of the base token for non-fungible set 12345 (this MAY be used to get a balance of the user for all of this token set if the implementation wishes as a convenience).
balanceOf(baseTokenNFT + indexNFT, msg.sender); // Get balance of the token at index 50 for non-fungible set 12345 (should be 1 if user owns the individual non-fungible token or 0 if they do not).
balanceOf(baseTokenFT, msg.sender); // Get balance of the fungible base token 54321.
```

Note that 128 is an arbitrary number, an implementation MAY choose how they would like this split to occur as suitable for their use case. An observer of the contract would simply see events showing balance transfers and mints happening and MAY track the balances using that information alone.
For an observer to be able to determine type (non-fungible or fungible) from an ID alone they would have to know the split ID bits format on an implementation by implementation basis.

##### Natural Non-Fungible tokens

Another simple way to represent non-fungibles is to allow a maximum value of 1 for each non-fungible token. This would naturally mirror the real world, where unique items have a quantity of 1 and fungible items have a quantity greater than 1.

## Copyright

Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
</description>
        <pubDate>Wed, 19 Aug 2020 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-37</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-37</guid>
      </item>
    
      <item>
        <title>Dynamic Gas Fee Pricing Mechanism</title>
        <category>Core</category>
        
          <comments>https://forum.klaytn.com/t/en-new-transaction-fee-mechanism-dynamic-gas-price-proposal/4844</comments>
        
        <description>&lt;!--You can leave these HTML comments in your merged KIP and delete the visible duplicate text guides, they will not appear and may be helpful to refer to if you edit it again. This is the suggested template for new KIPs. Note that a KIP number will be assigned by an editor. When opening a pull request to submit your KIP, please use an abbreviated title in the filename, `kip-draft_title_abbrev.md`. The title should be 44 characters or less.--&gt;

## Simple Summary
&lt;!--&quot;If you can&apos;t explain it simply, you don&apos;t understand it well enough.&quot; Provide a simplified and layman-accessible explanation of the KIP.--&gt;
A dynamic gas fee pricing mechanism based on network usage, that includes fixed-per-block network fee that is partially burned.

## Abstract
&lt;!--A short (~200 word) description of the technical issue being addressed.--&gt;
There is a base fee per gas in protocol, which can move up or down for each block according to a formula which is a function of the gas used in the parent block and the gas target. The algorithm results in the base fee per gas increasing when the gas used in the previous block is above the gas target, and decreasing when the gas used in the previous block is below the gas target. It repeats until base fee reaches the lower bound fee or the upper bound fee. The base fee per gas is partially burned. Transactions specify the maximum fee per gas they are willing to pay in total. The transaction will always pay the base fee per gas of the block it was included in.
 

## Motivation
&lt;!--The motivation is critical for KIPs that want to change the Klaytn protocol. It should clearly explain why the existing protocol specification is inadequate to address the problem that the KIP solves. KIP submissions without sufficient motivation may be rejected outright.--&gt;

Klaytn has been following a policy of “fixed gas price“, with the gas price fixed at a certain level (e.g. 25 ston and 750 ston). This approach was adopted in the initial phase because it allows users to easily send transactions without having to predict the gas fee, while minimizing the volatility of transaction fees. But in the periods leading up to the gas price increase, we have seen a lot of transaction bursts on Klaytn with the following problems:

* **Delay of transaction execution** : A large number of transactions were being generated at the same time, leading to increased process time for users. The fundamental cause to this was the sudden surge in transactions. But there were cases of thousands of transactions being fired to process a certain transaction. And the reason behind this repeated phenomenon was the absence of an algorithm that prioritizes Klaytn transactions as well as of an appropriate gas price policy.
* **Storage overload** : The aforementioned a large number of transactions are created mostly by bots, and are mostly reverted and useless transactions. These transactions strain the Klaytn storage in the long-term, which can hinder Klaytn from providing quick transaction finality and stable network.
* **Inefficient resource allocation due to difficulty in rational price determination** : Gas price should neither be too high or too low. A too low gas price increases vulnerability to DoS (denial-of-service) attacks and transaction delay for users. When it’s too high, the transaction cost is going to prevent users from using the network unreservedly. With the current fixed gas price policy, however, it is difficult to determine which price is appropriate, since the congestion status of the Klaytn network is constantly changing.
* **Inability to respond quickly and flexibly** : Gas price needs to go up during transaction spikes and go down when the network is not congested. But under the current fixed gas price scheme, it is not easy to analyze and change the gas price based on the network congestion situation.

A burn mechanism is needed to be introduced on Klaytn while solving the above problems, in order to link the growth of the ecosystem with KLAY value.

(This proposal only deals with dynamic fee policy, and a FIFO (First In First Out) transaction priority algorithm is already implemented. See https://github.com/klaytn/klaytn/pull/1282 and https://github.com/klaytn/klaytn/pull/1309)

## Specification
This specification derived heavily from Ethereum&apos;s [ERC-1559](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md) written by Vitalik Buterin (@vbuterin), Eric Conner (@econoar), Rick Dudley (@AFDudley), Matthew Slipper (@mslipper), Ian Norden (@i-norden), and Abdelhamid Bakhta (@abdelhamidbakhta).

Transactions under a dynamic gas fee policy consist of `base_fee_per_gas`, which are dynamically controlled according to the network congestion status. Network congestion is measured by `gas_used` which is the gas usage by blocks created on Klaytn. `base_fee_per_gas` changes every block.

When a consensus node creates a block, if the `gas_used` of the parent block exceeds `gas_target`, `base_fee_per_gas` would go up. On the other hand, if the `gas_used` is lower than `gas_target`, the `base_fee_per_gas` would be reduced. This process would be repeated until the `base_fee_per_gas` doesn’t exceed `lower_bound` or `upper_bound`. The block proposer would receive a part of the fee of transactions included in the block, and the rest would be burned.

The below shows pseudo code of this proposal.
_Note: // is integer division, round down._

```python
from asyncio.windows_events import NULL
from typing import Union, Dict, Sequence, List, Tuple, Literal
from dataclasses import dataclass, field
from abc import ABC, abstractmethod


# Since Klaytn has multiple transaction types,
# only 3 transaction types are defined here for sake of simplicity.
@dataclass
class TxTypeLegacyTransaction:
	value: int = 0
	to: int = 0
	input: bytes = bytes()
	v: int = 0
	r: int = 0
	s: int = 0
	nonce: int = 0
	gas: int = 0
	gas_price: int = 0

@dataclass
class TxTypeFeeDelegatedSmartContractExecution:
	type: int = 0x09
	nonce: int = 0
	gas_price: int = 0
	gas: int = 0
	to: int = 0
	value: int = 0
	from: int = 0
	input: bytes = bytes()
	tx_signatures: List[int] = field(default_factory=list)
	fee_payer: int = 0 
	fee_payer_signatures: List[int] = field(default_factory=list)

@dataclass
class TxTypeFeeDelegatedSmartContractExecutionWithRatio:
	type: int = 0x32
	nonce: int = 0
	gas_price: int = 0
	gas: int = 0
	to: int = 0
	value: int = 0
	from: int = 0
	input: bytes = bytes()
	fee_ratio: int = 1
	tx_signatures: List[int] = field(default_factory=list)
	fee_payer: int = 0 
	fee_payer_signatures: List[int] = field(default_factory=list)


# In Klaytn, Ethereum transaction types are supported by adding EthereumTxTypeEnvelope(0x78).
# RawTransaction : EthereumTxTypeEnvelope || EthereumTransactionType || TransactionPayload
# (|| is the byte/byte-array concatenation operator.)
# In this proposal, those concatenation is intentionally ommited for simplification.

# Transaction2930 in Ethereum. 
@dataclass
class TxTypeEthereumAccessList:
	type: int = 0x7801
	chain_id: int = 0
	nonce: int = 0
	gas_price: int = 0
	gas: int = 0
	to: int = 0
	value: int = 0
	data: bytes = bytes()
	access_list: List[Tuple[int, List[int]]] = field(default_factory=list)
	v: int = 0
	r: int = 0
	s: int = 0

# Transaction1559 in Ethereum.
@dataclass
class TxTypeEthereumDynamicFee:
	type: int = 0x7802
	chain_id: int = 0
	nonce: int = 0
	gas_tip_cap: int = 0
	gas_fee_cap: int = 0
	gas: int = 0
	to: int = 0
	value: int = 0
	data: bytes = bytes()
	access_list: List[Tuple[int, List[int]]] = field(default_factory=list)
	v: int = 0
	r: int = 0
	s: int = 0


EthereumTransactions = Union[TxTypeEthereumDynamicFee, TxTypeEthereumAccessList]

Transaction = Union[TxTypeLegacyTransaction, TxTypeFeeDelegatedSmartContractExecution, TxTypeFeeDelegatedSmartContractExecutionWithRatio, EthereumTransactions]

@dataclass
class NormalizedTransaction:
	signer_address: int = 0
	signer_nonce: int = 0
	max_fee_per_gas: int = 0
	gas_limit: int = 0
	to: int = 0
	value: int = 0
	data: bytes = bytes()
	fee_payer_address: int = 0
	fee_ratio: int = 1

@dataclass
class Block:
	hash: int = 0
	parent_hash: int = 0
	base_fee_per_gas: int = 0
	block_score: int = 0
	extra_data: bytes = bytes()
	gas_used: int = 0 
	governance_data: bytes = bytes()
	logs_bloom: int = 0
	number: int = 0
	transaction_receipt_root: int = 0
	reward: int = 0
	state_root: int = 0
	timestamp: int = 0
	timestamp_FoS: int = 0
	transaction_root: int = 0
	nonce: int = 0
	committee: List[int] = field(default_factory=list)
	proposer: int = 0 
	size: int = 0 


@dataclass
class Account:
	type: int = 0
	nonce: int = 0
	humanReadable: bool = False 
	address: int = 0
	key: int = 0 
	balance: int = 0
	storage_root: int = 0
	code_hash: int = 0
	code_format: int = 0
	vm_version: int = 0

INITIAL_FORK_BLOCK_NUMBER = 107806544 # TBD
BASE_FEE_DELTA_REDUCING_DENOMINATOR = 20 # To set max basefee change at 5% per block. Can be changed later by governance.
LOWER_BOUND_BASE_FEE = 25000000000 # 25 ston, can be changed later by governance.
UPPER_BOUND_BASE_FEE = 750000000000 # 750 ston, can be changed later by governance.
GAS_TARGET = 30000000 
MAX_BLOCK_GAS_USED_FOR_BASE_FEE = 60000000 # implicit block gas limit to enforce the max basefee change rate
BURN_RATIO = 0.5 # cannot be changed by governance.
CN_DISTRIBUTION_RATIO = 0.34 # set by governance
KGF_DISTRIBUTION_RATIO = 0.54 # set by governance
KIR_DISTRIBUTION_RATIO = 0.12 # set by governance

class World(ABC):
	def validate_block(self, block: Block) -&gt; None:

		parent_base_fee_per_gas = self.parent(block).base_fee_per_gas
		transactions = self.transactions(block)
		parent_gas_used = self.parent(block).gas_used

		lower_bound_base_fee = LOWER_BOUND_BASE_FEE
		upper_bound_base_fee = UPPER_BOUND_BASE_FEE

		# if LOWER_BOUND_BASE_FEE is not a even number, make it even by adding 1
		if lower_bound_base_fee &amp; 0x1:
			lower_bound_base_fee += 1

		# if UPPER_BOUND_BASE_FEE ix not a even number, make it even by subtracting 1
		if upper_bound_base_fee &amp; 0x1:
			upper_bound_base_fee -= 1

		if parent_gas_used &gt; MAX_BLOCK_GAS_USED_FOR_BASE_FEE:
			parent_gas_used = MAX_BLOCK_GAS_USED_FOR_BASE_FEE

		# check if the base fee is correct
		if INITIAL_FORK_BLOCK_NUMBER == block.number:
			expected_base_fee_per_gas = lower_bound_base_fee

		elif parent_gas_used == GAS_TARGET:
			expected_base_fee_per_gas = parent_base_fee_per_gas

		elif parent_gas_used &gt; GAS_TARGET:
			gas_used_delta = parent_gas_used - GAS_TARGET
			base_fee_per_gas_delta = max(parent_base_fee_per_gas * gas_used_delta // GAS_TARGET // BASE_FEE_DELTA_REDUCING_DENOMINATOR, 1)
			expected_base_fee_per_gas = parent_base_fee_per_gas + base_fee_per_gas_delta
			if expected_base_fee_per_gas &gt; upper_bound_base_fee:
				expected_base_fee_per_gas = upper_bound_base_fee
			
		else:
			gas_used_delta = GAS_TARGET - parent_gas_used
			base_fee_per_gas_delta = parent_base_fee_per_gas * gas_used_delta // GAS_TARGET // BASE_FEE_DELTA_REDUCING_DENOMINATOR
			expected_base_fee_per_gas = parent_base_fee_per_gas - base_fee_per_gas_delta
			if expected_base_fee_per_gas &lt; lower_bound_base_fee:
				expected_base_fee_per_gas = lower_bound_base_fee

		# Make it a even number by subtracting 1
		# Since the lower bound is even number at this moment, we are sure that the value will not be lower than the lower bound.
		if expected_base_fee_per_gas &amp; 0x1:
			expected_base_fee_per_gas -= 1

		assert expected_base_fee_per_gas == block.base_fee_per_gas, &apos;invalid block: base fee not correct&apos;

		# execute transactions and do gas accounting
		cumulative_transaction_gas_used = 0
		for unnormalized_transaction in transactions:
			# Note: this validates transaction signature and chain ID which must happen before we normalize below since normalized transactions don&apos;t include signature or chain ID
			signer_address = self.validate_and_recover_signer_address(unnormalized_transaction)
			transaction = self.normalize_transaction(unnormalized_transaction, signer_address)
			signer = self.account(signer_address)

			signer.balance -= transaction.amount
			assert signer.balance &gt;= 0, &apos;invalid transaction: signer does not have enough KLAY to cover attached value&apos;
			
			# fee delegation transaction accounting is needed 
			# the signer must be able to afford the transaction
			assert fee_payer.balance &gt;= transaction.gas_limit * transaction.max_fee_per_gas

			# ensure that the user was willing to at least pay the base fee
			assert transaction.max_fee_per_gas &gt;= block.base_fee_per_gas

			# Prevent impossibly large numbers
			assert transaction.max_fee_per_gas &lt; 2**256

			fee_payer.balance -= transaction.gas_limit * block.base_fee_per_gas
			assert fee_payer.balance &gt;= 0, &apos;invalid transaction: signer does not have enough KLAY to cover gas&apos;

			gas_used = self.execute_transaction(transaction, block.base_fee_per_gas)
			gas_refund = transaction.gas_limit - gas_used
			cumulative_transaction_gas_used += gas_used

			# signer gets refunded for unused gas
			fee_payer.balance += gas_refund * block.base_fee_per_gas

			# CN and KGF, KIR account only receives some propotion of the base fee(basefee burned)
			self.account(block.proposer).balance += gas_used * block.base_fee_per_gas * BURN_RATIO * CN_DISTRIBUTION_RATIO
			self.account(kgf_address).balance += gas_used * block.base_fee_per_gas * BURN_RATIO * KGF_DISTRIBUTION_RATIO
			self.account(kir_address).balance += gas_used * block.base_fee_per_gas * BURN_RATIO * KIR_DISTRIBUTION_RATIO

		# check if the block spent too much gas transactions
		assert cumulative_transaction_gas_used == block.gas_used, &apos;invalid block: gas_used does not equal total gas used in all transactions&apos;

		# validate the rest of the block

	def normalize_transaction(self, transaction: Transaction, signer_address: int) -&gt; NormalizedTransaction:
		# legacy transactions
		if isinstance(transaction, TxTypeLegacyTransaction):
			return NormalizedTransaction(
				signer_address = signer_address,
				signer_nonce = transaction.nonce,
				max_fee_per_gas = transaction.gas_price,
				gas_limit = transaction.gas,
				to = transaction.to,
				value = transaction.value,
				data = transaction.input,
				fee_payer_address = None,
				fee_ratio = None
			)

		elif isinstance(transaction, TxTypeFeeDelegatedSmartContractExecution):
			return NormalizedTransaction(
				signer_address = signer_address,
				signer_nonce = transaction.nonce,
				max_fee_per_gas = transaction.gas_price,
				gas_limit = transaction.gas,
				to = transaction.to,
				value = transaction.value,
				data = transaction.input,
				fee_payer_address = transaction.fee_payer,
				fee_ratio = None
			)
		
		elif isinstance(transaction, TxTypeFeeDelegatedSmartContractExecutionWithRatio):
			return NormalizedTransaction(
				signer_address = signer_address,
				signer_nonce = transaction.nonce,
				max_fee_per_gas = transaction.gas_price,
				gas_limit = transaction.gas,
				to = transaction.to,
				value = transaction.value,
				data = transaction.input,
				fee_payer_address = transaction.fee_payer,
				fee_ratio = transaction.fee_ratio
			)
		elif isinstance(transaction, TxTypeEthereumAccessList):
			return NormalizedTransaction(
				signer_address = signer_address,
				signer_nonce = transaction.nonce,
				max_fee_per_gas = transaction.gas_price,
				gas_limit = transaction.gas,
				to = transaction.to,
				value = transaction.value,
				data = transaction.data,
				fee_payer_address = None,
				fee_ratio = None
			)
		elif isinstance(transaction, TxTypeEthereumDynamicFee):
			return NormalizedTransaction(
				signer_address = signer_address,
				signer_nonce = transaction.nonce,
				max_fee_per_gas = transaction.gas_fee_cap,
				gas_limit = transaction.gas,
				to = transaction.to,
				value = transaction.value,
				data = transaction.data,
				fee_payer_address = None,
				fee_ratio = None
			)
		else:
			raise Exception(&apos;invalid transaction: unexpected number of items&apos;)

	@abstractmethod
	def parent(self, block: Block) -&gt; Block: pass

	@abstractmethod
	def block_hash(self, block: Block) -&gt; int: pass

	@abstractmethod
	def transactions(self, block: Block) -&gt; Sequence[Transaction]: pass

	# effective_gas_price is the value returned by the GASPRICE (0x3a) opcode
	@abstractmethod
	def execute_transaction(self, transaction: NormalizedTransaction, effective_gas_price: int) -&gt; int: pass

	@abstractmethod
	def validate_and_recover_signer_address(self, transaction: Transaction) -&gt; int: pass

	@abstractmethod
	def account(self, address: int) -&gt; Account: pass
```

## Expected Effect

The proposed dynamic gas price mechanism is expected to solve the following problems.
* Reduce transaction process delay and storage overcapacity
* Flexible gas price control depending on the network status
* Introduction of a burn mechanism

But it may give rise to the following changes:
* Edit gas price-related code of the ecosystem wallet and other tools
* Lower predictability of gas price

## Backwards Compatibility

* All previous transaction types will remain the same.
* Setting fixed gas price to send transactions will still work and be included in blocks, but those transactions will be not included in the block if a `base_fee_per_gas` is higher than the gas price.

## Reference
- https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md

## Copyright
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
</description>
        <pubDate>Thu, 14 Apr 2022 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-71</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-71</guid>
      </item>
    
      <item>
        <title>Implementing the on-chain governance voting method</title>
        <category>Core</category>
        
        <description>## Simple Summary
Introducing a new governance voting method based on the staking amount and implementation of the Klaytn Square, a governance portal

## Abstract
Klaytn introduces a stake-based governance model that provides voting power to governance participants. Currently, one vote per Governance Council(GC) member was cast. The new method will introduce the voting right that will be exercised based on the staking amount with the maximum cap to prevent an entity from making an arbitrary decision. The voting agenda is determined through discussion on the Klaytn Governance Forum, and the topic will be presented and voted on at the newly launched Governance Portal, Klaytn Square. This voting mechanism aims to provide responsibility and obligation of voting to Governance Councils.

## Motivation
The new on-chain voting mechanism discloses GC’s opinion transparently through the portal, allowing anyone to view the result. The governance agenda discussed in Klaytn Governance Forum will be voted on the governance portal, Klaytn Square. Since the voting power is calculated based on the staking amount, this voting process provides more power to GC members who share the same interest as Klaytn by staking and locking up more KLAYs.

## Specification
Klaytn Governance Forum is to freely propose and discuss Klaytn governance items. Once Klaytn Square, the governance portal, opens, the on-chain voting will be executed based on the discussion held in this forum.

Klaytn Square includes the following functions:
- Ability to vote on a governance agenda and view the voting process
- Information about Governance Councils: description, contract address, notice, staking status, staking and reward history, etc.
- View the history of governance agenda and Governance Councils

![voting process diagram](/assets/kip-81/voting_process_diagram.png)

The foundation will provide 7 days of preparation period for voting, providing a time for GC to adjust the staking amount. With the start of the voting, the foundation will announce the list of GC members and their voting power. GC will have 7 days of the voting period.

The Klaytn governance voting system is designed based on the following fundamental premises.
- Klaytn’s major decision-making process should reflect the opinions of as many participants as possible from the ecosystem.
- Participants will be more likely to make a decision that is beneficial to the Klaytn ecosystem if they hold more KLAY. This is based on the premise that the growth of Klaytn’s ecosystem is correlated to the rise in the value of KLAY.
- The governance system should be able to manage the situations in which a particular entity makes an arbitrary decision. This is because the entity may weaken the sustainability of the entire network.
- The act of voting and the gain of voting power is different.

Recognizing a tendency that the member with more KLAY makes a decision that benefits the Klaytn ecosystem in the long run, we are granting a higher voting power to members with more KLAY. Yet, to prevent a particular subject from making an arbitrary decision, we are placing a cap on the voting power one can receive at the maximum. Therefore, a GC member will receive 1 vote per a certain amount of staked KLAY (initial configuration: 5 million KLAY). The maximum number of votes a member can own is one less than the total number of GC members. In other words, [Maximum Voting Power =  Total number of GC members - 1]. For example, if there are 35 GC members, one can own a maximum of 34 voting power. This cap structure prevents potential monopolies.

### Smart Contracts Overview

The Klaytn on-chain governance voting will be conducted on smart contracts. Several contracts and accounts interact together in the process. The below diagram shows the relationship between contracts and accounts.

Find an example implementation [here](https://github.com/klaytn/governance-contracts-audit).

- Contracts
  - **AddressBook**: an existing contract that stores the list of GC nodes, their staking contracts, and their reward recipient addresses.
  - **CnStakingV2**: an updated version of the existing CnStakingContract. GCs stake their KLAYs to earn rights to validate blocks and cast on-chain votes.
  - **StakingTracker**: a new contract that tracks voting-related data from AddressBook and CnStakingV2 contracts.
  - **Voting**: a new contract that processes the on-chain voting. It stores governance proposals, counts votes, and sends approved transactions.
- Accounts
  - **AddressBook admins**: a set of accounts controlled by the Foundation which can manage the list of GCs in the AddressBook. AddressBook admins are managed in the AddressBook contract.
  - **CnStaking admins**: a set of accounts controlled by each GC that can manage the staked KLAYs and its voter account. CnStaking admins are managed in the respective CnStakingV2 contracts. Note that every CnStakingV2 contract has a different set of admins.
  - **Voter account**: an account controlled by each GC member that can cast on-chain votes. But this account cannot withdraw KLAYs from CnStakingV2 contracts. A Voter account is appointed at CnStakingV2 contracts of the respective GC member.
  - **Secretariat account**: an account controlled by the Foundation which can propose and execute on-chain governance proposals. The secretariat account is managed in the Voting contract.

![contracts and accounts](/assets/kip-81/smart_contract_relation.png)

### AdressBook

In Cypress mainnet and Baobab testnet, an [AddressBook contract](https://github.com/klaytn/klaytn/blob/v1.9.1/contracts/reward/contract/AddressBook.sol) is deployed at address `0x0000000000000000000000000000000000000400`. For the purpose of Voting, the following function of the AddressBook is used.

```solidity
interface IAddressBook {
    /// @dev Returns all addresses in the AddressBook.
    ///
    /// Each GC&apos;s addresses are stored in the same index of the three arrays.
    /// i.e. GC[i] is described in (nodeIds[i], stakingContracts[i], rewardAddresses[i])
    /// However, a GC may operate multiple staking contracts. In this case,
    /// the GC occupies multiple indices with the same reward address.
    ///
    /// @return nodeIds           GC consensus node address list
    /// @return stakingContracts  GC staking contract address list
    /// @return rewardAddresses   GC reward recipient address list
    /// @return pocAddress        PoC(KGF) recipient address
    /// @return kirAddress        KIR recepient address
    function getAllAddressInfo() external view returns(
        address[] memory nodeIds,
        address[] memory stakingContracts,
        address[] memory rewardAddresses,
        address pocAddress,
        address kirAddress);
}
```

### CnStakingV2

CnStakingV2 is an upgraded version of [CnStakingContract](https://github.com/klaytn/klaytn/blob/v1.9.1/contracts/cnstaking/CnStakingContract.sol). The CnStakingContract has been serving the purpose of locking GC stakes. V2 will add two new features related to StakingTracker, which will be described later.

- It holds a new unique identifier &quot;GC ID&quot; which is a nonzero integer assigned to each GC member. If a GC member owns multiple CnStakingV2 contracts, the contracts must contain the same GC ID value.
- Whenever its KLAY balance changes, V2 notifies the StakingTracker.
- V2 stores a voter account address, and notifies the StakingTracker whenever it changes. The voter account can be changed by its admins.

To support the new features, the following functions are added. The functions `setStakingTracker` and `setGCId` is only callable before contract [initialization](https://github.com/klaytn/klaytn/blob/v1.9.1/contracts/cnstaking/CnStakingContract.sol#L237). Functions `updateStakingTracker` and `updateVoterAddress` are invoked upon approval of CnStaking admins using the existing [CnStaking multisig facility](https://github.com/klaytn/klaytn/blob/v1.9.1/contracts/cnstaking/CnStakingContract.sol#L597).

```solidity
abstract contract CnStakingV2 {
    event UpdateStakingTracker(address stakingTracker);
    event UpdateVoterAddress(address voterAddress);
    event UpdateGCId(uint256 gcId);

    function stakingTracker() external view returns(address);
    function voterAddress() external view returns(address);
    function gcId() external view returns(uint256);

    /// @dev Set the initial stakingTracker address
    /// Emits an UpdateStakingTracker event.
    function setStakingTracker(address _tracker) external beforeInit;

    /// @dev Set the gcId
    /// The gcId never changes once initialized.
    /// Emits an UpdateGCId event.
    function setGCId(uint256 _gcId) external beforeInit;

    /// @dev Update the StakingTracker address this contract reports to
    /// Emits an UpdateStakingTracker event.
    function submitUpdateStakingTracker(address _tracker) external;
    function updateStakingTracker(address _tracker) external onlyMultisigTx;

    /// @dev Update the voter address of this GC
    /// Emits an UpdateVoterAddress event.
    function submitUpdateVoterAddress(address _addr) external;
    function updateVoterAddress(address _addr) external onlyMultisigTx;
}
```

#### Notifying changes to StakingTracker

To notify balance and voter account changes to the StakingTracker contract, the CnStakingV2 contract shall call the StakingTracker whenever after every change. For example,

```solidity
abstract contract CnStakingV2 {
    function depositLockupStakingAndInit() payable beforeInit() {
        // ...
        stakingTracker.refreshStake(address(this));
    }
    function stakeKlay() payable {
        // ...
        stakingTracker.refreshStake(address(this));
    }
    function withdrawLockupStaking(address _to, uint256 _value) {
        // ...
        _to.transfer(_value);
        stakingTracker.refreshStake(address(this));
    }
    function withdrawApprovedStaking(uint256 _approvedWithdrawalId) {
        // ...
        _to.transfer(_value);
        stakingTracker.refreshStake(address(this));
    }
    function updateVoterAddress(address _addr) {
        // ...
        voterAddress = _addr;
        stakingTracker.refreshVoter(address(this));
    }
}

interface IStakingTracker {
    /// @dev Re-evaluate Tracker contents related to the staking contract
    /// @param staking  The CnStaking contract address
    function refreshStake(address staking) external;

    /// @dev Update a GC&apos;s voter address from the given address
    /// @param staking  The CnStaking contract address
    function refreshVoter(address staking) external;
}
```

#### Version identifier

To distinguish the CnStakingContract and CnStakingV2 contracts, V2 will have VERSION value 2.

```solidity
abstract contract CnStakingV2 {
    uint256 constant public VERSION = 2;
}
```

#### Example implementation

See [here](https://github.com/klaytn/governance-contracts-audit/blob/main/contracts/CnStakingV2.sol) for an example implementation.

### StakingTracker

StakingTracker collects voting-related data of each GC member.

- Staking amounts and voting powers \
Stored separately in `Tracker` structs. Each `Tracker` struct can be updated between `trackStart` and `trackEnd` blocks, but becomes immutable afterward, essentially freezing the voting powers. A `Tracker` struct is first created in `createTracker()`, and updated by `refreshStake()`.

- Each GC’s voter accounts \
Stored in global mappings, and can be updated at any time. Voter account mappings are updated by `refreshVoter()`.

#### Contract interface

```solidity
abstract contract StakingTracker {
    struct Tracker {
        // Tracked block range.
        // Balance changes are only updated if trackStart &lt;= block.number &lt; trackEnd.
        uint256 trackStart;
        uint256 trackEnd;

        // Determined at createTracker() and does not change.
        uint256[] gcIds;
        mapping(uint256 =&gt; bool) gcExists;
        mapping(address =&gt; uint256) stakingToGCId;

        // Balances and voting powers.
        // First collected at createTracker() and updated at refreshStake() until trackEnd.
        mapping(address =&gt; uint256) stakingBalances; // staking address balances
        mapping(uint256 =&gt; uint256) gcBalances; // consolidated GC balances
        mapping(uint256 =&gt; uint256) gcVotes; // GC voting powers
        uint256 totalVotes;
        uint256 numEligible;
    }

    // Tracker objects. Added by createTracker().
    mapping(uint256 =&gt; Tracker) private trackers; // trackerId =&gt; Tracker
    uint256[] private allTrackerIds;

    // 1-to-1 mapping between nodeId and voter account.
    // Updated by refreshVoter().
    mapping(uint256 =&gt; address) public gcIdToVoter;
    mapping(address =&gt; uint256) public voterToGCId;

    // Minimum staking amount to have 1 vote
    function MIN_STAKE() external view returns(uint256);

    /// @dev Creates a new Tracker and populate initial values from AddressBook
    function createTracker(uint256 trackStart, uint256 trackEnd)
        external returns(uint256 trackerId);

    /// @dev Re-evaluate Tracker contents related to the staking contract
    /// Anyone can call this function, but `staking` must be a staking contract
    /// registered to the AddressBook.
    function refreshStake(address staking) external;

    /// @dev Re-evaluate voter account mapping related to the staking contract
    /// Anyone can call this function, but `staking` must be a staking contract
    /// registered to the AddressBook.
    /// Note that two different nodes cannot appoint the same voter address.
    function refreshVoter(address staking) external;

    /// @dev Return integer fields of a tracker
    function getTrackerSummary(uint256 trackerId) external view returns(
        uint256 trackStart,
        uint256 trackEnd,
        uint256 numGCs,
        uint256 totalVotes,
        uint256 numEligible)

    /// @dev Return balances and votes of all GC members stored in a tracker
    function getAllTrackedGCs(uint256 trackerId) external view returns(
        uint256[] memory gcIds,
        uint256[] memory gcBalances,
        uint256[] memory gcVotes)

    /// @dev Return the balance and votes of a specified GC member
    function getTrackedGC(uint256 trackerId, uint256 gcId) external view returns(
        uint256 gcBalance,
        uint256 gcVotes)
}
```

#### Usage

A voting contract can utilize StakingTracker as follows.

1. When a governance proposal is submitted, the voting contract calls `createTracker()` to finalize the eligible GC members list and evaluate voting powers.
2. Before `trackEnd` block, GC members stake or unstake their KLAYs from their CnStakingV2 contracts. The CnStakingV2 contracts will then call `refreshStake()` to notify the balance change.
3. GC members may change their voter account in their CnStakingV2 contracts. The CnStakingV2 contracts will then call `refreshVoter()` to notify voter account change.
4. After `trackEnd` block, the voting powers are frozen. The voting contract uses StakingTracker getters to process votes cast by voter accounts.

#### Example implementation

See [here](https://github.com/klaytn/governance-contracts-audit/blob/main/contracts/StakingTracker.sol) for an example implementation.

### Voting

The Voting contract operates the on-chain governance votes. The Voting contract stores governance proposals, counts casted votes, and sends on-chain transactions. Its design is influenced by [Compound Finance](https://docs.compound.finance/v2/governance/) and [OpenZeppelin](https://docs.openzeppelin.com/contracts/4.x/api/governance).

A proposal contains textual descriptions and optionally on-chain transactions. The transactions are executed on behalf of the voting contract if the proposal receives enough votes.

While GCs hold their voting rights from staked KLAYs, a special secretariat account manages the overall on-chain governance process. Governance proposals can be submitted by the secretariat account, or any GC if the secretariat account is not set. The secretariat account can be updated by on-chain governance.

#### Voting steps overview

Under the default timing settings, a typical governance proposal is handled as follows.

1. When a governance proposal is submitted, it enters a 7-day preparation period where GCs can adjust their voting powers by staking or unstaking KLAYs. The list of voters (i.e., GCs) is finalized at the moment of proposal submission. However, voting powers are finalized at the end of the preparation period.
2. A 7-day voting period immediately follows after the preparation period.
3. If there are enough Yes votes, the transactions can be queued within 14 days after voting ends.
4. The transaction is delayed by 2 days to be executable. This delay gives the ecosystem enough time to adjust to the change and perform a final review of transactions.
5. After the delay, the transaction can be executed within 14 days.

The proposer of a proposal can cancel it at any time prior to execution. If a passed transaction is not queued or executed within the timeout, the proposal automatically expires.

![voting steps](/assets/kip-81/voting_steps.png)

#### Access control

Voting contract has two category of users, secretary and voters.

- The secretary is a single account stored in the `secretary` variable. This account is intended to be controlled by the Klaytn Foundation. It will serve the GC by assisting administrative works such as submitting and executing proposals.
- The voters are identified by their GC ID. The list of voters differs per proposal, depending on the list of GC members registered in AddressBook and their staking amounts at the time of proposal submission.

In its default setting, only the secretary can propose and execute proposals. Later the voters may be allowed to propose and execute proposals by changing the following access rule.

| Name               | Meaning                                                                               | Default Value |
| ---                | ---                                                                                   | ---           |
| `secretaryPropose` | True if the secretary can propose()                                                   | true          |
| `voterPropose`     | True if any eligible voter at the time of the submission can propose() a proposal     | false         |
| `secretaryExecute` | True if the secretary can queue() and execute()                                       | true          |
| `voterExecute`     | True if any eligible voter of a given proposal can queue() and execute() the proposal | false         |

#### Timing rule

The proposal timeline is dictated by several timing values below. The settings are expressed in
block numbers. The number of days in the below table is calculated based on 1 block/sec assumption.

To support flexible operation, Voting contract allows each proposal to have different `votingDelay` and `votingPeriod` within respective min and max values.
However, `queueTimeout`, `execDelay` and `execTimeout` are should not be configurable.

| Name              | Meaning                                                      | Default Value     | Configurability        |
|-------------------|--------------------------------------------------------------|-------------------|------------------------|
| `minVotingDelay`  | Minimum `votingDelay`                                        | 86400 (1 day)     | via `updateTimingRule` |
| `maxVotingDelay`  | Maximum `votingDelay`                                        | 2419200 (28 days) | via `updateTimingRule` |
| `minVotingPeriod` | Minimum `votingPeriod`                                       | 86400 (1 day)     | via `updateTimingRule` |
| `maxVotingPeriod` | Maximum `votingPeriod`                                       | 2419200 (28 days) | via `updateTimingRule` |
| `votingDelay`     | Delay from proposal submission to voting start               |                   | Specify per proposal   |
| `votingPeriod`    | Duration of the voting                                       |                   | Specify per proposal   |
| `queueTimeout`    | Grace period to queue() passed proposals                     | 1209600 (14 days) | Cannot modify          |
| `execDelay`       | A minimum delay before a queued transaction can be executed  | 172800 (2 days)   | Cannot modify          |
| `execTimeout`     | Grace period to execute() queued proposals since `execDelay` | 1209600 (14 days) | Cannot modify          |

#### Quorum

A proposal passes when it satisfies a combination of the following conditions.

- `CountQuorum` = At least 1/3 of all eligible voters cast votes
- `PowerQuorum` = At least 1/3 of all eligible voting powers cast votes
- `MajorityYes` = Yes votes are more than half of total cast votes (i.e. `Yes &gt; No + Abstain`)
- `PassCondition = (CountQuorum or PowerQuorum) and MajorityYes`

#### Contract interface

```solidity
interface Voting {
    enum ProposalState { Pending, Active, Canceled, Failed, Passed, Queued, Expired, Executed }
    enum VoteChoice { No, Yes, Abstain }
    struct Receipt {
        bool hasVoted;
        uint8 choice;
        uint256 votes;
    }

    // events

    /// @dev Emitted when a proposal is created
    /// @param signatures  Array of empty strings; for compatibility with OpenZeppelin
    event ProposalCreated(
        uint256 proposalId, address proposer,
        address[] targets, uint256[] values, string[] signatures, bytes[] calldatas,
        uint256 voteStart, uint256 voteEnd, string description);

    /// @dev Emitted when a proposal is canceled
    event ProposalCanceled(uint256 proposalId);

    /// @dev Emitted when a proposal is queued
    /// @param eta  The block number where transaction becomes executable.
    event ProposalQueued(uint256 proposalId, uint256 eta);

    /// @dev Emitted when a proposal is executed
    event ProposalExecuted(uint256 proposalId);

    /// @dev Emitted when a vote is cast
    /// @param reason  An empty string; for compatibility with OpenZeppelin
    event VoteCast(address indexed voter, uint256 proposalId,
                   uint8 choice, uint256 votes, string reason);

    /// @dev Emitted when the StakingTracker is changed
    event UpdateStakingTracker(address oldAddr, address newAddr);

    /// @dev Emitted when the secretary is changed
    event UpdateSecretary(address oldAddr, address newAddr);

    /// @dev Emitted when the AccessRule is changed
    event UpdateAccessRule(
        bool secretaryPropose, bool voterPropose,
        bool secretaryExecute, bool voterExecute);

    /// @dev Emitted when the TimingRule is changed
    event UpdateTimingRule(
        uint256 minVotingDelay,  uint256 maxVotingDelay,
        uint256 minVotingPeriod, uint256 maxVotingPeriod);

    // mutator functions

    /// @dev Create a Proposal
    /// @param description   Proposal text
    /// @param targets       List of transaction target addresses
    /// @param values        List of KLAY values to send along with transactions
    /// @param calldatas     List of transaction calldatas
    /// @param votingDelay   Delay from proposal submission to voting start in block numbers
    /// @param votingPeriod  Duration of the voting in block numbers
    function propose(
        string memory description,
        address[] memory targets,
        uint256[] memory values,
        bytes[] memory calldatas,
        uint256 votingDelay,
        uint256 votingPeriod
    ) external returns (uint256 proposalId);

    /// @dev Cancel a proposal
    /// The proposal must be in Pending state
    /// Only the proposer of the proposal can cancel the proposal.
    function cancel(uint256 proposalId) external;

    /// @dev Cast a vote to a proposal
    /// The proposal must be in Active state
    /// A node can only vote once for a proposal
    /// choice must be one of VoteChoice.
    function castVote(uint256 proposalId, uint8 choice) external;

    /// @dev Queue a passed proposal
    /// The proposal must be in Passed state
    /// Current block must be before `queueDeadline` of this proposal
    /// If secretary is null, any GC with at least 1 vote can queue.
    /// Otherwise only secretary can queue.
    function queue(uint256 proposalId) external;

    /// @dev Execute a queued proposal
    /// The proposal must be in Queued state
    /// Current block must be after `eta` and before `execDeadline` of this proposal
    /// If secretary is null, any GC with at least 1 vote can execute.
    /// Otherwise only secretary can execute.
    function execute(uint256 proposalId) external payable;

    /// @dev Update the StakingTracker address
    /// Should not be called if there is an active proposal
    function updateStakingTracker(address newAddr) external;

    /// @dev Update the secretary account
    function updateSecretary(address newAddr) external;

    /// @dev Update the access rule
    function updateAccessRule(
        bool secretaryPropose, bool voterPropose,
        bool secretaryExecute, bool voterExecute) external;

    /// @dev Update the timing rule
    function updateTimingRule(
        uint256 minVotingDelay,  uint256 maxVotingDelay,
        uint256 minVotingPeriod, uint256 maxVotingPeriod) external;

    // getter functions

    function stakingTracker() external view returns(address);
    function secretary() external view returns(address);
    function accessRule() external view returns(
        bool secretaryPropose, bool voterPropose,
        bool secretaryExecute, bool voterExecute);
    function timingRule() external view returns(
        uint256 minVotingDelay,  uint256 maxVotingDelay,
        uint256 minVotingPeriod, uint256 maxVotingPeriod);
    function queueTimeout() external view returns(uint256);
    function execDelay() external view returns(uint256);
    function execTimeout() external view returns(uint256);

    /// @dev The id of the last created proposal
    /// Retrurns 0 if there is no proposal.
    function lastProposalId() external view returns(uint256);

    /// @dev State of a proposal
    function state(uint256 proposalId) external view returns(ProposalState);

    /// @dev Check if a proposal is passed
    /// Note that its return value represents the current voting status,
    /// and is subject to change until the voting ends.
    function checkQuorum(uint256 proposalId) external view returns(bool);

    /// @dev Resolve the voter account into its gcId and voting powers
    /// Returns the currently assigned gcId. Returns the voting powers
    /// effective at the given proposal. Returns zero gcId and 0 votes
    /// if the voter account is not assigned to any eligible GC.
    ///
    /// @param proposalId  The proposal id
    /// @return gcId    The gcId assigned to this voter account
    /// @return votes   The amount of voting powers the voter account represents
    function getVotes(uint256 proposalId, address voter) external view returns(uint256, uint256);

    /// @dev General contents of a proposal
    function getProposalContent(uint256 proposalId) external view returns(
        uint256 id,
        address proposer,
        string memory description
    );

    /// @dev Transactions in a proposal
    /// @return signatures  Array of empty strings; for compatibility with OpenZeppelin
    function function getActions(uint256 proposalId) external view returns(
        address[] memory targets,
        uint256[] memory values,
        string[] memory signatures,
        bytes[] memory calldatas
    );

    /// @dev Timing and state related properties of a proposal
    function getProposalSchedule(uint256 proposalId) external view returns(
        uint256 voteStart,      // propose()d block + votingDelay
        uint256 voteEnd,        // voteStart + votingPeriod
        uint256 queueDeadline,  // voteEnd + queueTimeout
        uint256 eta,            // queue()d block + execDelay
        uint256 execDeadline,   // queue()d block + execDelay + execTimeout
        bool canceled,          // true if successfully cancel()ed
        bool queued,            // true if successfully queue()d
        bool executed           // true if successfully execute()d
    );

    /// @dev Vote counting related properties of a proposal
    function getProposalTally(uint256 proposalId) external view returns(
        uint256 totalYes,
        uint256 totalNo,
        uint256 totalAbstain,
        uint256 quorumCount,      // required number of voters
        uint256 quorumPower,      // required number of voting powers
        uint256[] memory voters,  // list of gcIds who voted
    )

    /// @dev Individual vote receipt
    function getReceipt(uint256 proposalId, uint256 gcId)
        external view returns(Receipt memory);
}
```

#### Fetching votes from StakingTracker

Calculating votes requires information from StakingTracker contract. The Voting contract shall utilize the getters to find the voting powers of each voter as well as quorum conditions. Below is an example implementation of `castVote()` and `checkQuorum()` functions.

```solidity
abstract contract Voting {
    struct Proposal {
        uint256 trackerId;  // StakingTracker trackerId created in propose()
        uint256 totalYes;
        uint256 totalNo;
        uint256 totalAbstain;
        uint256[] voters;   // List of gcIds
        mapping(uint256 =&gt; Receipt) receipts;
    }
    mapping(uint256 =&gt; Proposal) proposals;
    IStakingTracker stakingTracker;

    function castVote(uint256 proposalId, uint8 choice) external {
        Proposal storage p = proposals[proposalId];

        (uint256 gcId, uint256 votes) = getVotes(proposalId, msg.sender);
        require(gcId != 0, &quot;Not a registered voter&quot;);
        require(votes &gt; 0, &quot;Not eligible to vote&quot;);

        require(choice == uint8(VoteChoice.Yes) ||
                choice == uint8(VoteChoice.No) ||
                choice == uint8(VoteChoice.Abstain), &quot;Not a valid choice&quot;);
        require(!p.receipts[gcId].hasVoted, &quot;Already voted&quot;);

        p.receipts[gcId].hasVoted = true;
        p.receipts[gcId].choice = choice;
        p.receipts[gcId].votes = votes;
        if (choice == VoteChoice.Yes) { p.totalYes += votes; }
        if (choice == VoteChoice.No) { p.totalNo += votes; }
        if (choice == VoteChoice.Abstain) { p.totalAbstain += votes; }
        p.voters.push(gcId);
    }

    function getVotes(uint256 proposalId, address voter) public view returns(
        uint256 gcId, uint256 votes) {
        Proposal storage p = proposals[proposalId];

        gcId = IStakingTracker(p.stakingTracker).voterToGCId(voter);
        ( , votes) = IStakingTracker(p.stakingTracker).getTrackedGC(p.trackerId, gcId);
    }

    function checkQuorum(uint256 proposalId) external view returns(bool) {
        Proposal storage p = proposals[proposalId];

        (uint256 quorumCount, uint256 quorumPower) = getQuorum(proposalId);
        uint256 totalVotes = p.totalYes + p.totalNo + p.totalAbstain;
        uint256 quorumYes = p.totalNo + p.totalAbstain + 1; // more than half of all votes

        bool countPass = (p.voters.length &gt;= quorumCount);
        bool powerPass = (totalVotes      &gt;= quorumPower);
        bool approval  = (p.totalYes      &gt;= quorumYes);

        return ((countPass || powerPass) &amp;&amp; approval);
    }

    /// @dev Calculate count and power quorums for a proposal
    function getQuorum(uint256 proposalId) private view returns(
        uint256 quorumCount, uint256 quorumPower) {

        Proposal storage p = proposals[proposalId];
        if (p.quorumCount != 0) { // return cached numbers
            return (p.quorumCount, p.quorumPower);
        }

        ( , , , uint256 totalVotes, uint256 numEligible) =
            IStakingTracker(p.stakingTracker).getTrackerSummary(p.trackerId);

        quorumCount = (numEligible + 2) / 3; // more than or equal to 1/3 of all GC members
        quorumPower = (totalVotes + 2) / 3; // more than or equal to 1/3 of all voting powers
        return (quorumCount, quorumPower);
    }
}
```

#### Determining state

The state of a proposal can be determined from stored variables, vote counts, and current block number. Below is an example implementation of `state()` function.

```solidity
abstract contract Voting {
    struct Proposal {
        uint256 voteStart;      // propose()d block + votingDelay
        uint256 voteEnd;        // voteStart + votingPeriod
        uint256 queueDeadline;  // voteEnd + queueTimeout
        uint256 eta;            // queue()d block + execDelay
        uint256 execDeadline;   // queue()d block + execDelay + execTimeout
        bool canceled;          // true if successfully cancel()ed
        bool queued;            // true if successfully queue()d
        bool executed;          // true if successfully execute()d
    }
    mapping(uint256 =&gt; Proposal) proposals;

    function state(uint256 proposalId) external view returns (ProposalState) {
        Proposal storage p = proposals[proposalId];

        if (p.executed) {
            return ProposalState.Executed;
        } else if (p.canceled) {
            return ProposalState.Canceled;
        } else if (block.number &lt; p.voteStart) {
            return ProposalState.Pending;
        } else if (block.number &lt;= p.voteEnd) {
            return ProposalState.Active;
        } else if (!checkQuorum(proposalId)) {
            return ProposalState.Failed;
        }

        if (!p.queued) {
            // A proposal without an action does not expire
            if (block.number &lt;= p.queueDeadline || p.targets.length == 0) {
                return ProposalState.Passed;
            } else {
                return ProposalState.Expired;
            }
        } else {
            if (block.number &lt;= p.execDeadline) {
                return ProposalState.Queued;
            } else {
                return ProposalState.Expired;
            }
        }
    }
}
```

#### Executing transactions

The transactions in a passed proposal are executed in the order they appear in the `propose()` function arguments.  Each transaction is sent to `targets[i]`, carrying `values[i]` of KLAYs, with `calldatas[i]` as input data. Below is an example of `execute()` function.

```solidity
abstract contract Voting {
    struct Proposal {
        address[] targets;
        uint256[] values;
        bytes[] calldatas;

    }
    mapping(uint256 =&gt; Proposal) proposals;

    function execute(uint256 proposalId) external payable {
        Proposal storage p = proposals[proposalId];

        for (uint256 i = 0; i &lt; p.targets.length; i++) {
            (bool success, bytes memory result) =
                    p.targets[i].call{value: p.values[i]}(p.calldatas[i]);
            require(success, &quot;Transaction failed&quot;);
        }
    }
}
```

#### Example implementation

See [here](https://github.com/klaytn/governance-contracts-audit/blob/main/contracts/Voting.sol) for an example implementation.

## Rationale

#### CnStakingV2 and StakingTracker instead of standard ERC20Votes

It is common for DAOs to manage their governance tokens in an [ERC20Votes](https://docs.openzeppelin.com/contracts/4.x/api/token/erc20#ERC20Votes) contract. However new contracts CnStakingV2 and StakingTracker were used to manage staked KLAYs. Using the new contracts provides better compatibility with ecosystem Dapps like public staking services. It also does not break the existing block validator selection algorithm that depends on AddressBook.

#### StakingTracker refreshStake and refreshVoting has no access control

It is a remnant of a previous design. Those functions were originally intended to allow optionally or gradually migrate from CnStakingContract to CnStakingV2 contracts. Now that old CnStakingContract are excluded from the voting power calculation, such a feature is unnecessary but the refreshStake and refreshVoter functions retain its shape.

#### Separate voter account

In a security standpoint, it is safer to have separate accounts for different roles. Existing CnStakingV2 admins take a financial role - withdrawing staked KLAYs - and the voter account take a voting role only.

#### Proposals have expired state

Proposal transactions expire if they are not queued or executed within the deadline. This guarantees the timeliness of the transactions. If a proposal was left unexecuted for a long time, the proposal might be irrelevant anymore and require a fresh proposal.

## Expected Effect

The proposed GC Voting method is expected to produce the following changes:
- All members in the Klaytn ecosystem grow together with credibility
- Providing obligation and authority to GC motivates active participation in governance voting activities and the KLAY staking
- Anyone can easily view the proposals and voting status through the governance portal. It encourages holders to participate and give responsibility and authority to GCs.
- The Klaytn network can take a step closer to transparency and decentralized networks.

## Backward Compatibility

- GCs should deploy new CnStakingV2 contracts and move their staked KLAYs. Existing CnStakingContract (i.e. V1) balances are excluded from voting power calculation.
- However, block validator selection is not affected by the change, so GCs can still participate in consensus before migrating to CnStakingV2.

## Reference
n/a

## Copyright
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
</description>
        <pubDate>Mon, 19 Sep 2022 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-81</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-81</guid>
      </item>
    
      <item>
        <title>A new GC reward structure due to abolition of the Gini coefficient</title>
        <category>Core</category>
        
        <description>## Simple Summary
A renewal of the Governance Council(GC) reward structure due to abolition of the Gini coefficient.

## Abstract
In addition to traditional enterprises, Klaytn is expanding the Governance Council (GC) by bringing DAOs in response to the growth of nontraditional entities. Such a change in the GC ecosystem will restructure the entire Klaytn governance structure. Abolishing the current block proposal selection method based on the Gini coefficient and staking quantity, the new reward program alleviates the problem of providing insufficient compensation for the network contribution and block verification due to lack of staking. The instability of certain nodes with high staking amounts can result in poor network stability as a whole. The renewed structure provides 20% of GC rewards to block proposers and the rest to the stakers. This form is to reward the participants for providing network stability with node operation and financial contribution.

## Motivation
Klaytn has been compensating GC with block proposal rewards based on the quantity of staking and the Gini coefficient. This included rewards incurred by minting and gas fee generated by transactions. As mentioned in the [Klaytn 2.0 Lightpaper](https://klaytn.foundation/wp-content/themes/klaytn/download/lightpaper.pdf), Klaytn is abolishing the Gini coefficient since this measure demotivates members from staking. Discontinuation of the Gini coefficient increases the risk to network stability due to the expanding reliance on certain nodes. This change ultimately calls for the revision of block proposer selection and reward structure. In an effort to enhance individual voting power and promote ultimate decentralization, we aim to accomplish the following changes:
- Ameliorate the risk that block creation depends on larger staker nodes
- Associate GC reward with the growth of the Klaytn ecosystem

## Specification

Block generation is rewarded for the node operations that are serving as public goods in the Klaytn network. CNs and PNs are in charge of block proposal and propagation while ENs are in charge of blockchain data inquiry based on an individual&apos;s needs. The infrastructure cost remains similar, enabling some fixed benefits. Therefore, fixed compensation will be equivalent to the sum of the transaction fee and an additional block compensation, which can be both inflationary and deflationary.

The block proposal method will be based on equal selection regardless of the staking amount. The current model provides a block generation reward based on staking amount and the Gini-coefficient. Until now, the Gini coefficient was utilized to alleviate the chance of an entity with more staking amount being selected as a block proposer. As this model provided a fairly equal chance to every participant, each node was promised a certain amount of rewards.

In the new method, the reward for node operation is required since the staking amount is not considered in the block proposer selection. Therefore, a certain percentage of the total reward amount will be placed as a node operation reward.

Node operators will also be compensated with the staking for enhancing network stability from an economic standpoint. The opportunity cost incurred from staking is rewarded based on inflation. In this system, inflation provides a tax-like common effect. Public users can also participate and receive rewards through the GC-run public staking program.

As of September 21st, 2022, of the 300 million KLAY that is minted annually, only 100 million KLAY goes to GC. The renewal separates GC reward into two components: block proposer reward and staking reward. The block proposer reward is compensating for node operation by providing 20% of the GC reward and 100% of gas fee resources from transactions. The staking reward is from 80% of the GC reward and is distributed based on GC staking amounts.

The relative ratio of 20% and 80% is a tentative value to be used in the Cypress mainnet. However, this ratio is defined as a governance paramter, so GC members can change the ratio through the governance process.

![gc reward allocation](/assets/kip-82/gc_reward_allocation.png)

After the Magma hard fork, Klaytn has been burning the first half of the gas fee based on [the KIP-71 proposal](https://kips.klaytn.foundation/KIPs/kip-71) while the second half is distributed. The second half is distributed into GC reward, KGF and KIR.

In addition to changing the reward structure, this renewal will include burning the gas fee up to a threshold which is the size of block proposer reward. If the gas fee exceeds the threshold, remaining amount is rewarded to the block proposer. The KGF and KIR portion will be excluded from the gas fee.

### Klaytn node update

The Klaytn node will be updated according to the new reward policy. The CN reward is further split into proposer reward and staking reward. The staking reward is distributed among CNs proportionally to their staking amounts minus the minimum staking amount.

A new governance parameter is introduced to tune the new reward distribution algorithm. The initial value below will be used in the Cypress mainnet when KIP-82 is first applied. The parameter can be updated using the existing governance mechanism.

| Name                | Type   | Meaning                                                 | Initial Value |
|---------------------|--------|---------------------------------------------------------|---------------|
| `reward.kip82ratio` | string | The relative ratio between proposer and staking rewards | &quot;20/80&quot;       |

During integer arithmetics, minuscule remaining amounts may be emitted as by-products. The proposer gets the remainder from the staking share disribution, and KGF gets the the remainder from the distribution among proposer, stakers, KIR, KGF.

Below pseudocode illustrates the new reward distribution algorithm. Note: `//` is round down integer division.

```python
from collections import defaultdict

MAGMA_BLOCK_NUMBER = 99841497
KORE_BLOCK_NUMBER = 120000000 # TBD

# Block header. Only relevant fields are shown here.
class Header:
    Number: int = 0   # Block number
    GasUsed: int = 0  # Total gas spent in the block
    BaseFee: int = 0  # Base fee per gas at the block
    RewardBase: string = &quot;&quot; # Reward recipient address of the block proposer

# Network configurations related to reward distribution.
# Corresponds to Klaytn&apos;s reward.rewardConfig struct.
class RewardConfig:
    # &quot;istanbul.policy&quot; parameter
    RoundRobin = 0
    Sticky = 1
    WeightedRandom = 2
    ProposerPolicy: int = WeightedRandom

    UnitPrice: int = 25000000000              # &quot;governance.unitprice&quot; parameter (in peb)
    MintingAmount: int = 9600000000000000000  # &quot;reward.mintingamount&quot; parameter (in peb)
    MinimumStake: int = 5000000               # &quot;reward.minimumstake&quot; parameter (in KLAY)
    DeferredTxFee: bool = True                # &quot;reward.deferredtxfee&quot; parameter

    # &quot;reward.ratio&quot; parameter (e.g. &quot;50/40/10&quot;)
    CnRatio: int = 50
    KgfRatio: int = 40
    KirRatio: int = 10
    TotalRatio: int = CnRatio + KgfRatio + KirRatio

    # &quot;reward.kip82ratio&quot; parameter (e.g. &quot;20/80&quot;) (new)
    CnProposerRatio: int = 20
    CnStakingRatio: int = 80
    CnTotalRatio: int = CnProposerRatio + CnStakingRatio

# CN staking status and KGF/KIR addresses.
# Corresponds to Klaytn&apos;s reward.StakingInfo struct.
# Can be obtained from reward.GetStakingInfo(blockNum) function.
class StakingInfo:
    KIRAddr: string = &quot;&quot;
    KGFAddr: string = &quot;&quot;
    Nodes: List[ConsolidatedNode] = []

# Staking information merged under the same CN.
# Sometimes a node would register multiple NodeAddrs
# in which each entry has a different StakingAddr and the same RewardAddr.
# We treat those entries with common RewardAddr as one node.
# Corresponds to Klaytn&apos;s reward.ConsolidatedNode struct.
class ConsolidatedNode:
    NodeAddrs: List[string] = []
    StakingAddrs: List[string] = []
    RewardAddr: string = &quot;&quot; # The common reward address of the CN
    StakingAmount: int = 0  # Total staking amount across StakingAddrs (in KLAY)

# Reward distribution details.
class RewardSpec:
    Minted: int = 0   # The amount newly minted
    TotalFee: int = 0 # Total tx fee spent
    BurntFee: int = 0 # The amount burnt
    Proposer: int = 0 # The amount allocated to the block proposer
    Stakers: int = 0  # Total amount allocated to stakers
    Kgf: int = 0      # The amount allocated to KGF
    Kir: int = 0      # The amount allocated to KIR
    Rewards: Dict[string, int] = {}  # Mapping from reward recipient to amounts

# Perform post-transaction state modifications such as block rewards.
# Corresponds to Klaytn&apos;s consensus/istanbul/backend.Finalize()
#
# - state is the StateDB to apply the rewards.
# - header is a Header instance.
# - config is a RewardConfig instance.
def finalize_block(state, header, config):
    if config.ProposerPolicy in [RoundRobin, Sticky]:
        spec = calc_deferred_reward_simple(header, config)
    else:
        spec = calc_deferred_reward(header, config)

    for addr, amount in spec.Rewards:
        state.AddBalance(addr, amount)
    header.Root = state.Root()

def calc_deferred_reward_simple(header, config):
    minted = config.MintingAmount

    total_fee = get_total_fee(header)
    if header.Number &gt;= KORE_BLOCK_NUMBER and not config.DeferredTxFee:
        total_fee = 0

    reward_fee = total_fee
    burnt_fee = 0

    if header.Number &gt;= MAGMA_BLOCK_NUMBER:
        burn_amount = get_burn_amount_magma(reward_fee)
        reward_fee -= burn_amount
        burnt_fee += burn_amount

    proposer = minted + reward_fee

    spec = RewardSpec()
    spec.Minted = minted
    spec.TotalFee = total_fee
    spec.BurntFee = burnt_fee
    spec.Proposer = proposer
    spec.Rewards = {header.RewardBase: proposer}
    return spec

# Calculates the deferred rewards, which are determined at the end of block processing.
# Used in reward distribution.
# Returns a RewardSpec.
def calc_deferred_reward(header, config):
    staking_info = GetStakingInfo(header.Number)
    minted = config.MintingAmount
    total_fee, reward_fee, burnt_fee = calc_deferred_fee(header, config)

    proposer, stakers, kgf, kir, split_rem = calc_split(header, config, minted, reward_fee)
    shares, share_rem = calc_shares(config, staking_info, stakers)

    # Remainder from (CN, KGF, KIR) split goes to KGF
    kgf += split_rem
    # Remainder from staker shares goes to Proposer
    proposer += share_rem

    # If KGF or KIR address is not set, proposer gets the portion.
    if staking_info.KGFAddr is None:
        proposer += kgf
        kgf = 0
    if staking_info.KIRAddr is None:
        proposer += kir
        kir = 0

    spec = RewardSpec()
    spec.Minted = minted
    spec.TotalFee = total_fee
    spec.BurntFee = burnt_fee
    spec.Proposer = proposer
    spec.Stakers = stakers
    spec.Kgf = kgf
    spec.Kir = kir

    spec.Rewards = defaultdict(int)
    spec.Rewards[header.RewardBase] += proposer
    if staking_info.KGFAddr is not None:
        spec.Rewards[staking_info.KGFAddr] += kgf
    if staking_info.KIRAddr is not None:
        spec.Rewards[staking_info.KIRAddr] += kir
    for reward_addr, reward_amount in shares:
        spec.Rewards[reward_addr] += reward_amount

    return spec

# Returns (total_fee, reward_fee, burnt_fee)
def calc_deferred_fee(header, config):
    # If not DeferredTxFee, fees are already added to the proposer during TX execution.
    # Therefore, there are no fees to distribute here at the end of block processing.
    # However, the fees must be compensated to calculate actual rewards paid.
    if not config.DeferredTxFee:
        return (0, 0, 0)

    # Start with the total block gas fee
    total_fee = get_total_fee(header)
    reward_fee = total_fee
    burnt_fee = 0

    # Since Magma, burn half of gas
    if header.number &gt;= MAGMA_BLOCK_NUMBER:
        burn_amount = get_burn_amount_magma(reward_fee)
        reward_fee -= burn_amount
        burnt_fee += burn_amount

    # If KIP-82 is enabled, burn fees up to proposer&apos;s minted reward
    if header.number &gt;= KORE_BLOCK_NUMBER:
        burn_amount = get_burn_amount_kip82(config, reward_fee)
        reward_fee -= burn_amount
        burnt_fee += burn_amount

    return (total_fee, reward_fee, burnt_fee)

def get_total_fee(header):
    if header.number &gt;= MAGMA_BLOCK_NUMBER:
        return header.GasUsed * header.BaseFee
    else:
        return header.GasUsed * config.UnitPrice

def get_burn_amount_magma(fee):
    return fee / 2

def get_burn_amount_kip82(config, fee):
    cn, _, _ = split_by_ratio(config, config.MintingAmount)
    proposer, _ = split_by_kip82_ratio(config, cn)
    if fee &gt;= proposer:
        return proposer
    else:
        return fee

# Returns (proposer, stakers, kgf, kir, remaining) amounts
# The sum of output must be equal to (minted + reward_fee).
def calc_split(header, config, minted, reward_fee):
    total_resource = minted + reward_fee

    if header.number &gt;= KORE_BLOCK_NUMBER:
        cn, kgf, kir = split_by_ratio(config, minted)
        proposer, stakers = split_by_kip82_ratio(config, cn)
        proposer += reward_fee

        remaining = total_resource - kgf - kir - proposer - stakers
        return (proposer, stakers, kgf, kir, remaining)
    else:
        cn, kgf, kir = split_by_ratio(config, minted + reward_fee)

        remaining = total_resource - kgf - kir - cn
        return (cn, 0, kgf, kir, remaining)

# Split by `ratio`. Ignore remaining amounts.
def split_by_ratio(config, source):
    cn = source * config.CnRatio // config.TotalRatio
    kgf = source * config.KgfRatio // config.TotalRatio
    kir = source * config.KirRatio // config.TotalRatio
    return (cn, kgf, kir)

# Split by `kip82ratio`. Ignore remaining amounts.
def split_by_kip82_ratio(config, source):
    proposer = source * config.CnProposerRatio // config.CnTotalRatio
    stakers = source * config.CnStakingRatio // config.CnTotalRatio
    return (proposer, stakers)

# Distribute stake_reward among staked CNs
# Returns a mapping from each reward address to their reward shares,
# and the remaining amount.
def calc_shares(config, staking_info, stake_reward):
    min_stake = config.MinimumStake
    total_stakes = 0
    for node in staking_info.Nodes:
        if node.StakingAmount &gt; min_stake:
            total_stakes += (node.StakingAmount - min_stake)

    shares = {}
    remaining = stake_reward
    for node in staking_info.Nodes:
        if node.StakingAmount &gt; min_stake:
            effective_stake = node.StakingAmount - min_stake
            reward_amount = stake_reward * effective_stake // total_stakes
            remaining -= reward_amount
            shares[node.RewardAddr] = reward_amount

    return (shares, remaining)
```

The updated algorithm increases the balance of every GC member&apos;s reward account at every block. The performance impact should be reasonable since the number of GC members is significantly smaller than Klaytn&apos;s transaction processing capability.

### Reward JSON-RPC API

A new JSON-RPC method should be added to provide historic reward distribution details.

- Name: `klay_getRewards`
- Description: Returns allocation details of reward distribution at the specified block. If the parameter is not set, returns a breakdown of reward distribution at the latest block.
- Parameters
  1. `QUANTITY | TAG` - (optional) integer or hexadecimal block number, or the string &quot;earlist&quot; or &quot;latest&quot;.
- Returns
  - `DATA`
    - `minted`: The amount minted
    - `totalFee`: Total tx fee spent
    - `burntFee`: The amount burnt
    - `proposer`: The amount for the block proposer
    - `stakers`: Total amount for stakers
    - `kgf`: The amount for KGF
    - `kir`: The amount for KIR
    - `rewards`: A mapping from reward recipient addresses to reward amounts
- Example
  ```
  // Request
  curl -X POST -H &quot;Content-Type: application/json&quot; --data &apos;{&quot;jsonrpc&quot;:&quot;2.0&quot;, &quot;method&quot;:&quot;klay_getRewards&quot;, &quot;params&quot;:[&quot;0x64fe7e0&quot;],&quot;id&quot;:1}&apos; https://api.baobab.klaytn.net:8651
  // Response
  {
    &quot;jsonrpc&quot;:&quot;2.0&quot;,
    &quot;id&quot;:1,
    &quot;result&quot;:{
      &quot;minted&quot;: &quot;6400000000000000000&quot;,
      &quot;totalFee&quot;: &quot;1075975000000000&quot;,
      &quot;burntFee&quot;: &quot;537987500000000&quot;,
      &quot;proposer&quot;: &quot;3200268993750000000&quot;,
      &quot;stakers&quot;: &quot;0&quot;,
      &quot;kgf&quot;: &quot;2560215195000000000&quot;,
      &quot;kir&quot;: &quot;640053798750000000&quot;
      &quot;rewards&quot;: {
        &quot;0xa86fd667c6a340c53cc5d796ba84dbe1f29cb2f7&quot;: &quot;3200268993750000000&quot;,
        &quot;0x2bcf9d3e4a846015e7e3152a614c684de16f37c6&quot;: &quot;2560215195000000000&quot;,
        &quot;0x716f89d9bc333286c79db4ebb05516897c8d208a&quot;: &quot;640053798750000000&quot;
      }
    }
  }
  ```

## Expected Effect
The proposed GC reward mechanism is expected to produce the following changes:
- GC members increase individual staking amount.
- KLAY holders receive profit based on the staking amount of KLAY.
- Total Value Locked (TVL) of Klaytn increases.
- The total circulation reduces.

## Backward Compatibility
Klaytn nodes must be upgraded before `KORE_BLOCK_NUMBER` to correctly produce or verify blocks.

## Reference
n/a

## Copyright
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
</description>
        <pubDate>Wed, 21 Sep 2022 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-82</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-82</guid>
      </item>
    
      <item>
        <title>NFT Avatar in Multi-Metaverse</title>
        <category>Tokenization</category>
        
          <comments>https://forum.klaytn.foundation/t/en-kip-87-nft-avatar-standard-proposal/6161</comments>
        
        <description>## Simple Summary
Creating an NFT avatar standard to provide an interchangeable visualization of NFT across multiple metaverses.

## Abstract
Although fungible tokens (FTs) are easily exchangeable across different platforms through DEX, non-fungible tokens (NFTs) do not easily migrate from one metaverse to another. The exchange of NFTs across platforms often does not promise the equivalent asset value, including quality and style. We aim to define an NFT as a single character with different visualization for multi-metaverse. An NFT character with the standard definition and predefined motion images can create an adequate visualization when entering metaverses as an NFT avatar. The NFT avatar standard will increase the utility of NFT, invigorate the metaverse industry and strengthen interoperability.

## Motivation
NFT has different utilities. Some are used as an asset in the gaming industry while some are used as a parcel of virtual land in the metaverse. The usage of NFTs has often been limited to PFP (profile picture) until today. Yet, an NFT character should be interchanged into several forms of avatars corresponding to the metaverse platform it enters. This standard distinguishes the usage between NFT characters as absolute graphical assets and NFT avatars as motion-added chameleon-ed NFT characters.

With the NFT avatar standard, NFT characters will transfer into avatars enabling use cases within multiple metaverses. The character purchased in the NFT marketplace can enter as a 2D avatar in Zep.us while it enters Another.world as a 3D avatar. In addition, user-created 3D avatars, like VR Chat, can also compatibly enter multi-metaverse as NFT avatars.

The NFT avatar standard will generate a positive value creation cycle between creators, the NFT marketplace, and the metaverse. Each individual entity will have a common standard to share, maximizing the utility and interoperability of NFTs. With the standard, the value of an item minted by creators will be increased with heightened practicality and utilization. This will boost the activity within NFT marketplaces while the usage of NFT within metaverses also increases. The versatile utility of NFT characters will increase the public desire to purchase and incentivize creators to generate, ultimately vitalizing the creator economy. Likewise, such dynamics will unleash NFT characters in the multi-metaverse.

## Specification
The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY” and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.

As an a metadata extension of [ERC-721](https://eips.ethereum.org/EIPS/eip-721) and [EIP-4955](https://eips.ethereum.org/EIPS/eip-4955), the NFT avatar standard supplements the &quot;zep&quot; section under &quot;namespaces&quot;. The data required for the avatar varies by metaverse platform. In the instance of Zep, type of asset and properties of image are defined to maximize the scalability of individual NFT.

## Schema
In addition to [EIP-4955](https://eips.ethereum.org/EIPS/eip-4955), we define more in-depth specification to represent an avatar in various metaverses.

Here is the schema for this. This schema only defines an avatar schema for [Zep](https://zep.us), but it can be used for various metaverses using similar avatar representation.
```json
{
  &quot;title&quot;: &quot;Asset Metadata&quot;,
  &quot;type&quot;: &quot;object&quot;,
  &quot;properties&quot;: {
    &quot;name&quot;: {
      &quot;type&quot;: &quot;string&quot;,
      &quot;description&quot;: &quot;Identifies the asset to which this NFT represents&quot;
    },
    &quot;description&quot;: {
      &quot;type&quot;: &quot;string&quot;,
      &quot;description&quot;: &quot;Describes the asset to which this NFT represents&quot;
    },
    &quot;image&quot;: {
      &quot;type&quot;: &quot;string&quot;,
      &quot;description&quot;: &quot;A URI pointing to a resource with mime type image/* representing the asset to which this NFT represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive.&quot;
    },
    &quot;namespaces&quot;: {
      &quot;type&quot;: &quot;object&quot;,
      &quot;description&quot;: &quot;Projects that needs specific properties to use the NFT&quot;,
      &quot;properties&quot;: {
        &quot;zep&quot;: {
          &quot;$ref&quot;: &quot;#/$defs/zep&quot;
        }
      },
      &quot;additionalProperties&quot;: {
        &quot;type&quot;: &quot;object&quot;
      }
    }
  },
  &quot;$defs&quot;: {
    &quot;zep&quot;: {
      &quot;title&quot;: &quot;Avatar Schema for Zep&quot;,
      &quot;type&quot;: &quot;object&quot;,
      &quot;properties&quot;: {
        &quot;type&quot;: {
          &quot;type&quot;: &quot;string&quot;,
          &quot;enum&quot;: [ &quot;avatar&quot; ],
          &quot;description&quot;: &quot;Type of asset that an item will perform within the metaverse. Currently, only &apos;avatar&apos; is supported.&quot;
        },
        &quot;properties&quot;: {
          &quot;type&quot;: &quot;object&quot;,
          &quot;properties&quot;: {
            &quot;image&quot;: {
              &quot;type&quot;: &quot;string&quot;,
              &quot;format&quot;: &quot;uri&quot;,
              &quot;description&quot;: &quot;A URL to the image of the avatar sprite sheet. (only PNG images are supported)&quot;
            },
            &quot;frame_width&quot;: {
              &quot;type&quot;: &quot;integer&quot;,
              &quot;minimum&quot;: 1,
              &quot;description&quot;: &quot;Width of a frame. The original image width must be divided by the frame_width. It must range between a minimum of 1 and a maximum of the image width. The values of frame_width and frame_height can be different.&quot;
            },
            &quot;frame_height&quot;: {
              &quot;type&quot;: &quot;integer&quot;,
              &quot;minimum&quot;: 1,
              &quot;maximum&quot;: 256,
              &quot;description&quot;: &quot;Height of a frame. The original image height must be divided by the frame_height. It must range between a minimum of 1 and a maximum of the image height. This must be a multiple of an original image size, ranging between a minimum of 1 and a maximum of 256. The values of frame_width and frame_height can be different.&quot;
            },
            &quot;animations&quot;: {
              &quot;type&quot;: &quot;object&quot;,
              &quot;properties&quot;: {
                &quot;down&quot;: {
                  &quot;$ref&quot;: &quot;#/$defs/zep/$defs/animationDef&quot;,
                  &quot;description&quot;: &quot;The downward movement of the avatar.&quot;
                },
                &quot;left&quot;: {
                  &quot;$ref&quot;: &quot;#/$defs/zep/$defs/animationDef&quot;,
                  &quot;description&quot;: &quot;The movement of the avatar to the left.&quot;
                },
                &quot;right&quot;: {
                  &quot;$ref&quot;: &quot;#/$defs/zep/$defs/animationDef&quot;,
                  &quot;description&quot;: &quot;The movement of the avatar to the right.&quot;
                },
                &quot;up&quot;: {
                  &quot;$ref&quot;: &quot;#/$defs/zep/$defs/animationDef&quot;,
                  &quot;description&quot;: &quot;The upward movement of the avatar.&quot;
                },
                &quot;dance&quot;: {
                  &quot;$ref&quot;: &quot;#/$defs/zep/$defs/animationDef&quot;,
                  &quot;description&quot;: &quot;The dance movement of the avatar.&quot;
                },
                &quot;down_jump&quot;: {
                  &quot;$ref&quot;: &quot;#/$defs/zep/$defs/animationDef&quot;,
                  &quot;description&quot;: &quot;The downward jump movement of the avatar.&quot;
                },
                &quot;left_jump&quot;: {
                  &quot;$ref&quot;: &quot;#/$defs/zep/$defs/animationDef&quot;,
                  &quot;description&quot;: &quot;The jumping movement of the avatar to the left.&quot;
                },
                &quot;right_jump&quot;: {
                  &quot;$ref&quot;: &quot;#/$defs/zep/$defs/animationDef&quot;,
                  &quot;description&quot;: &quot;The jumping movement of the avatar to the right.&quot;
                },
                &quot;up_jump&quot;: {
                  &quot;$ref&quot;: &quot;#/$defs/zep/$defs/animationDef&quot;,
                  &quot;description&quot;: &quot;The upward jump movement of the avatar.&quot;
                },
                &quot;down_attack&quot;: {
                  &quot;$ref&quot;: &quot;#/$defs/zep/$defs/animationDef&quot;,
                  &quot;description&quot;: &quot;The downward attack movement of the avatar.&quot;
                },
                &quot;left_attack&quot;: {
                  &quot;$ref&quot;: &quot;#/$defs/zep/$defs/animationDef&quot;,
                  &quot;description&quot;: &quot;The attacking movement of the avatar to the left.&quot;
                },
                &quot;right_attack&quot;: {
                  &quot;$ref&quot;: &quot;#/$defs/zep/$defs/animationDef&quot;,
                  &quot;description&quot;: &quot;The attacking movement of the avatar to the right.&quot;
                },
                &quot;up_attack&quot;: {
                  &quot;$ref&quot;: &quot;#/$defs/zep/$defs/animationDef&quot;,
                  &quot;description&quot;: &quot;The upward attack movement of the avatar.&quot;
                },
                &quot;down_idle&quot;: {
                  &quot;$ref&quot;: &quot;#/$defs/zep/$defs/animationDef&quot;,
                  &quot;description&quot;: &quot;The downward idle movement of the avatar.&quot;
                },
                &quot;left_idle&quot;: {
                  &quot;$ref&quot;: &quot;#/$defs/zep/$defs/animationDef&quot;,
                  &quot;description&quot;: &quot;The idleing movement of the avatar to the left.&quot;
                },
                &quot;right_idle&quot;: {
                  &quot;$ref&quot;: &quot;#/$defs/zep/$defs/animationDef&quot;,
                  &quot;description&quot;: &quot;The idleing movement of the avatar to the right.&quot;
                },
                &quot;up_idle&quot;: {
                  &quot;$ref&quot;: &quot;#/$defs/zep/$defs/animationDef&quot;,
                  &quot;description&quot;: &quot;The upward idle movement of the avatar.&quot;
                }
              },
              &quot;required&quot;: [
                &quot;down&quot;,
                &quot;left&quot;,
                &quot;right&quot;,
                &quot;up&quot;,
                &quot;dance&quot;,
                &quot;down_jump&quot;,
                &quot;left_jump&quot;,
                &quot;right_jump&quot;,
                &quot;up_jump&quot;,
                &quot;down_attack&quot;,
                &quot;left_attack&quot;,
                &quot;right_attack&quot;,
                &quot;up_attack&quot;,
                &quot;down_idle&quot;,
                &quot;left_idle&quot;,
                &quot;right_idle&quot;,
                &quot;up_idle&quot;
              ]
            }
          },
          &quot;required&quot;: [
            &quot;image&quot;,
            &quot;frame_width&quot;,
            &quot;frame_height&quot;,
            &quot;animations&quot;
          ]
        }
      },
      &quot;required&quot;: [ &quot;type&quot;, &quot;properties&quot; ],
      &quot;$defs&quot;: {
        &quot;animationDef&quot;: {
          &quot;type&quot;: &quot;object&quot;,
          &quot;properties&quot;: {
            &quot;frames&quot;: {
              &quot;type&quot;: &quot;array&quot;,
              &quot;items&quot;: &quot;integer&quot;,
              &quot;maxItems&quot;: 256,
              &quot;description&quot;: &quot;Frame index of the sprite sheet (zero-based). Each frame has (frame_width X frame_height) image fragment in the sprite sheet image. The frame index starts from 0. The maximum array length is 256.&quot;
            },
            &quot;frame_rate&quot;: {
              &quot;type&quot;: &quot;integer&quot;,
              &quot;minimum&quot;: 1,
              &quot;maximum&quot;: 64,
              &quot;description&quot;: &quot;frame_rate must be between a minimum of 1 and a maximum of 64. frame_rate represents the number of frames per second.&quot;
            },
            &quot;repeat&quot;: {
              &quot;type&quot;: &quot;integer&quot;,
              &quot;enum&quot;: [-1, 1],
              &quot;description&quot;: &quot;The number of repetitions of the animation. Only two values are supported: -1 and 1. If it is set to -1, it repeats endlessly. If 1, it repeats once.&quot;
            }
          },
          &quot;required&quot;: [ &quot;frames&quot;, &quot;frame_rate&quot;, &quot;repeat&quot; ]
        }
      }
    }
  }
}
```

### Example

Here is an example of the schema.

```json
{
  &quot;name&quot;: &quot;Klaytn Core&quot;,
  &quot;description&quot;: &quot;Klaytn Story NFTs - Core&quot;,
  &quot;image&quot;: &quot;https://klaytn-story-bucket.s3.ap-northeast-2.amazonaws.com/core-face.png&quot;,
  &quot;namespaces&quot;: {
    &quot;zep&quot;: {
      &quot;type&quot;: &quot;avatar&quot;,
      &quot;properties&quot;: {
        &quot;image&quot;: &quot;https://klaytn-story-bucket.s3.ap-northeast-2.amazonaws.com/core-sheet.png&quot;,
        &quot;frame_width&quot;: 48,
        &quot;frame_height&quot;: 64,
        &quot;animations&quot;: {
          &quot;down&quot;: {
            &quot;frames&quot;: [ 1, 2, 3, 4 ],
            &quot;frame_rate&quot;: 8,
            &quot;repeat&quot;: -1
          },
          &quot;left&quot;: {
            &quot;frames&quot;: [ 6, 7, 8, 9 ],
            &quot;frame_rate&quot;: 8,
            &quot;repeat&quot;: -1
          },
          &quot;right&quot;: {
            &quot;frames&quot;: [ 11, 12, 13, 14 ],
            &quot;frame_rate&quot;: 8,
            &quot;repeat&quot;: -1
          },
          &quot;up&quot;: {
            &quot;frames&quot;: [ 16, 17, 18, 19 ],
            &quot;frame_rate&quot;: 8,
            &quot;repeat&quot;: -1
          },
          &quot;dance&quot;: {
            &quot;frames&quot;: [ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37 ],
            &quot;frame_rate&quot;: 8,
            &quot;repeat&quot;: 1
          },
          &quot;down_jump&quot;: {
            &quot;frames&quot;: [ 38 ],
            &quot;frame_rate&quot;: 8,
            &quot;repeat&quot;: -1
          },
          &quot;left_jump&quot;: {
            &quot;frames&quot;: [ 39 ],
            &quot;frame_rate&quot;: 8,
            &quot;repeat&quot;: -1
          },
          &quot;right_jump&quot;: {
            &quot;frames&quot;: [ 40 ],
            &quot;frame_rate&quot;: 8,
            &quot;repeat&quot;: -1
          },
          &quot;up_jump&quot;: {
            &quot;frames&quot;: [ 41 ],
            &quot;frame_rate&quot;: 8,
            &quot;repeat&quot;: -1
          },
          &quot;down_attack&quot;: {
            &quot;frames&quot;: [ 42 ],
            &quot;frame_rate&quot;: 8,
            &quot;repeat&quot;: 1
          },
          &quot;left_attack&quot;: {
            &quot;frames&quot;: [ 43 ],
            &quot;frame_rate&quot;: 8,
            &quot;repeat&quot;: 1
          },
          &quot;right_attack&quot;: {
            &quot;frames&quot;: [ 44 ],
            &quot;frame_rate&quot;: 8,
            &quot;repeat&quot;: 1
          },
          &quot;up_attack&quot;: {
            &quot;frames&quot;: [ 45 ],
            &quot;frame_rate&quot;: 8,
            &quot;repeat&quot;: 1
          },
          &quot;down_idle&quot;: {
            &quot;frames&quot;: [ 0 ],
            &quot;frame_rate&quot;: 1,
            &quot;repeat&quot;: 1
          },
          &quot;left_idle&quot;: {
            &quot;frames&quot;: [ 5 ],
            &quot;frame_rate&quot;: 1,
            &quot;repeat&quot;: 1
          },
          &quot;right_idle&quot;: {
            &quot;frames&quot;: [ 10 ],
            &quot;frame_rate&quot;: 1,
            &quot;repeat&quot;: 1
          },
          &quot;up_idle&quot;: {
            &quot;frames&quot;: [ 15 ],
            &quot;frame_rate&quot;: 1,
            &quot;repeat&quot;: 1
          }
        }
      }
    }
  }
}
```

## Backward Compatibility 
This standard can be fully [ERC-721](https://eips.ethereum.org/EIPS/eip-721), [ERC-1155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1155.md#erc-1155-metadata-uri-json-schema), [EIP-4955](https://eips.ethereum.org/EIPS/eip-4955), [KIP-17](https://kips.klaytn.foundation/KIPs/kip-17), and [KIP-37](https://kips.klaytn.foundation/KIPs/kip-37) compatible by adding an extension “avatars” attribute in the metadata. This allows developers to easily adopt the standard quickly.

## Reference
- [ERC-721](https://eips.ethereum.org/EIPS/eip-721) Non-Fungible Token Standard
- [ERC-1155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1155.md#erc-1155-metadata-uri-json-schema) Multi-Token Standard 
- [EIP-4955](https://eips.ethereum.org/EIPS/eip-4955)
- [KIP-17](https://kips.klaytn.foundation/KIPs/kip-17)
- [KIP-37](https://kips.klaytn.foundation/KIPs/kip-37)
- [Decentraland Docs](https://docs.decentraland.org/creator/wearables/linked-wearables/)

## Copyright
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
</description>
        <pubDate>Tue, 01 Nov 2022 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-87</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-87</guid>
      </item>
    
      <item>
        <title>Signed Data Standard</title>
        <category>Core</category>
        
          <comments>https://forum.klaytn.foundation/t/kip/6359</comments>
        
        <description>## Simple Summary
&lt;!--&quot;If you can&apos;t explain it simply, you don&apos;t understand it well enough.&quot; Provide a simplified and layman-accessible explanation of the KIP.--&gt;
This is to standardize message signing methods different from those of Ethereum.

## Abstract
&lt;!--A short (~200 word) description of the technical issue being addressed.--&gt;
This standard proposes a specification about how to handle signed data in Klaytn.

## Motivation
&lt;!--The motivation is critical for KIPs that want to change the Klaytn protocol. It should clearly explain why the existing protocol specification is inadequate to address the problem that the KIP solves. KIP submissions without sufficient motivation may be rejected outright.--&gt;
Several multisignature wallet implementations have been created which accepts `presigned` transactions. A `presigned` transaction is a chunk of binary `signed_data`, along with signature (`r`, `s` and `v`). The interpretation of the `signed_data` has not been specified in Klaytn and Ethereum solved this by [EIP-191](https://eips.ethereum.org/EIPS/eip-191) standard. Additionally, the signatures are different when using Metamask and Kaikas because they have the different preamble.

## Specification
&lt;!--The technical specification should describe the syntax and semantics of any new feature. The specification should be detailed enough to allow competing, interoperable implementations for any of the current Klaytn platforms (klaytn). --&gt;
This document is heavily derived from [EIP-191](https://eips.ethereum.org/EIPS/eip-191) written by Martin Holst Swende and Nick Johnson.

We propose the following format for `signed_data`:

```
0x19 &lt;1 byte version&gt; &lt;version specific data&gt; &lt;data to sign&gt;
```

The initial `0x19` byte is intended to ensure that the `signed_data` is not valid RLP.

&gt; For a single byte whose value is in the [0x00, 0x7f] range, that byte is its own RLP encoding.

That means that any `signed_data` cannot be one RLP-structure, but a 1-byte `RLP` payload followed by something else. Thus, any `signed_data` can never be an Klaytn transaction.

The following format is prepended before hashing in personal_sign:

```
&quot;\x19Klaytn Signed Message:\n&quot; + len(message)
```

Using `0x19` thus makes it possible to extend the scheme by defining a version `0x4b` (`K`) to handle these kinds of signatures.

### Registry of version bytes

| Version byte | KIP            | Description
| ------------ | -------------- | -----------
|    `0x4b`    | [97][kip-97]   | `personal_sign` messages

#### Version `0x4b` (K)

```
0x19 &lt;0x4b (K)&gt; &lt;laytn Signed Message:\n&quot; + len(message)&gt; &lt;data to sign&gt;
```

The version `0x4b` (K) has `&lt;laytn Signed Message:\n&quot; + len(message)&gt;` for the version-specific data. The data to sign can be any arbitrary data.

&gt; NB: The `K` in `Klaytn Signed Message` refers to the version byte 0x4b. The character `K` is `0x4b` in hexadecimal which makes the remainder, `laytn Signed Message:\n + len(message)`, the version-specific data.

[kip-97]: /KIPs/kip-97

### Specification of the caver.js API

```JavaScript
caver.klay.accounts.sign(&apos;Some data&apos;, &apos;0x{private key}&apos;);
```

### Returns

```shell
{
    message: &apos;Some data&apos;,
    messageHash: &apos;0x8ed2036502ed7f485b81feaec1c581d236a8b711e55a24077724879c8a263c2a&apos;,
    v: &apos;0x1b&apos;,
    r: &apos;0x4a57bcff1637346a4323a67acd7a478514d9f00576f42942d50a5ca0e4b0342b&apos;,
    s: &apos;0x5914e19a8ebc10ce1450b00a3b9c1bf0ce01909bca3ffdead1aa3a791a97b5ac&apos;,
    signature: &apos;0x4a57bcff1637346a4323a67acd7a478514d9f00576f42942d50a5ca0e4b0342b5914e19a8ebc10ce1450b00a3b9c1bf0ce01909bca3ffdead1aa3a791a97b5ac1b&apos;
}
```

## Backwards Compatibility
&lt;!-- All KIPs that introduce backwards incompatibilities must include a section describing these incompatibilities and their severity. The KIP must explain how the author proposes to deal with these incompatibilities. KIP submissions without a sufficient backwards compatibility treatise may be rejected outright. The authors should answer the question: &quot;Does this KIP require a hard fork?&quot; --&gt;
- [caver-js](https://docs.klaytn.foundation/content/dapp/sdk/caver-js/v1.4.1/api-references/caver.klay.accounts#sign) already sign in this format.
- [caver-java](https://javadoc.io/doc/com.klaytn.caver/core/1.10.0/com/klaytn/caver/wallet/keyring/PrivateKey.html) already sign in this format.


## Reference
[EIP-191](https://eips.ethereum.org/EIPS/eip-191)

## Copyright
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
</description>
        <pubDate>Tue, 10 Jan 2023 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-97</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-97</guid>
      </item>
    
      <item>
        <title>Treasury Fund Rebalancing</title>
        <category>Core</category>
        
          <comments>https://govforum.klaytn.foundation/t/kgp-6-proposal-to-establish-a-sustainable-and-verifiable-klay-token-economy/157</comments>
        
        <description>## Simple Summary

&lt;!--&quot;If you can&apos;t explain it simply, you don&apos;t understand it well enough.&quot; Provide a simplified and layman-accessible explanation of the KIP.--&gt;

This proposal suggests a smart contract interface standard that records the rebalance of treasury funds. The main objective is to facilitate the approval and redistribution of treasury funds to new addresses while keeping record of the the previous fund addresses.

## Abstract

&lt;!--A short (~200 word) description of the technical issue being addressed.--&gt;

Organizations need to manage treasury funds in a transparent and accountable manner. The proposed standard aims to make the management of treasury funds more transparent by recording the rebalance of treasury funds. The smart contract will keep records of the addresses which hold the treasury funds before and after rebalancing. It also facilates approval before execution.

## Motivation

&lt;!--The motivation is critical for KIPs that want to change the Klaytn protocol. It should clearly explain why the existing protocol specification is inadequate to address the problem that the KIP solves. KIP submissions without sufficient motivation may be rejected outright.--&gt;

Transparency is the one of most important aspect of blockchain. It is important to ensure that treasury funds are allocated and managed in a transparent and verifiable manner. The proposed smart contract aims to disclose the management of treasury funds in a transparent manner through smart contracts reducing the risk of errors and mismanagement. By providing transparency and accountability in the allocation and management of funds, the smart contract can help to build trust and confidence among stakeholders.

## Specification

&lt;!--The technical specification should describe the syntax and semantics of any new feature. The specification should be detailed enough to allow competing, interoperable implementations for any of the current Klaytn platforms (klaytn). --&gt;

The proposed smart contract will be implemented in Solidity and will be compatible with the Ethereum Virtual Machine (EVM). The smart contract will use Ownable contract to restrict access to certain functions to the owner of the contract.

The smart contract will have the following features:

- Register/Remove fund addresses
  - Previous fund addresses(Retired): Retired fund represents the previous fund addresses such as KGF or KIR
  - New fund addresses(Newbie): Newbie fund represents the &apos;rebalanced fund&apos; such as KCF or KFF
- Approve fund addresses and fund allocation. Receive approval for asset transfer from the admins/owners of the funds
- Reset the storage values at any unforeseen circumstances before Finalized
- Finalize the smart contract after execution

### Smart Contracts Overview

#### Enums

The smart contract will have the following enum to track the status of the contract:

- `Initialized - 0`: The initial state of the contract.
- `Registered - 1`: Retirees and Newbies are registered.
- `Approved - 2`: All retirees approved by msg.sender
- `Finalized - 3`: Rebalance executed and finalized.

#### Life Cycle

The contract status should follow the ENUM order above during status transition. The only way to go to previous state is by calling Reset() function.

![](/assets/kip-103/lifecycle.png)

Status transition

- Initialized → Registered → Approved → Finalized ✅
- Initialized, Registered, Approved → Initialized, when `reset()` is called.
- Registered, Approved → Initialized, when `reset()` is called.

All other status transitions are not possible.

#### Structs

The smart contract will have the following structs:

- `Retired`: to represent the details of retired and approver addresses.
  - Retired represents the previous fund addresses such as KGF or KIR
  - Approver represents the admin of the retired address. Approver addresses are stored to track the approvals received for asset transfer
- `Newbie`: to represent newbies and their fund allocation.
  - Newbie represents the &apos;rebalanced fund&apos; such as KCF or KFF

#### Storage

The smart contract will have the following storage variables:

- `retirees`: array of `Retired` struct.
- `newbies`: array of `Newbie` struct.
- `status`: current status of the contract.
- `rebalanceBlockNumber`: the target block number of the execution of rebalancing.
- `memo`: result of the treasury fund rebalance.

#### Modifiers

The smart contract will have the following modifier:

- `onlyAtStatus`: to restrict access to certain functions based on the current status of the contract. If the status is not the same with the given status, it reverts.

#### Constructor

The smart contract will have the following constructor:

- `constructor`: to initialize the contract with the target block number of the execution of rebalance.

#### State Changing Functions

The smart contract will have the following state changing functions:

- `registerRetired`: to register retired details. Retired stores the retired address and approvers
- `removeRetired`: to remove retired details from the array.
- `registerNewbie`: to register newbie address and its fund distribution.
- `removeNewbie`: to remove newbie details from the array.
- `finalizeRegistration`: sets the status to Registered only executed by owner. After this stage, registrations will be restricted.
- `approve`: to approve a retiredAddress by the admin.
- `finalizeApproval`: sets the status to Approved. After this stage, approvals will be restricted.
  &lt;/br&gt;
  **Conditions**
  - every retiredAddress must be approved
  - min required admin’s approval is done for retired contract address.
  - sum of retired’s balance is greater than sum of the newbies&apos; amount
    &lt;/br&gt;
- `reset`: resets all storage values to empty objects except rebalanceBlockNumber. It can only be called during Initialization, Registration and Approved status.
- `finalizeContract`: to record the execution result of the rebalance and finalize the contract. The storage data cannot be modified after this stage.

#### Fallback Function

The smart contract will have a fallback function to revert any payments. Since its a treasury contract, it should not accept any payments nor it should allow any withdrawal.

### Core Logic Overview

To enable treasury fund rebalancing, a Klaytn node should specify a deployed smart contract address and a target block number on the node configuration like hardfork items. At the configured block number, a Klaytn node reads registered information from the smart contract and executes treasury fund rebalancing.

#### Chain Configuration

ChainConfig introduces the following fields for treasury fund rebalancing. The configurations are used to trigger rebalancing and find the contract storing rebalancing information. All nodes in the network should update `genesis.json` configuration with the same value as if updating a hardfork block number. The configuration values for Baobab and Cypress networks can be hard coded on the client source code.

- `kip103CompatibleBlock`: Treasury fund rebalance executing block which is the same as `rebalanceBlockNumber` of the smart contract
- `kip103ContractAddress`: The address of the treasury fund rebalancing contract

#### Validation

Before executing rebalancing, registered values on the smart contract are validated to confirm whether the owners of the retired account agree on the rebalancing and whether the rebalancing doesn&apos;t cause inflation of KLAY. The following should be confirmed before the execution.

- `Kip103CompatibleBlock` == `contract.rebalanceBlockNumber`: Ensure the owner of the retired accounts agree on this timing
- `contract.status` == `Approved`: Confirm the status of the contract is ready to rebalance
- `contract.checkRetiredsApproved()`: Double-check the agreement of the retired accounts&apos; owners at the execution time since the ownership is replaceable
- `totalRetiredAmount &gt;= totalNewbieAmount`: Ensure the rebalancing doesn&apos;t issue any KLAY

#### Execution

The rebalancing is executed at the end of the block processing process. In other words, it is executed after processing all transactions of the block and distributing block rewards. If a retired account is one of the receiver of the block reward, the amount also will be used for rebalancing. All KLAY in newbies account before rebalancing will be burnt as well. After rebalancing, the remaining KLAY of retired accounts will be burnt. Below is the new fund allocation logic including burn.

```
for addr := range Retireds {
	state.SetBalance(addr, 0)
}

for addr, balance := Newbie {
	// if newbie has KLAY before the allocation, it will be burnt
	currentBalance := state.GetBalance(addr)
	Burnt = Burnt + currentBalance

	state.SetBalance(addr, balance)
}
```

#### Result

The execution result of treasury fund rebalancing will be printed as an INFO-level log on each node. The owner of the treasury rebalancing contract is supposed to update the log on the smart contract as `memo` finalizing the status of the contract. Anyone can read and verify the rebalancing result by interacting with the smart contract. The data type of `memo` is byte array containing marshaled json data. Refer to the following format and example if you want to parse it.

**Format**

```
  {
    &quot;retirees&quot;: [ { &quot;retired&quot;: &quot;0xRetiredAddress1&quot;, &quot;balance&quot;: [removed balance in Peb] }, { &quot;retired&quot;: &quot;0xRetiredAddress2&quot;, &quot;balance&quot;: removed balance in Peb], ... } ],
    &quot;newbies&quot;: [ { &quot;newbie&quot;: &quot;0xNewbieAddress1&quot;, &quot;fundAllocated&quot;: [new allocated balance in Peb] }, { &quot;newbie&quot;: &quot;0xNewbieAddress2&quot;, &quot;fundAllocated&quot;: [new allocated balance in Peb], ... } ],
    &quot;burnt&quot;: [burnt amount in Peb],
    &quot;success&quot;: [true/false]
}
```

Note: 10^18 [Peb](https://docs.klaytn.foundation/content/klaytn/design/klaytn-native-coin-klay) is equal to 1 KLAY

**Example**

```
memo=&quot;{
  &quot;retirees&quot;:[
    {&quot;retired&quot;:&quot;0xafd197d383453b08b7c1509bdb4b6afee9f66578&quot;,&quot;balance&quot;:5000000001892521406055074536},
    {&quot;retired&quot;:&quot;0x5678300abc1f599d865c3525df851b3902c88266&quot;,&quot;balance&quot;:2280917577134567890000000000},
    {&quot;retired&quot;:&quot;0x278e6332d69eed782784d21802e3504a64a16456&quot;,&quot;balance&quot;:352628334320754365571158456},
    {&quot;retired&quot;:&quot;0x3d803a7375a8ee5996f52a8d6725637a89f5bbf8&quot;,&quot;balance&quot;:112778356560412760866604672}
  ],
  &quot;newbies&quot;:[
    {&quot;newbie&quot;:&quot;0x4f04251064274252d27d4af55bc85b68b3add992&quot;,&quot;fundAllocated&quot;:2000000000000000000000000000},
    {&quot;newbie&quot;:&quot;0x85d82d811743b4b8f3c48f3e48a1664d1ffc2c10&quot;,&quot;fundAllocated&quot;:180000000000000000000000000},
    {&quot;newbie&quot;:&quot;0xdd4c8d805fc110369d3b148a6692f283ffbdccd3&quot;,&quot;fundAllocated&quot;:270000000000000000000000000}
  ],
  &quot;burnt&quot;:5296324269908256422492837664,
  &quot;success&quot;:true
}
```

## Rationale

&lt;!--The rationale fleshes out the specification by describing what motivated the design and why particular design decisions were made. It should describe alternate designs that were considered and related work, e.g. how the feature is supported in other languages. The rationale may also provide evidence of consensus within the community, and should discuss important objections or concerns raised during discussion.--&gt;

The smart contract is mainly for recording the details, and the Core will execute the fund re-distributions.

### Design decision

#### KLAY transfer is not allowed via smart contracts

As the balance of treasury funds keeps increasing for every block with the block reward its hard to keep track of the balances and rebalance token allocation. So smart contract will only record the rebalanced allocation and the core will execute the allocation by reading from the contract.

#### Approval of retiredAddress

To record the addresses in a verifiable manner, the addresses are verified in the contract by calling approve method. The retiredAddress can either be a Contract Account (CA) or an Externally Owned Account(EOA).

- In case of an EOA, verification occurs when the account holder directly calls the approve function. `msg.sender == retiredAddress`
- In case of a Contract, verification occurs when the admin of the contract calls approve function. The smart contract calls the `getState()` function implemented in the retiredAddress contract to get the admin details. `msg.sender == admin`
  - `getState()` funtion is implemented in Klaytn treasury contracts. It returns the adminList and quorom (min required admins to approve).
  - Condition: Min required admins should approve the retiredAddress contract.

#### No Withdrawal

Smart contract is not allowed to receive KLAY due to security reasons. So any transaction sending KLAY to the contract will be reverted and withdraw function is not implemented.

#### Finalize Contract

Once the re-distribution a.k.a rebalance is executed by the Core, the status of the smart contract will be finalized by adding a memo. Any modifications to the storage data will be restricted after finalization.

Once Finalized anyone can read and verify the rebalancing result by querying the memo in the smart contract.

Query Result:

```js
memo= &quot;{
  &quot;retirees&quot;: [{&quot;retired&quot;: &quot;0xRetiredAddress1&quot;, &quot;balance&quot;: &quot;0xamount&quot;}, {&quot;retired&quot;: &quot;0xRetiredAddress2&quot;, &quot;balance&quot;: &quot;0xamount&quot;}, ...],
  &quot;newbies&quot;: [{&quot;newbie&quot;: &quot;0xNewbieAddress1&quot;, &quot;fundAllocated&quot;: &quot;0xamount&quot;}, {&quot;newbie&quot;: &quot;0xNewbieAddress2&quot;,&quot;fundAllocated&quot;: &quot;0xamount&quot;}, ...],
  &quot;burnt&quot;: &quot;0xamount&quot;,
  &quot;success&quot;: true
}&quot;
```

## Backwards Compatibility

&lt;!-- All KIPs that introduce backwards incompatibilities must include a section describing these incompatibilities and their severity. The KIP must explain how the author proposes to deal with these incompatibilities. KIP submissions without a sufficient backwards compatibility treatise may be rejected outright. The authors should answer the question: &quot;Does this KIP require a hard fork?&quot; --&gt;

- The foundation should deploy new TreasuryRebalance contract to record the token redistribution.
- To rebalance the funds and redistribute in a consistent manner the foundation should burn the designated funds before re-distribution.
- This does not affect the backward compatibility as this a newly dpeloyed contract

## Implementation

&lt;!--The implementations must be completed before any KIP is given status &quot;Final&quot;, but it need not be completed before the KIP is accepted. While there is merit to the approach of reaching consensus on the specification and rationale before writing code, the principle of &quot;rough consensus and running code&quot; is still useful when it comes to resolving many discussions of API details.--&gt;

#### Example implementation

```solidity
pragma solidity ^0.8.0;

/**
 * @dev External interface of TreasuryRebalance
 */
interface ITreasuryRebalance {
    /**
     * @dev Emitted when the contract is deployed
     * `rebalanceBlockNumber` is the target block number of the execution the rebalance in Core
     * `deployedBlockNumber` is the current block number when its deployed
     */
    event ContractDeployed(
        Status status,
        uint256 rebalanceBlockNumber,
        uint256 deployedBlockNumber
    );

    /**
     * @dev Emitted when a Retired is registered
     */
    event RetiredRegistered(address retired);

    /**
     * @dev Emitted when a Retired is removed
     */
    event RetiredRemoved(address retired);

    /**
     * @dev Emitted when a Newbie is registered
     */
    event NewbieRegistered(address newbie, uint256 fundAllocation);

    /**
     * @dev Emitted when a Newbie is removed
     */
    event NewbieRemoved(address newbie);

    /**
     * @dev Emitted when a admin approves the retired address.
     */
    event Approved(address retired, address approver, uint256 approversCount);

    /**
     * @dev Emitted when the contract status changes
     */
    event StatusChanged(Status status);

    /**
     * @dev Emitted when the contract is finalized
     * memo - is the result of the treasury fund rebalancing
     */
    event Finalized(string memo, Status status);

    // Status of the contract
    enum Status {
        Initialized,
        Registered,
        Approved,
        Finalized
    }

    /**
     * Retired struct to store retired address and their approver addresses
     */
    struct Retired {
        address retired;
        address[] approvers;
    }

    /**
     * Newbie struct to newbie receiver address and their fund allocation
     */
    struct Newbie {
        address newbie;
        uint256 amount;
    }

    // State variables
    function status() external view returns (Status); // current status of the contract

    function rebalanceBlockNumber() external view returns (uint256); // the target block number of the execution of rebalancing

    function memo() external view returns (string memory); // result of the treasury fund rebalance

    /**
     * @dev to get retired details by retiredAddress
     */
    function getRetired(
        address retiredAddress
    ) external view returns (address, address[] memory);

    /**
     * @dev to get newbie details by newbieAddress
     */
    function getNewbie(
        address newbieAddress
    ) external view returns (address, uint256);

    /**
     * @dev returns the sum of retirees balances
     */
    function sumOfRetiredBalance()
        external
        view
        returns (uint256 retireesBalance);

    /**
     * @dev returns the sum of newbie funds
     */
    function getTreasuryAmount() external view returns (uint256 treasuryAmount);

    /**
     * @dev returns the length of retirees list
     */
    function getRetiredCount() external view returns (uint256);

    /**
     * @dev returns the length of newbies list
     */
    function getNewbieCount() external view returns (uint256);

    /**
     * @dev verify all retirees are approved by admin
     */
    function checkRetiredsApproved() external view;

    // State changing functions
    /**
     * @dev registers retired details
     * Can only be called by the current owner at Initialized state
     */
    function registerRetired(address retiredAddress) external;

    /**
     * @dev remove the retired details from the array
     * Can only be called by the current owner at Initialized state
     */
    function removeRetired(address retiredAddress) external;

    /**
     * @dev registers newbie address and its fund distribution
     * Can only be called by the current owner at Initialized state
     */
    function registerNewbie(address newbieAddress, uint256 amount) external;

    /**
     * @dev remove the newbie details from the array
     * Can only be called by the current owner at Initialized state
     */
    function removeNewbie(address newbieAddress) external;

    /**
     * @dev approves a retiredAddress,the address can be a EOA or a contract address.
     *  - If the retiredAddress is a EOA, the caller should be the EOA address
     *  - If the retiredAddress is a Contract, the caller should be one of the contract `admin`
     */
    function approve(address retiredAddress) external;

    /**
     * @dev sets the status to Registered,
     *      After this stage, registrations will be restricted.
     * Can only be called by the current owner at Initialized state
     */
    function finalizeRegistration() external;

    /**
     * @dev sets the status to Approved,
     * Can only be called by the current owner at Registered state
     */
    function finalizeApproval() external;

    /**
     * @dev sets the status of the contract to Finalize. Once finalized the storage data
     * of the contract cannot be modified
     * Can only be called by the current owner at Approved state after the execution of rebalance in the core
     *  - memo format: { &quot;retirees&quot;: [ { &quot;retired&quot;: &quot;0xaddr&quot;, &quot;balance&quot;: 0xamount },
     *                 { &quot;retired&quot;: &quot;0xaddr&quot;, &quot;balance&quot;: 0xamount }, ... ],
     *                 &quot;newbies&quot;: [ { &quot;newbie&quot;: &quot;0xaddr&quot;, &quot;fundAllocated&quot;: 0xamount },
     *                 { &quot;newbie&quot;: &quot;0xaddr&quot;, &quot;fundAllocated&quot;: 0xamount }, ... ],
     *                 &quot;burnt&quot;: 0xamount, &quot;success&quot;: true/false }
     */
    function finalizeContract(string memory memo) external;

    /**
     * @dev resets all storage values to empty objects except targetBlockNumber
     */
    function reset() external;
}
```

Reference Implementation : https://github.com/klaytn/treasury-rebalance/tree/main/contracts

## Test Cases

&lt;!--Test cases for an implementation are mandatory for KIPs that are affecting consensus changes. Other KIPs can choose to include links to test cases if applicable.--&gt;

You can find test cases for this KIP, please refer to [this link](https://github.com/klaytn/treasury-rebalance/blob/main/test/TreasuryRebalance.js).

## Reference

n/a

## Copyright

Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
</description>
        <pubDate>Fri, 24 Feb 2023 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-103</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-103</guid>
      </item>
    
      <item>
        <title>BLS public key registry</title>
        <category>Core</category>
        
          <comments>https://github.com/klaytn/kips/issues/113</comments>
        
        <description>## Simple Summary

A design of simple BLS12-381 registry.

## Abstract

This standard defines an interface for BLS12-381 registry, which enables nodes or smart contracts to access and view all the public keys of the associated addresses.

## Motivation

A standard interface allows developers to associate an address with a BLS public key. The objective is to design a simple registry interface, enabling Klaytn nodes to read BLS keys, each of which is associated with a node ID.

## Specification

The key words &quot;MUST&quot;, &quot;MUST NOT&quot;, &quot;REQUIRED&quot;, &quot;SHALL&quot;, &quot;SHALL NOT&quot;, &quot;SHOULD&quot;, &quot;SHOULD NOT&quot;, &quot;RECOMMENDED&quot;, &quot;MAY&quot;, and &quot;OPTIONAL&quot; in this document are to be interpreted as described in RFC 2119.

Registry contract must implement the IKIP113 interface:

```solidity
pragma solidity ^0.8.19;

/// @title KIP-113 BLS public key registry
/// @dev See https://github.com/klaytn/kips/issues/113
interface IKIP113 {
    struct BlsPublicKeyInfo {
        /// @dev compressed BLS12-381 public key (48 bytes)
        bytes publicKey;
        /// @dev proof-of-possession (96 bytes)
        ///  must be a result of PopProve algorithm as per
        ///  draft-irtf-cfrg-bls-signature-05 section 3.3.3.
        ///  with ciphersuite &quot;BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_&quot;
        bytes pop;
    }

    /// @dev Returns all the stored addresses, public keys, and proof-of-possessions at once.
    ///  _Note_ The function is not able to verify the validity of the public key and the proof-of-possession due to the lack of [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537). See [validation](https://kips.klaytn.foundation/KIPs/kip-113#validation) for off-chain validation.
    function getAllBlsInfo() external view returns (address[] memory nodeIdList, BlsPublicKeyInfo[] memory pubkeyList);
}
```

### Definitions

The terms public key and proof-of-possession (hereinafter referred to as pop) are from the ciphersuite [BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP\_](https://www.ietf.org/archive/id/draft-irtf-cfrg-bls-signature-05.html#section-4.2.3). Things to note:

- Public key: 48 bytes.
- Proof-of-possession (pop): 96 bytes.

### Smart Contracts Overview

The proposed smart contract must be implemented in Solidity and must be compatible with the Ethereum Virtual Machine (EVM).

The smart contract must have the following features:

- View the registered public keys

#### Methods

##### getAllBlsInfo

Returns all the stored addresses, public keys, and proof-of-possessions at once.

_Note_ The contract is not able to verify the validity of the public key and the proof-of-possession due to the lack of [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537). See [validation](#validation) for off-chain validation.

```solidity
function getAllBlsInfo() external view returns (address[] nodeIdList, BlsPublicKeyInfo[] pubkeyList)
```

#### Validation

After reading from IKIP113, users must perform the following validations:

- [KeyValidate](https://www.ietf.org/archive/id/draft-irtf-cfrg-bls-signature-05.html#section-2.5)
- [PopVerify](https://www.ietf.org/archive/id/draft-irtf-cfrg-bls-signature-05.html#section-3.3.3)

### JSON-RPC API

The following JSON-RPC method for the Klaytn node should be added to provide the registered BLS public keys.

- Name: `klay_getBlsInfos`
- Description: Returns all registered BLS public keys and proof-of-possessions of the validators.
- Parameters:
    - `number` - (optional) integer or hexadecimal block number, or the string &quot;pending&quot; or &quot;latest&quot;.
- Returns: A map of the addresses and their associated BLS public keys, proof-of-possessions and verify error.
    - `publicKey` - The compressed BLS12-381 public key in hex string.
    - `pop` - The proof-of-possession in hex string.
    - `verifyErr` - The error message of the verification. If the verification is successful, it returns null.
* Example
    ```
    // Request
    curl -X POST -H &quot;Content-Type: application/json&quot; --data &apos;{&quot;jsonrpc&quot;:&quot;2.0&quot;, &quot;method&quot;:&quot;klay_getBlsInfos&quot;, &quot;params&quot;:[&quot;latest&quot;],&quot;id&quot;:1}&apos; http://localhost:8551
    // Response
    {
        &quot;jsonrpc&quot;:&quot;2.0&quot;,
        &quot;id&quot;:1,
        &quot;result&quot;:{
            &quot;0x18a80d0d3a8b7348277c497105cc2163b242799a&quot;:{
                &quot;publicKey&quot;:&quot;8247754efd7aa2b02b30b6db8e1c24bb8c4207f5600470c2dafde1b46270a6c8c3fe9e7f8b939965d6a59dbe2ebd0cf7&quot;,
                &quot;pop&quot;:&quot;8c35987399eab7a98ad24e869a7d7225bdb95203167e6aa20b3db73c0a91317e805e48d33c606484b1e9db3d8c8d64b60f0c6a8816b219f43747bf201dd65193ade23434270c78441e370a95d7632a6a76f1f3a62842425f76ebc4a041fc1dbb&quot;,
                &quot;verifyErr&quot;:null
            },
            &quot;0x527d6d61f53305c1e1d3680b23393c3c13c8db9e&quot;:{
                &quot;publicKey&quot;:&quot;a16153b81453a9e3099728ff1d3aac3d3fca32b3594407277ec0b8df4aa66cba743916d9f2098707aecc350954b0cd4e&quot;,
                &quot;pop&quot;:&quot;a92c7ba586a12a38e3a7bbfed7f7a311664cb0dc66a7bd26ee79bed4755e7652850dff1cfabf39acd58ea8efe24ff7c217f1d5c2ac3fe948016056e99b03bc26aa8c59b89cfc9ee961bf8cfd1802295d4ff07852f93d0607a53655f15665aad9&quot;,
                &quot;verifyErr&quot;:null
            },
            &quot;0xa72ccdf72dc401df79805013a42b74f12b43caa1&quot;:{
                &quot;publicKey&quot;:&quot;883eb2c623a19671461bc0dadcfa17384198ff06b2e2c9cd1ca539ff554c377256624e9f5f69d27e17e4635080938d9a&quot;,
                &quot;pop&quot;:&quot;ac1bb19588b2ec7e1452486b8a4afe0ecb27847bca04b7e4919a9d6b70c1342dcfb1a6983788d3756607b84d3d7a009118246ed5b3b3449638e591ab9fe2c4ec5cafb64ddc12a5dcf48cbcf305175d5dddb48845e80f7cb9a32d5f2e67ca2163&quot;,
                &quot;verifyErr&quot;:null
            }
        }
    }
    ```
## Backwards Compatibility

This does not affect the backward compatibility as this is a newly deployed contract.

## Copyright

Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
</description>
        <pubDate>Mon, 22 May 2023 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-113</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-113</guid>
      </item>
    
      <item>
        <title>Supplant DIFFICULTY opcode with RANDOM</title>
        <category>Core</category>
        
          <comments>https://github.com/klaytn/kips/issues/114</comments>
        
        <description>## Simple Summary

Introduce on-chain randomness and expose it in the EVM by supplanting DIFFICULTY opcode semantics

## Abstract

This standard outlines an approach for generating an on-chain random value based on digital signature. Specifically, it introduces new fields, namely `randomReveal` and `mixHash`, in the block header; `randomReveal` is a verifiable random number, and `mixHash` is a derivation from a mixture of `randomReveals` and additional information. They serve as an unpredictable, verifiable, and dependable source of on-chain randomness. In addition, these fields can be fetched from the EVM via `RANDOM (0x44)` opcode.

## Motivation

An unpredictable, verifiable, and dependable on-chain randomness can be utilized in various use-cases including the protocol and applications.

- Unpredictability indicates that the random should be as difficult to predict as possible.

- Verifiability ensures that the random is authentic and cannot be manipulated or forged.

- Dependability implies that the random is generated without trusting any external sources of randomness, such as oracles.

An example of such use-cases is the block proposer selection. An exemplary requirement can be that the next block&apos;s proposer remains undisclosed until the latest block has been finalized, or that the proposer cannot tamper with deciding the next block&apos;s proposer.

In this proposal, new header fields `randomReveal` and `mixHash` are introduced. `randomReveal` is the signature of the proposer, and `randomReveal`s are mixed to compute `mixHash`, which users can use for a part of the random source. `mixHash` is unpredictable, verifiable, and dependable:

- (Unpredictability) the signature is random and so is `mixHash`.
- (Verifiability) the signature can be verified with the public key of the proposer, and `mixHash` is computed in a deterministic manner.
- (Dependability) `mixHash` only relies on the validators.

## Specification

The key words &quot;MUST&quot;, &quot;MUST NOT&quot;, &quot;REQUIRED&quot;, &quot;SHALL&quot;, &quot;SHALL NOT&quot;, &quot;SHOULD&quot;, &quot;SHOULD NOT&quot;, &quot;RECOMMENDED&quot;, &quot;MAY&quot;, and &quot;OPTIONAL&quot; in this document are to be interpreted as described in RFC 2119.

The terms hash_to_point (hereinafter referred to as hash), public key, proof-of-possession (hereinafter referred to as pop) are from the ciphersuite [BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP\_](https://www.ietf.org/archive/id/draft-irtf-cfrg-bls-signature-05.html#section-4.2.3).

### Parameters

| Constant     | Value |
| ------------ | ----- |
| `FORK_BLOCK` | TBD   |

### Block Structure

Beginning with `FORK_BLOCK`, client software must set the fields in a block header:

- `randomReveal`: 96 bytes. It must be specified in the 15th field (0-indexed) of a block header.

- `mixHash`: 32 bytes. It must be specified in the 16th field (0-indexed) of a block header.

### Block Processing

#### Block Proposal

Beginning with `FORK_BLOCK`, the proposer must fill the new fields in the block proposal:

- `randomReveal`: The proposer must sign the hash of the proposing block number.

- `mixHash`: The proposer must XOR the keccak256 hash of `randomReveal` and the previous block&apos;s `mixHash`. If the previous block&apos;s `mixHash` is null (e.g., previous block is before `FORK_BLOCK`), then use 32 bytes of zero instead.

```python
class Header:
	parentHash:   hash
	rewardbase:   address
	root:         hash
	txHash:       hash
	receiptHash:  hash
	bloom:        bloom
	blockScore:   int
	number:       int
	gasUsed:      uint64
	time:         int
	timeFoS:      uint8
	extra:        bytes
	governance:   bytes
	vote:         bytes
	baseFee:      int

	randomReveal: bytes # new
	mixHash:      bytes # new

header.randomReveal = sign(privateKey, header.number)
header.mixHash = xor(prevHeader.mixHash, keccak256(randomReveal))
```

#### Validation

Beginning with `FORK_BLOCK`, validators must validate the new fields:

- `randomReveal`

  - The validators must fetch the public key and the pop of the proposer by `getInfo(cnNodeId)` of the BLS registry standardized by [KIP-113](https://github.com/klaytn/kips/blob/main/KIPs/kip-113.md#validation).
  - The validators must verify the validity of the proposer public key using pop as mentioned in [here](https://github.com/klaytn/kips/blob/main/KIPs/kip-113.md#validation).
  - The validators must verify if `randomReveal` is signed by the proposer public key.

- `mixHash`: The validators must verify the validity of the proposed `mixHash` by calculating `mixHash` using the aforementioned `mixHash` algorithm.

### Pseudocode

See the following pseudocode that describes the block processing in python:

```python
DEFAULT_MIXHASH = b&quot;\x00&quot; * 32

def fill_kip114_header(newHeader, prevHeader, privateKey):
    newHeader.randomReveal = calc_random_reveal(privateKey, newHeader.number)

    if newHeader.number == FORK_BLOCK:
        prevMixHash =  DEFAULT_MIXHASH
    else:
        prevMixHash = prevHeader.mixHash
    newHeader.mixHash = calc_mix_hash(prevMixHash, newHeader.randomReveal)

def calc_random_reveal(privateKey, headerNumber):
    return sign(privateKey, headerNumber)

def calc_mix_hash(prevMixHash, randomReveal):
    return xor(prevMixHash, keccak256(randomReveal))

def verify_kip114_header(newHeader, prevHeader):
    # Getting PoP information from the smart contract registry defined in KIP-113.
    [proposerPubkey, proposerPop] = get_proposer_pubkey_pop()
    # pop verify
    if not pop_verify(proposerPubkey, proposerPop):
        return False

    # signature verify
    if not verify(proposerPubkey, newHeader.number, newHeader.randomReveal):
        return False

    # mixHash verify
    if newHeader.number == FORK_BLOCK:
        prevMixHash = DEFAULT_MIXHASH
    else:
        prevMixHash = prevHeader.mixHash
    return newHeader.mixHash == calc_mix_hash(prevMixHash, newHeader.randomReveal):
```

### EVM

Beginning with `FORK_BLOCK`, the `RANDOM (0x44)` instruction must return the value of `mixHash` of the current block it is executing in.

Note: The gas cost of the `RANDOM (0x44)` opcode remains unchanged.

### Genesis Block

If `FORK_BLOCK` is zero, the genesis block must have `randomReveal` and `mixHash` fields.

## Rationale

### Names of the header fields

The naming is highly inspired by [EIP-4339](https://eips.ethereum.org/EIPS/eip-4399).
We use `randomReveal` instead of RANDAO reveal because the randoms are generated in a different manner.

### Block size increment due to new fields in the header

The size increment cost of RLP-encoded header is around 131 bytes per block, which is approximately 3.8GB per year. It is a negligible cost compared to its benefit.

### BLS-based random over EC-based random

We chose BLS-based verifiable random over EC-based because:

- A BLS library written in Golang is easy to find as in [link](https://github.com/klaytn/klaytn/issues/1823), whereas it is not the case for EC-based as in [link](https://www.ietf.org/archive/id/draft-irtf-cfrg-vrf-15.html#section-6).
- There is a potential consensus change based on BLS signature. Therefore, this proposal gently introduces BLS signature to the Klaytn protocol as well as adding on-chain randomness.

## Backwards Compatibility

Blocks before `FORK_BLOCK` will remain the same.

## Security Considerations

### Biasability

BLS signature as well as the signing message (i.e., proposing block number) is deterministic. Thus, the proposer has no influence over the computation of `mixHash`. In addition, the biasability of `mixHash` mainly depends on that of the hash of `randomReveal`. To the best of our knowledge, keccak256 is not a biased hash function. Therefore, `mixHash`, which is the XOR of the output of keccak256 and the previous `mixHash`, is unbiased.

### Predictability

A list of inputs influencing future randomness consists of but is not limited to the following items:

- **Number of colluding validators.** When colluding validators become proposers of consecutive blocks, they collectively share their `randomReveal`s in advance to predict the value of `mixHash`.
- **Proposer selection policy.** This contributes to how likely the colluding validators become proposers of consecutive blocks.

Define `f(x, N, C)`:

- `N`: the number of validators
- `C`: the number of colluding validators (N &gt; C)
- `x`: the number of blocks from the latest block
- `f`: the probability of predicting the value of `mixHash` `x` blocks in advance. In other words, the probability of colluding validators becoming proposers for `x` consecutive blocks.

Under random proposer selection policy, the proposer is randomly selected after each block, thus `f(x, N, C) = (C/N)^x`.

### Tips for application developers

The following tips attempt to reduce predictability and biasability of randomness outputs returned by `RANDOM (0x44)`:

- Make your applications rely on the future randomness with a reasonably high lookahead. Given that IBFT permits one third of the validators to be malicious, developers are advised to give a distance of 200 blocks between bidding and rolling the dice to be &quot;95 nines&quot; safe.

## Implementation

A simulation in Python:

```python
from blspy import PrivateKey, G1Element, G2Element, PopSchemeMPL  # blspy
from Crypto.Hash import keccak  # pycryptodome

FORK_BLOCK = 100  # TBD
DEFAULT_MIXHASH = b&quot;\x00&quot; * 32

validatorNum: int = 3
proposerBLSPrivKeys: list[PrivateKey] = [
    PrivateKey.from_bytes((i + 1).to_bytes(32, byteorder=&quot;big&quot;))
    for i in range(validatorNum)
]  # b&quot;\x00..\x01&quot;, b&quot;\x00..\x02&quot;, ...
proposerBLSPublicKeys: list[G1Element] = [i.get_g1() for i in proposerBLSPrivKeys]
proposerAddrs: list[str] = [
    str(i + 1).zfill(40) for i in range(validatorNum)
]  # &quot;0x00..1, 0x00..2, ...&quot;


# Block header. Only relevant fields are shown here.
class Header:
    number: int = 0  # Block number
    randomReveal: bytes
    mixHash: bytes
    proposer: str

    def __init__(self, number: int, randomReveal: bytes = b&quot;&quot;, mixHash: bytes = b&quot;&quot;):
        self.number = number
        self.randomReveal = randomReveal
        self.mixHash = mixHash
        self.proposer = proposerAddrs[number % validatorNum]

    def __repr__(self):
        return &quot;number: {}\n\nrandomReveal ({} bytes): {}\n\nmixHash ({} bytes): {}&quot;.format(
            self.number,
            len(self.randomReveal),
            self.randomReveal,
            len(self.mixHash),
            self.mixHash,
        )


class BlsPublicKeyInfo:
    publicKey: bytes
    pop: bytes

    def __init__(self, publicKey, pop):
        self.publicKey = publicKey
        self.pop = pop


# A BLSRegistry that supports the interface IKIP-113 (https://github.com/klaytn/kips/blob/main/KIPs/kip-113.md)
class BLSRegistry:
    mapping = {}

    def __init__(self):
        for i, proposerAddr in enumerate(proposerAddrs):
            pop = bytes(PopSchemeMPL.pop_prove(proposerBLSPrivKeys[i]))
            self.mapping[proposerAddr] = BlsPublicKeyInfo(
                bytes(proposerBLSPublicKeys[i]), pop
            )

    def getInfo(self, addr: str) -&gt; BlsPublicKeyInfo:
        return self.mapping[addr]


def is_kip114_fork_enabled(num) -&gt; bool:
    return FORK_BLOCK &lt;= num


# dummy function for fetching the proposer secret key
# in implementation, this should be fetched from a file
def get_proposer_private_key(num) -&gt; PrivateKey:
    proposerIdx = num % validatorNum
    return proposerBLSPrivKeys[proposerIdx]


def fill_kip114_header(newHeader, prevHeader, privateKey):
    newHeader.randomReveal = calc_random_reveal(privateKey, newHeader.number)

    if newHeader.number == FORK_BLOCK:
        prevMixHash = DEFAULT_MIXHASH
    else:
        prevMixHash = prevHeader.mixHash
    newHeader.mixHash = calc_mix_hash(prevMixHash, newHeader.randomReveal)


def calc_random_reveal(sk, num) -&gt; bytes:
    msg = block_num_to_bytes(num)
    return bytes(PopSchemeMPL.sign(sk, msg))


def calc_mix_hash(prevMixHash, randomReveal) -&gt; bytes:
    return xor(prevMixHash, keccak256(randomReveal))


def gen_next_header(prevHeader) -&gt; Header:
    nextBlockNum = prevHeader.number + 1
    newHeader = Header(nextBlockNum)
    if is_kip114_fork_enabled(nextBlockNum):
        privateKey = get_proposer_private_key(nextBlockNum)
        fill_kip114_header(newHeader, prevHeader, privateKey)

    return newHeader


def keccak256(msg) -&gt; bytes:
    keccak256 = keccak.new(digest_bits=256)
    keccak256.update(msg)
    return keccak256.digest()


def block_num_to_bytes(num) -&gt; bytes:
    return num.to_bytes(32, byteorder=&quot;big&quot;)


def xor(a: bytes, b: bytes) -&gt; bytes:
    return bytes(x ^ y for x, y in zip(a, b))


def verify_kip114_header(header, prevHeader) -&gt; bool:
    if not is_kip114_fork_enabled(header.number):
        return True

    # pop verify
    blsPublicKeyInfo = BLSRegistry().getInfo(header.proposer)
    publicKey = G1Element.from_bytes(blsPublicKeyInfo.publicKey)
    pop = G2Element.from_bytes(blsPublicKeyInfo.pop)
    if not PopSchemeMPL.pop_verify(publicKey, pop):
        return False

    # signature verify
    msg = block_num_to_bytes(header.number)
    sig = G2Element.from_bytes(header.randomReveal)
    if not PopSchemeMPL.verify(publicKey, msg, sig):
        return False

    # mixHash verify
    if header.number == FORK_BLOCK:
        # prevHeader mixHash does not exist, so fill with default
        prevMixHash = DEFAULT_MIXHASH
    else:
        prevMixHash = prevHeader.mixHash
    return header.mixHash == calc_mix_hash(prevMixHash, header.randomReveal)


def main():
    header = Header(FORK_BLOCK - 2)
    prevHeader = header
    N = 10
    # print header numbers in [FORK_BLOCK - 1, FORK_BLOCK + N]
    for _ in range(N + 2):
        header = gen_next_header(header)
        print(header)
        verified = verify_kip114_header(header, prevHeader)
        assert verified
        print(&quot;verified:&quot;, verified)
        print(&quot;=&quot; * 80)
        prevHeader = header


main()
```

## Test Cases

```
Proposer secret key:   0x6c605527c8e4f31c959478801d51384d690a22dfc6438604646f7709032c893a
Previous MixHash:      0x8019df1a2a9f833dc7f400a15b33e54a5c80295165c5953dc23891aab9203810
Block number:          31337
Expected Msg:          0x0000000000000000000000000000000000000000000000000000000000007a69
Expected RandomReveal: 0xadfe25ced45819332cbf088f01cdd2807686dd6309b11d7440237dd623624f401d4753747f5fb92374235e997edcd18318bae2806a1675b1e685e792abd1fbdf5c50ec1e148cc7fe861984d8bc3204c1b2136725b
Expected MixHash:      0x8772d58248bdf34e81ecbf36f28299cfa758b61ccf3f64e1dc0646687a55892f
```

Testing the Python simulation above:

```
sk = PrivateKey.from_bytes(unhexlify(&quot;6c605527c8e4f31c959478801d51384d690a22dfc6438604646f7709032c893a&quot;))
prevMix = unhexlify(&quot;8019df1a2a9f833dc7f400a15b33e54a5c80295165c5953dc23891aab9203810&quot;)
num = 31337

msg = block_num_to_bytes(num)
reveal = calc_random_reveal(sk, num)
mix = calc_mix_hash(prevMix, reveal)
assert hexlify(msg) == b&quot;0000000000000000000000000000000000000000000000000000000000007a69&quot;
assert hexlify(reveal) == b&quot;adfe25ced45819332cbf088f01cdd2807686dd6309b11d7440237dd623624f401d4753747f5fb92374235e997edcd18318bae2806a1675b1e685e792abd1fbdf5c50ec1e148cc7fe861984d8bc3204c1b2136725b
assert hexlify(mix) == b&quot;8772d58248bdf34e81ecbf36f28299cfa758b61ccf3f64e1dc0646687a55892f&quot;
```

## References

- [consensus-specs/beacon-chain.md at dev · ethereum/consensus-specs](https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#randao)
- [EIP-4399: Supplant DIFFICULTY opcode with PREVRANDAO](https://eips.ethereum.org/EIPS/eip-4399)

## Copyright

Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
</description>
        <pubDate>Wed, 31 May 2023 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-114</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-114</guid>
      </item>
    
      <item>
        <title>Unpredictable Proposer Selection</title>
        <category>Core</category>
        
          <comments>https://github.com/klaytn/kips/issues/146</comments>
        
        <description>## Simple Summary

An unpredictable and verifiable proposer selection policy

## Abstract

This standard outlines an approach for an unpredictable proposer selection policy. A proposer is determined every block in a random manner. The randomness relies on [KIP-114](https://github.com/klaytn/kips/blob/main/KIPs/kip-114.md), thereby rendering the new policy both unpredictable and verifiable.

## Motivation

Klaytn has adopted the Byzantine Fault Tolerance (BFT) consensus mechanism, where a block proposer selected by a deterministic algorithm proposes the next block. A vulnerable point of BFT is that an adversary can effectively halt the entire network by simply targeting the proposer, a.k.a. targeted DoS. What makes Klaytn even more vulnerable to a targeted DoS attack is that the current proposer selection policy enables the prediction of up to `proposerUpdateInterval` (3600 in case of Cypress) number of proposers in advance.

As of now, a targeted DoS is practically infeasible in Klaytn because it is a permissioned chain which allows only authorized validators to join the network. However, in a permissionless network, a targeted DoS is a viable option for attackers. A random proposer selection can increase the difficulty of the attack by introducing an uncertainty of the upcoming proposers.

Meanwhile, a few malicious validators must not be able to forge the randomness to arbitrarily determine the upcoming proposers. If not, it may increase the risk of censorship or monopolizing block rewards. Therefore, the new random proposer selection policy should be both unpredictable and verifiable.

## Specification

The key words &quot;MUST&quot;, &quot;MUST NOT&quot;, &quot;REQUIRED&quot;, &quot;SHALL&quot;, &quot;SHALL NOT&quot;, &quot;SHOULD&quot;, &quot;SHOULD NOT&quot;, &quot;RECOMMENDED&quot;, &quot;MAY&quot;, and &quot;OPTIONAL&quot; in this document are to be interpreted as described in RFC 2119.

### Parameters

| Constant     | Value |
| ------------ | ----- |
| `FORK_BLOCK` | TBD   |

### Shuffling

A shuffling algorithm must be defined which will act as a building block of proposer and committee selection.

[Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle) must be used in the below pseudocode (i.e., `random.shuffle`), and [Golang&apos;s RNG](https://cs.opensource.google/go/go/+/master:src/math/rand/rng.go;l=7-12) must be used as a pseudo random number generator internally in Fisher-Yates.

The shuffling function must be deterministic given `validators` and `seed`.

```py
def shuffle_validators_KIP146(validators: list[int], seed: int):
    &quot;&quot;&quot;returns a list of randomly shuffled validators.
    Keyword arguments:
    validators -- deterministically sorted list of validators.
    seed -- `mixHash[0:8]` interpreted as the bytes of a big-endian signed 64-bit integer. The `mixHash` of the previous block is used (see KIP-114).
    &quot;&quot;&quot;
    ret = validators[:]

    random.seed(seed)
    random.shuffle(ret)
    return ret
```

### Proposer selection

For calculating the proposer of `FORK_BLOCK` and afterward, the new logic must be used.
This only affects the chain whose proposer selection policy is set to `WeightedRandom`.

Below algorithm ensures that

- The proposer must be randomly selected each block.
- The proposer must change in case of round change.
- The proposer must be selected from committee.

```py
def proposer_selector(validators: list[int], committee_size: int, round: int, seed: int):
    &quot;&quot;&quot;returns a proposer from validators.

    Keyword arguments:
    validators -- deterministically sorted list of validators.
    committee_size -- the value of `istanbul.sub` in ChainConfig.
    round -- the target consensus round.
    seed -- `mixHash[0:8]` interpreted as the bytes of a big-endian signed 64-bit integer. The `mixHash` of the previous block is used (see KIP-114).
    &quot;&quot;&quot;
    return select_committee_KIP146(validators, committee_size, seed)[round % len(validators)]
```

### Committee selection

For calculating the committee of `FORK_BLOCK` and afterward, the new logic must be used.
This only affects the chain whose proposer selection policy is set to `WeightedRandom`.

This design guarantees that the given proposer address is always in the committee.

Below algorithm ensures that

- The committee must be randomly selected each block.
- The committee must _not_ change in case of round change.
- The next round proposer must be in the committee.

```py
def select_committee_KIP146(validators: list[int], committee_size: int, seed: int):
    &quot;&quot;&quot;returns a committee from validators.

    Keyword arguments:
    validators -- deterministically sorted list of validators.
    committee_size -- the value of `istanbul.sub` in ChainConfig.
    seed -- `mixHash[0:8]` interpreted as the bytes of a big-endian signed 64-bit integer. The `mixHash` of the previous block is used (see KIP-114).
    &quot;&quot;&quot;
    shuffled = shuffle_validators_KIP146(validators, seed)
    return shuffled[:min(committee_size, len(validators))]
```

## Rationale

### Using part of `mixHash` as a seed

As defined in [KIP-114](https://github.com/klaytn/kips/blob/main/KIPs/kip-114.md), `mixHash` is the result of XOR operation with a keccak256 hash.
To the best of our knowledge, truncating the cryptographic hash is safe to be used for the seed of pseudo random number generator.
Ethereum also uses the first eight bytes of a hash: [link](https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#compute_shuffled_index).

### Committee remaining the same in case of round change

Unlike the previous semantic of `WeightedRandom` proposer selection policy, this proposal suggests a policy where the members of the committee remains the same in case of round change.
This new policy addresses a potential security issue where a fork could happen even within the number of Byzantine nodes permitted by Istanbul Byzantine Fault Tolerance mechanism (i.e., one third of validators).

### Side effect

#### Differentiation of next round proposer and next block proposer

The current algorithm for composing a committee makes sure that the proposer and the next round proposer is included in the committee. Before `FORK_BLOCK`, the next round proposer is equal to the next block proposer, and thus the next block proposer always ended up in the committee. However, after `FORK_BLOCK`, the next round proposer can be different from the next block proposer, which may result in the next block proposer not being included from the committee. This side effect does not degrade the network security; while the next proposer being in the committee could potentially be helpful to next block generation, its significance is negligible, if any.

#### Affecting the predictability of `mixHash`

Allowing a certain validator to be selected as the proposer for consecutive blocks affects the predictability of `mixHash` in KIP-114. See [here](https://github.com/klaytn/kips/blob/main/KIPs/kip-114.md#predictability) for further details.

## Backwards Compatibility

The proposer selection policy before `FORK_BLOCK` remains the same.

## Security Considerations

### Biasability

The biasability of this proposal relies on that of KIP-114, the shuffling algorithm, and the pseudo random number generator.

- KIP-114 is unbiased because keccak256 hash is.
- The shuffling algorithm Fisher-Yates is unbiased as long as the pseudo random number generator is.
- To the best of our knowledge, the Golang&apos;s pseudo random number generator is unbiased; it uses [Additive Lagged Fibonacci Generator](https://en.wikipedia.org/wiki/Lagged_Fibonacci_generator) where initial values are generated by a [Linear Congruential Generator](https://en.wikipedia.org/wiki/Linear_congruential_generator) based on the user seed.

### Predictability

The probability of predicting a proposer at a certain block is the maximum of the followings:

- Predictability of `mixHash` as specified [here](https://github.com/klaytn/kips/blob/main/KIPs/kip-114.md#predictability).
- Randomly picking one out of validators.

It implies that the probability has a lower bound of `1/N`, where `N` is the number of validators.

## Implementation

A reference implementation in Golang: [link](https://github.com/ian0371/klaytn/tree/fork/proposer-selection-3).

Note that Golang&apos;s standard package [rand.shuffle](https://cs.opensource.google/go/go/+/master:src/math/rand/rand.go;l=252) implements Fisher-Yates.

## References

- [Eth2 beacon chain docs](https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md)
- [Eth2 book](https://eth2book.info/capella/part2/building_blocks/shuffling/)
- [Attacking Go&apos;s Lagged Fibonacci Generator](https://www.leviathansecurity.com/media/attacking-gos-lagged-fibonacci-generator)
- [KIP-114](https://github.com/klaytn/kips/blob/main/KIPs/kip-114.md)

## Copyright

Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
</description>
        <pubDate>Thu, 15 Jun 2023 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-146</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-146</guid>
      </item>
    
      <item>
        <title>Unified System Contract Management Process</title>
        <category>Core</category>
        
          <comments>https://github.com/klaytn/kips/issues/149</comments>
        
        <description>## Simple Summary

A unified deployment and management process for system contracts.

## Abstract

This standard defines a unified deployment and management process for system contracts. To effectively manage system contracts, it also introduces a Registry contract that manages all system contracts.

## Motivation

Currently, Klaytn has multiple [system contracts](#definitions), but they are deployed and managed without any defined standards. For example, the `AddressBook` contract was deployed by a bytecode injection at the genesis block with a reserved address. In contrast, an EOA deployed `TreasuryRebalance`, and its address is set in the chain config. As more system contracts will be deployed in the future, it’s essential to have a standard way to deploy and manage system contracts.

## Specification

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.

If a system contract implements a proxy pattern for upgradeability, it must follow the separation of data and logic contracts as defined in this standard. This method, often called the proxy pattern, allows the change of the logic contract while keeping the data, which can greatly reduce the cost of contract updates. Upgrading a logic contract will not affect the Registry since the Registry only holds the address of the proxy(data) contract. Delegating ownership of a proxy contract to a governance contract can solve the centralized and potential private key loss issues.

For system contracts that implement the proxy pattern, they must be developed and validated based on the [`UUPS`](https://eips.ethereum.org/EIPS/eip-1822) proxy pattern rather than the `Transparent` pattern.

### Definitions

- System contract: A contract that is read by the Klaytn core protocol or directly affect the protocol. The currently deployed system contracts are as follows: [**AddressBook**](https://github.com/klaytn/klaytn/blob/dev/contracts/contracts/system_contracts/consensus/AddressBook.sol), [**GovParam**](https://github.com/klaytn/klaytn/blob/dev/contracts/contracts/system_contracts/gov/GovParam.sol), **Voting**([KIP-81](https://github.com/klaytn/kips/blob/main/KIPs/kip-81.md)), **TreasuryRebalance**([KIP-103](https://github.com/klaytn/kips/blob/main/KIPs/kip-103.md))

- System contract upgrade: The process of updating an existing logic contract while maintaining a proxy contract. The upgraded logic contract must be compatible with the previous interface and its storage layout.

- System contract replacement: The deployment of a new system contract that is then registered to the Registry using the same name as its predecessor. The new contract effectively deprecates the previous system contract.

### Smart Contracts Overview

The proposed smart contract will be implemented in Solidity and compatible with the Ethereum Virtual Machine (EVM).

The smart contract will have the following features:

1. Registry

    - Register a new system contract with an activation block.

    - Return the state of the system contracts.

2. Proxy

    - Delegate a call to logic contract.

    - Upgrade a logic contract.

#### 1. Registry

The registry must have data for system contracts at a specific block. It will be done by state injection, which injects data into the Registry directly using the `state.SetState`. A reference implementation is introduced in [Implementation](#implementation). Note that it only records system contracts developed based on KIP-149.

#### Interface of Registry

```solidity
pragma solidity ^0.8.0;

abstract contract IRegistry {
    /* ========== VARIABLES ========== */
    /// The following variables are baked in the interface because their storage layout matters in protocol consensus 
    /// when inject initial states (system contracts, owner) of the Registry.
    /// @dev Mapping of system contracts
    mapping(string =&gt; Record[]) public records;

    /// @dev Array of system contract names
    string[] public names;

    /// @dev Owner of contract
    address internal _owner;

    /* ========== TYPES ========== */
    /// @dev Struct of system contracts
    struct Record {
        address addr;
        uint256 activation;
    }

    /* ========== EVENTS ========== */
    /// @dev Emitted when the contract owner is updated by `transferOwnership`.
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    
    /// @dev Emitted when a new system contract is registered.
    event Registered(string name, address indexed addr, uint256 indexed activation);

    /* ========== MUTATORS ========== */
    /// @dev Registers a new system contract.
    function register(string memory name, address addr, uint256 activation) external virtual;
    
    /// @dev Transfers ownership to newOwner.
    function transferOwnership(address newOwner) external virtual;

    /* ========== GETTERS ========== */
    /// @dev Returns an address for active system contracts registered as name if exists.
    ///  It returns a zero address when there&apos;s no active system contract with name.
    function getActiveAddr(string memory name) external virtual view returns (address);
  
    /// @dev Returns all system contracts registered as name.
    function getAllRecords(string memory name) external virtual view returns (Record[] memory);
    
    /// @dev Returns all names of registered system contracts.
    function getAllNames() external virtual view returns (string[] memory);
    
    /// @dev Returns owner of contract.
    function owner() external virtual view returns (address);
}
```

#### Methods

```solidity
function register(string memory name, address addr, uint256 activation)
```

Registers a new system contract. It will be activated at `activation`. It overwrites the predecessor if a predecessor system contract exists and is not yet active. Passing `addr == address(0)` is an implicit deprecation for the `name`, meaning the `name` will no longer be used.

The function validates the following requirements:

- The function caller MUST be an owner address.

- The function MUST revert if a `name` is an empty string.

- The function MUST revert if `activation &lt; block.number`.

The function emits a `Registered` event.

```solidity
function getActiveAddr(string memory name) view returns (address)
```

Returns the address of the active system contract with the `name`. It returns a zero address if there’s no registered system contract with the `name` or the `name` has been deprecated by registering a zero address.

#### 2. Proxy

The implementation of the proxy contract will come from [OZ&apos;s UUPS implementation](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.9.3/contracts/proxy/ERC1967/ERC1967Proxy.sol), which follows [EIP-1967](https://eips.ethereum.org/EIPS/eip-1967) and [EIP-1822](https://eips.ethereum.org/EIPS/eip-1822). 

### System Contracts Life Cycle

The Registry contract manages system contracts based on the current block number. Its state will be managed implicitly, which means there’s no explicit state variable(e.g., enum State). It has three implicit states and cannot be reversed to the previous state:

- Registered: It has been registered but has not been activated yet.

- Active: The registered address isn&apos;t a zero address and current block number exceeds its activation. Also, no active successor system contract exists.

- Deprecated: It&apos;s registered with a zero address or there’s an active successor system contract.

![](/assets/kip-149/LifeCycle.png)

#### Upgrade System Contracts

When upgrading a system contract, its logic contract will be changed by governance proposal. The Registry will not be updated since it only manages the address of the proxy contract.

![](/assets/kip-149/UpgradeProcess.png)

#### Replace System Contracts

If current system contract updates can’t be done by upgrading the logic contract (e.g., not compatible storage layout or immutable contract), it must be replaced with the newly deployed system contract. The predecessor doesn’t need to be explicitly deprecated since a new system contract will implicitly replace and deprecate it. The replacement process is the same as the initial registration for the system contract except for having a predecessor.

![](/assets/kip-149/ReplacementProcess.png)

### Core Logic Overview

After a target block number, a Klaytn node should read all the active addresses of system contracts through the Registry. A Klaytn node deploys the Registry at the configured block number at the reserved address by bytecode injection. Note that the Registry will be replaced by bytecode injection if necessary since it&apos;s deployed by bytecode injection.

#### Chain Configuration

In the Chain Config, the following fields are newly introduced. All node operators in a network must update `genesis.json` configuration with the same value. The configuration values for Baobab and Cypress networks are hard-coded on the client source code.

- `RegistryAddress`: the reserved address for the Registry contract, which is `0x0000000000000000000000000000000000000401`.

- `Kip149CompatibleBlock`: the target block number that the Registry will be deployed.

- `RegistryInit`: the initial data config for the Registry. Tye type of this field is defined as `RegistryConfig` as shown below. It is used when injecting the initial state after deploying the Registry contract.
  
```go
// In klaytn/params/config.go
type RegistryConfig struct {
	Records  map[string]common.Address // Map for system contracts
	Owner   common.Address // Address for initial owner of Registry
}

var ChainConfig = &amp;ChainConfig{
    ...
    Kip149CompatibleBlock: TBD,
    // RegistryInit will be used when injecting initial state for the Registry
    // Note that the registry only records the system contracts based on the KIP-149, which is currently only KIP-113
    // The activation block deployed by state injection will be 0
    RegistryInit: &amp;RegistryConfig{
		Records: map[string]common.Address{
			&quot;KIP113&quot;: Kip113Address,
		},
		Owner: OwnerAddress,
    },
}
```


#### Execution

The Registry deployment is executed at the `engine.Finalize` function, which means the end of the block generation. It reads the reserved address and runtime bytecode and deploys the Registry by the bytecode injection. Also, it injects the initial state provided by `RegistryConfig` for the Registry here. When `kip149CompatibleBlock == 0`, the Registry will be allocated at the genesis block.

```go
if chain.Config().IsKIP149ForkBlock(header.Number) {
	// Inject the bytecode and states for the Registry
	err := registry.InstallRegistry(state, chain.Config().RegistryInit)
	if err != nil {
		logger.Error(&quot;failed to set the registry contract code&quot;, &quot;err&quot;, err)
	} else {
		logger.Info(&quot;successfully set the registry contract code&quot;, &quot;block&quot;, header.Number.Uint64())
	}
}
```

#### Resolver

A Klaytn node will have a resolver to read the active addresses of system contracts from the Registry. But the system contracts deployed by state injection (e.g., `KIP113`) will be directly read from `chain.Config().RegistryInit` at the `Kip149CompatibleBlock` since the Registry will be deployed in the `engine.Finalize` function, which is the last part of the block generation. In other words, the resolver will be used starting in the `Kip149CompatibleBlock + 1`.

```go
// Note that it will be activated in the next of KIP-149 fork block
func ReadActiveAddressFromRegistry(backend bind.ContractCaller, name string, num *big.Int) (common.Address, error) {
	code, err := backend.CodeAt(context.Background(), RegistryAddr, num)
	if err != nil {
		return common.Address{}, err
	}
	if code == nil {
		return common.Address{}, ErrRegistryNotInstalled
	}

	caller, err := contracts.NewRegistryCaller(RegistryAddr, backend)
	if err != nil {
		return common.Address{}, err
	}

	opts := &amp;bind.CallOpts{BlockNumber: num}
	return caller.GetActiveAddr(opts, name)
}
```

#### JSON-RPC APIs

The following JSON-RPC methods for the Klaytn node should be added to provide the records of registered system contracts.

1. `klay_getActiveAddressFromRegistry`

    - Parameters:
        - `name`: the name of the system contract in string.
        - `number`: (optional) integer or hexadecimal block number, or the string &quot;pending&quot; or &quot;latest&quot;.
    - Description: Returns the active address of the system contract registered as `name` if exists.
    - Return: The address of the active system contract.
    - Example
        ```json
        // Request
        curl -X POST -H &quot;Content-Type: application/json&quot; --data &apos;{&quot;jsonrpc&quot;:&quot;2.0&quot;, &quot;method&quot;:&quot;klay_getActiveAddressFromRegistry&quot;, &quot;params&quot;:[&quot;KIP113&quot;, &quot;latest&quot;],&quot;id&quot;:1}&apos; http://localhost:8551
        // Response
        {
            &quot;jsonrpc&quot;:&quot;2.0&quot;,
            &quot;id&quot;:1,
            &quot;result&quot;: &quot;0x0000000000000000000000000000000000000402&quot;
        }

        // Request - no active system contract
        curl -X POST -H &quot;Content-Type: application/json&quot; --data &apos;{&quot;jsonrpc&quot;:&quot;2.0&quot;, &quot;method&quot;:&quot;klay_getActiveAddressFromRegistry&quot;, &quot;params&quot;:[&quot;KIP114&quot;, &quot;latest&quot;],&quot;id&quot;:1}&apos; http://localhost:8551
        // Response
        {
            &quot;jsonrpc&quot;:&quot;2.0&quot;,
            &quot;id&quot;:1,
            &quot;error&quot;:{
                &quot;code&quot;:-32000,
                &quot;message&quot;:&quot;no active address for KIP114&quot;
            }
        }
        ```
2. `klay_getAllRecordsFromRegistry`

    - Parameters:
        - `name`: the name of the system contract in string.
        - `number`: (optional) integer or hexadecimal block number, or the string &quot;pending&quot; or &quot;latest&quot;.
    - Description: Returns all records of the system contract registered as `name` if it has been registered.
    - Returns:
        - `Record[]`: An array of the records of the system contract.
            - `Record`: A struct of the record with the following fields.
                - `addr`: The address of the system contract.
                - `activation`: The block number when the system contract is activated.
    - Example
        ```
        // Request
        curl -X POST -H &quot;Content-Type: application/json&quot; --data &apos;{&quot;jsonrpc&quot;:&quot;2.0&quot;, &quot;method&quot;:&quot;klay_getAllRecordsFromRegistry&quot;, &quot;params&quot;:[&quot;KIP113&quot;, &quot;latest&quot;],&quot;id&quot;:1}&apos; http://localhost:8551
        // Response
        {
            &quot;jsonrpc&quot;:&quot;2.0&quot;,
            &quot;id&quot;:1,
            &quot;result&quot;:[
                {
                    &quot;addr&quot;:&quot;0x0000000000000000000000000000000000000402&quot;,
                    &quot;activation&quot;:0
                }
            ]
        }

        // request - no records
        curl -X POST -H &quot;Content-Type: application/json&quot; --data &apos;{&quot;jsonrpc&quot;:&quot;2.0&quot;, &quot;method&quot;:&quot;klay_getAllRecordsFromRegistry&quot;, &quot;params&quot;:[&quot;KIP114&quot;, &quot;latest&quot;],&quot;id&quot;:1}&apos; http://localhost:8551
        // Response
        {
            &quot;jsonrpc&quot;:&quot;2.0&quot;,
            &quot;id&quot;:1,
            &quot;error&quot;:{
                &quot;code&quot;:-32000,
                &quot;message&quot;:&quot;KIP114 has not been registered&quot;
            }
        }
        ```

## Rationale

### Bytecode Injection for Registry Deployment

In the future, all system contracts will be registered in the Registry, and a Klaytn node will read the active addresses of system contracts from the Registry. Not only for a Klaytn node but also for other ecosystem participants who will use the registry to read the system contracts they need, meaning the registry should be registered at an easily accessible reserved address.

### Delegating Ownership of Registry and System Contracts to Governance

The Registry holds all system contracts for Klaytn, affecting the protocol directly. This means its registration and deprecation process must be very careful and not centralized. The same goes for the system contract. As mentioned in [specification](#specification), making the EOA the owner of system contracts can cause centralization and potential private key loss issues. By delegating ownership of the Registry and system contracts to Governance, all registry and system contract changes will only be applied after full discussion and consensus of the GCs and Validators. Validators have the right to accept or reject the proposal depending on whether or not the hard fork proceeds.

### State Injection for System Contracts

The Registry must hold data for system contracts at a specific block. To handle this, there are three main approaches:

1. Send multiple register transactions after deploying the Registry.

2. Use a fallback logic in the getter to return the state of system contracts.

3. Use state injection, which injects state for system contracts by the `state.SetState`.

The first approach seems straightforward. However, the Registry will be set in the `engine.Finalize`, which is the last part of the block generation. It means the transaction cannot be processed in the same block and must be entered in the first order of the next block. This requires additional implementation and can&apos;t be applied when `KIP149CompatibleBlock == 0`. In the second approach, the Registry contract should have different codes by the network, requiring direct code modification in the getter(e.g., add/remove hard-coded system contracts and modify if-else statements). It can cause potential security vulnerabilities and increase costs for getter calls permanently. On the other hand, the last approach is much safer because it&apos;s more structured and doesn&apos;t require modifying any code. It can also set the necessary configuration without working with the additional contract&apos;s constructor. Note that the state injection for system contracts will follow the [solidity layout rule](https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html).

### Separate Data and Logic Contract

This proxy pattern simplifies the process of system contract update because the existing data can be used even if the logic contract is changed. The main issue of the proxy pattern is the centralization and potential private key loss. But delegating ownership to upgrade a logic contract to Governance can solve those problems. Choosing `UUPS` as the proxy pattern is because it&apos;s lighter and more secure than `Transparent` proxy pattern. For example, `Transparent` requires additional logic to prevent `proxy selector clashing`. However, with `UUPS`, the proxy functionality is managed in a logic contract, eliminating the need for additional work. For more details, please refer to [OZ&apos;s article](https://docs.openzeppelin.com/contracts/4.x/api/proxy#transparent-vs-uups)

## Backward Compatibility

### Deployed System Contracts

The existing system contracts will not be registered in the Registry, since they are not developed based on KIP-149. They will be used in a Klaytn node same way as before.

## Implementation

- A reference implementation for the Registry contract: [Implementation](https://github.com/klaytn/klaytn/blob/dev/contracts/contracts/system_contracts/kip149/Registry.sol)
- A reference implementation for core logic: [Implementation](https://github.com/klaytn/klaytn/blob/dev/blockchain/system/registry.go)

## References

- Binance Smart Chain: https://github.com/bnb-chain/bsc/tree/master/core/systemcontracts

- Celo: https://docs.celo.org/community/release-process/smart-contracts

## Copyright

Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
</description>
        <pubDate>Wed, 20 Sep 2023 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-149</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-149</guid>
      </item>
    
      <item>
        <title>An Update of Treasury Fund Rebalancing</title>
        <category>Core</category>
        
        <description>## Simple Summary
An update of treasury fund rebalancing.

## Abstract
The treasury fund rebalancing is updated, resulting in updates to the treasury rebalance contract v2 and the related core logic.

## Motivation
According to KIP-103, the treasury rebalancing refers to the act of burning existing fund balances and minting new funds. This event happens at a reserved time, such as a hard fork block number. Through the TreasuryRebalance contract, the disclosure of the treasury fund activities can be conducted transparently and verifiably. However, there are a few shortcomings of KIP-103; (1) it only permits the decrease in the total balance of the funds, (2) its rebalance blocknumber or memo is immutable. Once these values are set, they cannot be changed, even if they are incorrectly set.

To address those above, this proposal made some improvements. First of all, this proposal expands to the general cases so that we don&apos;t have to consider whether the final result is burn or mint. The other improvement is about making rebalanceBlocknumber in the RebalanceContract editable. The rebalanceBlocknumber should be matched with the related hardfork block number.

## Specification
Before proceeding, this proposal will first organize and clarify the terminology. Here, the previous/new fund addresses are referred to as `Zeroed`, `Allocated` unlike in KIP-103. `Retired`, `Newbie` were ambiguous and did not clearly indicate that the previous fund balance is zeroed and the new fund balance is allocated, which degraded the readability.

### To consider both total burn/total mint cases
In KIP-103, rebalancing is limited to cases where the total balance decreases. This proposal aims to generalize the process by removing the requirement that the total balance of `Zeroeds` should exceed the total minting amount of `Allocateds`. Consequently, the checking code is eliminated from the contract and core code.

In the `TreasuryRebalanceV2` contract, the `finalizeApproval` method has been revised as follows. Initially, this `require` statement, `require(getTreasuryAmount() &lt; sumOfZeroedBalance())`, was present. However, it has been removed to accommodate all cases.

```solidity
   /**
    * @dev finalizeApproval sets the status to Approved,
    *      After this stage, approvals will be restricted.
    */
   function finalizeApproval()
        public
        onlyOwner
        onlyAtStatus(Status.Registered)
    {
        checkZeroedsApproved();
        status = Status.Approved;
        emit StatusChanged(status);
    }
```

The next validation has also been removed from the core logic. It previously ensured that the total balance of `Zeroeds` was greater than the total balance of `Allocateds`. However, it has been removed to support all cases.
* totalZeroedAmount &gt;= totalAllocatedAmount

### To enable editing of RebalanceBlocknumber defined in treasury rebalance contract
Within the treasury rebalance contract, there exists a storage value named `RebalanceBlocknumber`, which ideally should correspond to the associated hard fork block number. Despite being able to update the hard fork block number, the `RebalanceBlocknumber` field was immutable. To align the behavior of this field with that of the hard fork block number, this proposal advocates for making the `RebalanceBlocknumber` field editable.

The next method is added to the TreasuryRebalanceV2 contract.
```solidity
    /**
     * @dev updates rebalance block number
     * @param _rebalanceBlockNumber is the updated target block number of the execution the rebalance in Core
     */
    function updateRebalanceBlocknumber(
        uint256 _rebalanceBlockNumber
    ) public onlyOwner {
        require(block.number &lt; rebalanceBlockNumber, &quot;current block shouldn&apos;t be past the currently set block number&quot;);
        require(block.number &lt; _rebalanceBlockNumber, &quot;rebalance blockNumber should be greater than current block&quot;);
        rebalanceBlockNumber = _rebalanceBlockNumber;
    }
```

### To enable editing of Memo defined in treasury rebalance contract
The execution result of treasury fund rebalancing V2 is printed as an INFO-level log on each node. This log is added as a memo to the contract, making it permanently viewable. 
However, once finalized, any modifications to the memo value were restricted even if set incorrectly.
This proposal aims to seperate the memo setting process from the contract finalization, allowing the memo value to be set repeatedly.

The `setPendingMemo` is added and `finalizeContract` of the TreasuryRebalanceV2 contract is revised as follows.

```solidity
    string public pendingMemo; // temporary storage for memo
    string public memo; // result of the treasury fund rebalance

    /**
     * @dev sets the pendingMemo of the Contract. Once finalized, the memo cannot be modified.
     * @param _memo is the result of the rebalance after executing successfully in the core.
     */
    function setPendingMemo(string memory _memo) external onlyOwner onlyAtStatus(Status.Approved) {
        pendingMemo = _memo;
    }

    /**
     * @dev sets the status of the contract to Finalize. Once finalized the storage data
     * of the contract cannot be modified. Also it sets the memo with pendingMemo value.
     * It will be used for public disclosure.
     * Can only be called by the current owner at Approved state after the execution of rebalance in the core
     */
    function finalizeContract() external onlyOwner onlyAtStatus(Status.Approved) {
        require(block.number &gt; rebalanceBlockNumber, &quot;Contract can only finalize after executing rebalancing&quot;);
        require(bytes(pendingMemo).length &gt; 0, &quot;no pending memo, cannot finalize without memo&quot;);

        memo = pendingMemo;
        status = Status.Finalized;
        emit Finalized(memo, status);
    }
```

### Result
In the Kip-103 memo format, the `zeroed` item showed the balance of the zereods before rebalancing, while the `allocated` item showed the balance of the allocateds after rebalancing. 
It was insufficient for interpreting the rebalancing results. Therefore, this proposal changes the format to display balances both before and after rebalancing. 
The newly proposed memo format is shown below with balance/amount in kei (peb of Klaytn).

**Format**

```
{
  &quot;before&quot;: { 
    &quot;zeroed&quot;: { &quot;0xZeroedAddress1&quot;: [balance of zeroedAddress1 before rebalance],  &quot;0xZeroedAddress2&quot;: [balance of zeroedAddress2 before rebalance], ...},
    &quot;allocated&quot;: {  &quot;0xAllocatedAddress1&quot;: [balance of AllocatedAddress1 before rebalance],  &quot;0xAllocatedAddress2&quot;: [balance of AllocatedAddress1 before rebalance], ...}
  },
  &quot;after&quot;: {  
    zeroed&quot;: {  &quot;0xZeroedAddress1&quot;: [balance of zeroedAddress1 after rebalance], &quot;0xZeroedAddress2&quot;: [balance of zeroedAddress2 after rebalance], ...},
    &quot;allocated&quot;: { &quot;0xAllocatedAddress1&quot;: [balance of AllocatedAddress1 after rebalance],  &quot;0xAllocatedAddress2&quot;: [balance of AllocatedAddress1 after rebalance], ...}
  },
  &quot;burnt&quot;: [burnt amount],
  &quot;success&quot;: [true/false]
}
```

## Rationale
While executing the core logic of the KIP-103 treasury rebalance, the unintended increase in the nonce of the &apos;0x0&apos; address occured. In KIP-160, this issue can be resolved by replacing the usage of `kip103ContractBackend` with `BlockchainContractBackend`. For your reference, `ContractBackend` facilitates the interaction between the core and contracts.

The following pseudocode illustrates how to define a contract callers for each rebalancing.
```golang
RebalanceTreasury() {
	if current block number is equal to KIP-160 hard fork block number {
		// Define the caller for the NewTreasuryRebalanceV2 contract 
		caller, err = rebalance.NewTreasuryRebalanceV2Caller(chain.Config().Kip160ContractAddress, backends.NewBlockchainContractBackend(chain, nil, nil))
	}
	if current block number is equal to KIP-103 hard fork block number {
		// Define the caller for the NewTreasuryRebalance contract 
		caller, err = rebalance.NewTreasuryRebalanceCaller(chain.Config().Kip103ContractAddress, &amp;Kip103ContractCaller{state: state, chain: chain, header: header},
	}
	if err != nil {
		stop
	}
}
```
## Test cases
TBD

## Reference
TBD</description>
        <pubDate>Mon, 22 Apr 2024 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-160</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-160</guid>
      </item>
    
      <item>
        <title>Priority Fee Mechanism</title>
        <category>Core</category>
        
          <comments>https://github.com/klaytn/kips/issues/161</comments>
        
        <description># Simple summary

Priority fee mechanism for transaction type 2 (EthereumDynamicFee) in terms of transaction ordering.

# Abstract

Activate the priority fee mechanism as defined in the EIP-1559. The maxPriorityFeePerGas field of transaction type 2 is no longer a placeholder, now it represents the priority fee the sender is willing to pay. The transactions are ordered by descending effective gas price, which is usually in the descending priority fee order. Under congested network, high priority transactions can be included to block by declaring high priority fee.

# Motivation

In Klaytn, the KIP-71 dynamic base fee mechanism and the FCFS (first come first serve) policy had been introduced to alleviate the network stability and usability issue caused by transaction bursts. However, if there are enough transactions that are willing to pay the upper bound base fee (e.g. 750 ston) then the network traffic will stay contested. Under such a condition, senders may experience transaction delay even if they pay the highest fee and send the transactions as soon as possible.

Raising the base fee upper bound is undesirable solution because the users no longer be able to predict or budget their gas fee spendings. Instead, this proposal adds a reliable method for urgent transactions to be included in blocks.

# Specification

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in [RFC-2119](https://www.ietf.org/rfc/rfc2119.txt).

## Block processing

### Configuration

The KIP-162 shall be activated since the `DRAGON_FORK_BLOCK_NUMBER`.

| config | value |
|-|-|
| `MAGMA_FORK_BLOCK_NUMBER` | 98347376 (testnet), 99841497 (mainnet) |
| `DRAGON_FORK_BLOCK_NUMBER` | TBD |

### Effective gas price and effective priority fee per gas

The effective gas price and effective priorirty fee per gas of a transaction at given block are calculated as follows. The effective gas price is the actual price of gas fee paid by the sender. The calculation differs by whether the transaction type is 2 (EIP-1559 EthereumDynamicFee) or not.

```py
def EffectiveGasPrice(header: Header, tx: Transaction):
    if header.number &gt;= DRAGON_FORK_BLOCK_NUMBER:
      # Transaction type 2 has {maxFeePerGas, maxPriorityFeePerGas} fields
      # Other transactions only have {gasPrice} field
      if tx.type == 2:
          maxFeePerGas = tx.maxFeePerGas
          maxPriorityFeePerGas = tx.maxPriorityFeePerGas
      else:
          # makes EffectiveGasPrice() = tx.gasPrice
          maxFeePerGas = tx.gasPrice
          maxPriorityFeePerGas = tx.gasPrice
      return min(header.baseFeePerGas + maxPriorityFeePerGas, maxFeePerGas)

    elif header.number &gt;= MAGMA_FORK_BLOCK_NUMBER:
      return header.baseFeePerGas

    else:
      # Note: before Magma hardfork, transactions must satisfy
      # tx.gasPrice == config.UnitPrice or tx.maxFeePerGas == config.UnitPrice.
      # Therefore EffectiveGasPrice is essentially config.UnitPrice.
      if tx.type == 2:
          return tx.maxFeePerGas
      else:
          return tx.gasPrice

def EffectivePriorityFeePerGas(header: Header, tx: Transaction):
    if header.number &gt;= DRAGON_FORK_BLOCK_NUMBER:
        effectiveGasPrice = EffectiveGasPrice(header, tx)
        return effectiveGasPrice - header.baseFeePerGas
    else:
        return 0
```

### KIP-82 reward scheme

The [KIP-82](https://github.com/klaytn/kips/blob/main/KIPs/kip-82.md) reward scheme is slightly modified. The `get_total_fee` now accounts for effective gas price. Otherwise remains the same.

```py
def get_total_fee(header: Header, txs: list[Transaction], receipts: list[Receipt]):
    # Note: EffectiveGasPrice handles hardfork
    totalFee = 0
    for i in range(len(txs)):
        totalFee += EffectiveGasPrice(header, txs[i]) * receipts[i].gasUsed
    return totalFee
```

### EVM GASPRICE opcode

The `GASPRICE` (0x3a) opcode returns the effective gas price of the currently executing transaction.

## Transaction pool

### Transaction ordering

Block transaction ordering depends on client implementation as the ordering is not checked by block validators. However, under the KIP-82 reward scheme, a proposer should prioritize high priority fee transactions for the maximum proposer reward.

Block proposers are recommended to order transactions with descending effective gas price, and the same effective gas price are sorted by transaction arrival time. This discourages the transaction spamming where a sender sends many transactions hoping that at least one is included in the block. Instead, the sender would pay a higher priority fee to ensure the transaction be included in the block.

### Txpool management

A transaction pool should not accept underpriced transactions. A transaction is underpriced if its `tx.gasPrice` or `tx.maxFeePerGas` field is lower than the baseFeePerGas of the pending block.

## JSON-RPC API

### `eth_gasPrice` and `klay_gasPrice`

Returns suggested value for `gasPrice` or `maxFeePerGas` fields of a transaction.

- Parameters: none
- Result: Recommended gas price in peb. The result depends on the client implementation.
- Example
  ```sh
  curl $RPC_URL -X POST -H &apos;Content-Type: application/json&apos; --data &apos;
    {&quot;jsonrpc&quot;:&quot;2.0&quot;,&quot;id&quot;:1,&quot;method&quot;:&quot;eth_gasPrice&quot;,&quot;params&quot;:[]}&apos;

  {&quot;jsonrpc&quot;:&quot;2.0&quot;,&quot;id&quot;:1,&quot;result&quot;:&quot;0xba43b7400&quot;}
  ```

### `eth_maxPriorityFeePerGas` and `klay_maxPriorityFeePerGas`

Returns suggested value for `gasPrice` or `maxFeePerGas` fields of a transaction.

- Parameters: none
- Result: Recommended priority fee per gas in peb. The result depends on the client implementation.
- Example
  ```sh
  curl $RPC_URL -X POST -H &apos;Content-Type: application/json&apos; --data &apos;
    {&quot;jsonrpc&quot;:&quot;2.0&quot;,&quot;id&quot;:1,&quot;method&quot;:&quot;eth_maxPriorityFeePerGas&quot;,&quot;params&quot;:[]}&apos;

  {&quot;jsonrpc&quot;:&quot;2.0&quot;,&quot;id&quot;:1,&quot;result&quot;:&quot;0x3b9aca00&quot;}
  ```

### `eth_feeHistory` and `klay_feeHistory`

Returns historical gas information for a range of blocks.

- Parameters
  - `blockCount` - Number of blocks in the requested range.
  - `newestBlock` - Highest block of the requested range. Can be a number or &quot;latest&quot;.
  - `rewardPercentiles` - (optional) A monotonically increasing list of percentile (between 0 and 100) values. For each block in the requested range, the transactions will be sorted in ascending order by effective tip per gas and sampled at the specified percentiles.
- Result
  - `oldestBlock` - Lowest number block of returned range.
  - `baseFeePerGas` - An array of block base fee per gas. This includes the next block after the newest of the returned range, because this value can be derived from the newest block. Zeroes are returned for pre-Magma blocks.
  - `gasUsedRatio` - An array of block gas used ratios. Measures the network congestion level. These are calculated as the ratio of block gas used and [KIP-71 MAX_BLOCK_GAS_USED_FOR_BASE_FEE](https://github.com/klaytn/kips/blob/main/KIPs/kip-71.md). If the ratio is above 1, then 1 is returned.
  - `reward` - (optional) A 2D array of effective priority fees per gas. `reward[n][i]` is the `rewardPercentiles[i]`-th percentile effective priority fees per gas among the transactions in the block `oldestBlock + n`. Only returned if `rewardPercentiles` is specified.
- Example
  ```sh
  curl $RPC_URL -X POST -H &apos;Content-Type: application/json&apos; --data &apos;
    {&quot;jsonrpc&quot;:&quot;2.0&quot;,&quot;id&quot;:1,&quot;method&quot;:&quot;eth_feeHistory&quot;,&quot;params&quot;:[
      4,
      &quot;latest&quot;,
      [50.0, 90.0, 95.0]
    ]}&apos;

  {
    &quot;id&quot;: &quot;1&quot;,
    &quot;jsonrpc&quot;: &quot;2.0&quot;,
    &quot;result&quot;: {
      &quot;oldestBlock&quot;: &quot;0x8e0a025&quot;,
      &quot;baseFeePerGas&quot;: [&quot;0x5d21dba00&quot;, &quot;0x5d21dba00&quot;, &quot;0x5d21dba00&quot;, &quot;0x61c9f3680&quot;],
      &quot;gasUsedRatio&quot;: [0.023, 0.31, 1.0, 0.88],
      &quot;reward&quot;: [
        [&quot;0x3b9aca00&quot;, &quot;0x3b9aca00&quot;, &quot;0x9502f900&quot;],
        [&quot;0x3b9aca00&quot;, &quot;0x3b9aca00&quot;, &quot;0x9502f900&quot;],
        [&quot;0x3b9aca00&quot;, &quot;0x9502f900&quot;, &quot;0x2e90edd00&quot;],
        [&quot;0x3b9aca00&quot;, &quot;0x9502f900&quot;, &quot;0x30e4f9b40&quot;],
      ]
    }
  }
  ```

# Rationale

## A KIP-71 parameter is used in place of block gas limit

The Ethereum&apos;s `eth_feeHistory` calculates gasUsedRatio as `block.gasUsed/blockGasLimit`. But Klaytn does not have hard limit of block gas. Instead, Klaytn&apos;s MAX_BLOCK_GAS_USED_FOR_BASE_FEE is analogous to Ethereum&apos;s block gas limit.

In Ethereum, block gas limit is 30,000,000 and the base fee starts to rise when the block gas used exceeds 15,000,000 (`block.gasLimit / ELASTICITY_MULTIPLIER`). Similartly, the Klaytn&apos;s initial KIP-71 parameters stipulates that the block gas used is only accounted until 60,000,000 for the purpose of base fee calculation (`MAX_BLOCK_GAS_USED_FOR_BASE_FEE`), and the base fee starts to rise when the block gas used exceeds 30,000,000 (`GAS_TARGET`).

# Backward compatibility

## Continued use of other transaction types

Legacy (type 0) transactions, EIP-2930 AccessList (type 1) transactions, and Klaytn transaction types (types 8+) will work and be included in blocks. Those transactions have `gasPrice` field but no `maxFeePerGas` nor `maxPriorityFeePerGas` fields. For those transaction types, their effective gas price is considered to equal to the `gasPrice`. The senders pay the full price as declared in the `gasPrice`. This policy is identical to [EIP-1559](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md) and the same as pre-1559 auction market. In this manner, those transaction types pay a nonzero priority fee.

| baseFeePerGas | transaction | effectiveGasPrice | effectivePriorityFeePerGas |
|-|-|-|-|
| 25 | {type: 0, gasPrice: 26} | 26 | 1 |
| 25 | {type: 2, maxFeePerGas: 51, maxPriorityFeePerGas: 1} | 26 | 1 |

## EVM opcodes

The `GASPRICE` (0x3a) opcode is backwards compatible because it had been correctly returning the effective gas price before `DRAGON_FORK_BLOCK_NUMBER`. The `BASEFEE` (0x48) opcode remains the same; returns the base fee per gas of the currently executing block.

# References

- https://github.com/klaytn/kips/blob/main/KIPs/kip-71.md
- https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md
- https://github.com/ethereum/execution-apis/blob/main/src/eth/fee_market.yaml
</description>
        <pubDate>Wed, 20 Mar 2024 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-162</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-162</guid>
      </item>
    
      <item>
        <title>CnStakingV3 with public delegation</title>
        <category>Core</category>
        
          <comments>https://github.com/klaytn/kips/issues/163</comments>
        
        <description>## Simple Summary

Introducing a new CnStakingV3 with public delegation.

## Abstract

The new CnStakingV3 will be compatible with public delegation. The public delegation will support `delegation` and `redelegation` for general users. Also, the staking information interval will be changed to 1 block, which is currently 86,400 blocks.

## Motivation

In KIP-81, Klaytn introduced CnStakingV2. However, it’s difficult for general users to delegate their KLAY if a validator doesn’t provide public delegation services. For example, users can delegate to `SwapScanner` and `KommuneDAO` since they provide their own public delegation services. This narrowed the delegation options to a small number of GC members. In addition, Klaytn expects various GC members to join in the merged chain; there is a strong need to introduce a new CnStakingV3 with public delegation.

The `redelegation` can make massive staking changes in a short period, which can lead to non-negligible errors in the validator set and reward distribution with the current 86,400 blocks interval. To prevent this, the staking information interval will be changed to 1 block.

## Specification

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.

### Overview

CnStakingV3 is basically an upgraded version of CnStakingV2, so it must be compatible with the existing core contracts like `AddressBook`, `StakingTracker` and so on. The notifying changes include the following:

- CnStakingV3 supports public delegation natively, allowing it to receive delegations and re-delegation from general users.

- Re-delegation is supported between CnStakingV3 enabling public delegation and re-delegation.

A GC member can enable public delegation from CnStakingV3. If it is enabled, all staking functionalities must be proxied via a public delegation contract defined in this proposal, namely `PublicDelegation`. When users want to delegate their KLAY, they stake KLAY to `PublicDelegation` and receive `shares` in return, which represent users&apos; assets.

`PublicDelegation` shall be implemented based on an interest-bearing token model, especially ERC-4626. However, `shares` must not be transferable.

From `FORK_BLOCK`, the staking information for block `N` will be come from block `N-1` instead of `CalcStakingBlockNumber(N)`.

### Smart Contracts Overview

This KIP contains upgraded and newly implemented smart contracts. The overall structure is shown in below diagram.

- CnStakingV3: an upgraded version of CnStakingV2. It can enable public delegation and use re-delegation between CNs enable public delegation.

- IKIP163: an interface that a public delegation must implement to be compatible with CnStakingV3.

- PublicDelegation: a public delegation contract provided by Klaytn. Note that it will be only compatible with CnStakingV3.

Henceforth, CnStakingV3 shall be denoted as CNv3, and Public Delegation as PD.

![](/assets/kip-163/overview.png)

#### CnStakingV3

CNv3 can use either initial lockup or PD; the two features must be mutually exclusive. If CNv3 uses initial lockup, there’s no difference from CNv2. Henceforth, all CNv3s are considered to be using PD to focus on the newly added features.

##### Interface

Note that it only addresses changes compared to CNv2. The function `delegate` renames from `stakeKlay` to clarify its meaning.

```solidity
pragma solidity 0.8.25;

interface ICnStakingV3 {
    event SetPublicDelegation(address indexed from, address publicStaking, address rewardAddress);

    event ToggleRedelegation();
    event UpdatePublicStaking(address indexed publicStaking);
    event Redelegation(address indexed targetCnStakingV3, address indexed from, address indexed to, uint256 value);
    event HandleRedelegation(address indexed prevCnStakingV3, address indexed targetCnStakingV3, address indexed recipient, uint256 value);

    // Setup public delegation
    function setPublicDelegation(address _pdFactory, bytes memory _pdArgs) external;

    // Toggle redelegation
    function submitToggleRedelegation() external;
    function toggleRedelegation() external;

    // Delegation/Redelegation functions.
    function delegate() external payable;
    function redelegate(address _targetCnV3, address _from, address _to, uint256 _value) external;
    function handleRedelegation(address _recipient) external payable;

    // Record _account&apos;s last redelegation to prevent redelegation hopping.
    function lastRedelegation(address _account) external view returns (uint256);
}
```

##### Methods

The features added in CNv3 are only active when PD is enabled; without PD, existing features behave exactly as they did in CNv2. If PD is enabled, some staking functions only callable through PD. This ensures that users&apos; assets staked via PD are safe even if control of CNv3 is compromised.

|                                         | PD disabled (Same as CNv2) | PD enabled |
| --------------------------------------- | -------------------------- | ---------- |
| `delegate/receive`                      | No condition               | Only PD    |
| `submitApproveStakingWithdrawal`        | Only Admin                 | N.A.       |
| `submitCancelApprovedStakingWithdrawal` | Only Admin                 | N.A.       |
| `approveStakingWithdrawal`              | Only Admin                 | Only PD    |
| `cancelApprovedStakingWithdrawal`       | Only Multi-sig             | Only PD    |
| `withdrawApprovedStaking`               | Only Multi-sig             | Only PD    |

The CNv3 must call `setPublicDelegation` to set public delegation. In `setPublicDelegation`, it deploys a new public delegation contract and connect to CNv3.

Redelegation can be configured, and it requires that both the originating and the target CNv3 have PD and redelegation enabled to function properly. Users can redelegate their staking to another CNv3 without waiting for lockup period. But to prevent redelegation hopping, which makes Klaytn&apos;s consensus be unstable, users who have been redelegated must wait a lockup period before they can redelegate again. For example, if a user redelegates from [`A` → `B`], then user must wait for a lockup period to redelegate from B to another CNv3. The last redelegation records will be stored in each CNv3s. The below diagram shows how redelegation is processed between CNv3 and PD. The action of PD is discussed in more detail in Public Delegation.

![](/assets/kip-163/redelegation_flow.png)

```solidity
function setPublicDelegation(address _pdFactory, bytes memory _pdArgs);
```

Deploys and sets a PD to CnV3. The `PublicDelegation` will be deployed via `_pdFactory` with `_pdArgs`. The `_pdFactory` is a simple factory contract that deploys `PublicDelegation`, which will be singleton contract deployed by Klaytn. The `_pdArgs` will be encoded `struct PDConstructorArgs` to deploy PD. The reward address must be set to the PD address.

The function validates the following requirement(s):

- The function MUST revert if PD is not enabled.
- The function MUST revert if CnV3 already initialized.
- The function MUST revert if `_pdArgs` isn’t encoded correctly.
- The function MUST revert if PD deployment fails.

The function emits `SetPublicDelegation` event.

```solidity
function redelegate(address _targetCnV3, address _user, uint256 _value);
```

Requests CNv3 to redelegate `_user`’s `_value` of KLAY to `_targetCnV3`. CNv3 calls `handleRedelegation` with `_value` to `_targetCnV3` internally.

The function validates the following requirement(s):

- The function MUST revert if CNv3 does not allow PD, or if the caller is not PD.
- The function MUST revert if CNv3 does not allow redelegation.
- The function MUST revert if `_targetCnV3` is itself.
- The function MUST revert if `_targetCnV3` isn’t a valid CNv3 staking contract registered in AddressBook.
- The function MUST revert if `_value` is larger than total staking amount.
- The function MUST revert if `lastRedelegation[_user] != 0 &amp;&amp; lastRedelegation[_user] + lockup &lt;= block.timestamp`.

The function emits `Redelegation` event.

```solidity
function handleRedelegation(address _user) payable
```

Handles redelegation request from departure CNv3. This CNv3 will stake `msg.value` on behalf of `_user` using `stakeFor(_user)` in PD. It allows existing code to be reused without additional implementation for redelegation. It records `lastRedelegation[_user]` to prevent redelegation hopping.

The function validates the following requirement(s):

- The function MUST revert if CNv3 does not allow PD.
- The function MUST revert if CNv3 does not allow redelegation.
- The function MUST revert if departure CNv3 isn’t a valid CNv3 staking contract registered in AddressBook.
- The function MUST revert if balance after calling `stakeFor` is lower than expected.
  - In `stakeFor`, PD will stake `msg.value` back to CNv3. It means CNv3 can compute deterministic balance changes.

The function emits `HandleRedelegation` event.

#### IKIP163

IKIP163 must be implemented in `PublicDelegation` to be fully compatible with CnStakingV3:

##### Interface

```solidity
pragma solidity 0.8.25;

/// @title KIP-163 Public Delegation Interface
/// @dev See https://github.com/klaytn/kips/issues/163
interface IKIP163 {
    /// @dev Stake KLAY for the _recipient.
    /// It is used in CnStakingV3.handleRedelegation to stake KLAY for the _recipient when handling redelegation.
    /// It must stake KLAY to the CnStakingV3 in the same transaction.
    /// See CnStakingV3.handleRedelegation for more details.
    /// @param _recipient The address to stake for
    function stakeFor(address _recipient) external payable;

    /// @dev Returns the current rewards to be automatically compounded during the `stakeFor` function.
    /// It is used in CnStakingV3.handleRedelegation to calculate the expected balance after calling `stakeFor`.
    /// If implemented public delegation doesn&apos;t support auto-compounding during the `stakeFor`, it should return 0.
    /// See CnStakingV3.handleRedelegation for more details.
    function reward() external view returns (uint256);
}
```

Also, all staking operations in CNv3 must be done through public delegation, so there is no fixed interface, but it should call CNv3 appropriately.

##### Methods

```solidity
function stakeFor(address _recipient) payable;
```

It is used in `CnStakingV3.handleRedelegation` to stake KLAY for the `_recipient` when handling redelegation. The `stakeFor` MUST not revert `handleRedelegation`.

```solidity
function reward() view returns (uint256);
```

It returns the current rewards to be automatically compounded during the `stakeFor` function. It is used in `CnStakingV3.handleRedelegation` to calculate the expected balance after calling `stakeFor` function. If the public delegation doesn’t support auto-compounding during the `stakeFor`, it must return 0, otherwise exact rewards to be compounded. The `reward` MUST not revert `handleRedelegation`.

#### PublicDelegation

PD is a public delegation contract based on ERC4626. It must be set up through `setPublicDelegation` in CNv3. Since PD must receive and calculate block rewards, the reward address of the CNv3 must be set to PD. If validator deploys multiple CNv3s to use both initial lockup and PD, the reward address for all CNv3s must be PD (i.e., the reward for initial lockup is also sent to PD).

When block reward is auto-compounding, the commission is calculated and paid automatically.

Since PD is a type of token contract, its name and symbol will be determined as follows:

- Name: `{gcName} Public Delegated KLAY` (ex. `KF Public Delegated KLAY`)
- Symbol: `{gcName}-pdKLAY` (ex. `KF-pdKLAY`)

##### Interface

```solidity
pragma solidity 0.8.25;

import &quot;../CnV3/ICnStakingV3.sol&quot;;
import &quot;./IKIP163.sol&quot;;

interface IPublicDelegation is IKIP163 {
    /* ========== STRUCT ========== */

    struct PDConstructorArgs {
        address owner;
        address commissionTo;
        uint256 commissionRate;
        string gcName;
    }

    /* ========== ENUM ========== */

    /// @dev Current state of the withdrawal request
    enum WithdrawalRequestState {
        Undefined,
        Requested,
        Withdrawable,
        Withdrawn,
        PendingCancel,
        Canceled
    }

    /* ========== EVENTS ========== */

    // Initialization
    event DeployContract(string _contractType, address _baseCnStakingV3, PDConstructorArgs _pdArgs);

    // Operations
    event UpdateCommissionTo(address indexed _prevCommissionTo, address indexed _commissionTo);
    event UpdateCommissionRate(uint256 indexed _prevCommissionRate, uint256 indexed _commissionRate);
    event SendCommission(address indexed _commissionTo, uint256 _commission);

    // Staking
    event Staked(address indexed _user, uint256 _assets, uint256 _shares);
    event Redeemed(address indexed _user, address indexed _recipient, uint256 _assets, uint256 _shares);
    event Redelegated(address indexed _user, address indexed _targetCnV3, uint256 _assets);
    event RequestWithdrawal(
        address indexed _user,
        address indexed _recipient,
        uint256 indexed _requestId,
        uint256 _assets
    );
    event RequestCancelWithdrawal(address indexed _user, uint256 indexed _requestId);
    event Claimed(address indexed _user, uint256 indexed _requestId);

    /* ========== CONSTANT/IMMUTABLE GETTERS ========== */

    function MAX_COMMISSION_RATE() external pure returns (uint256);

    function COMMISSION_DENOMINATOR() external pure returns (uint256);

    function CONTRACT_TYPE() external pure returns (string memory);

    function VERSION() external pure returns (uint256);

    function baseCnStakingV3() external view returns (ICnStakingV3);

    /* ========== OPERATION FUNCTIONS ========== */

    function updateCommissionTo(address _commissionTo) external;

    function updateCommissionRate(uint256 _commissionRate) external;

    /* ========== PUBLIC FUNCTIONS ========== */

    // Staking
    function stake() external payable;

    function stakeFor(address _recipient) external payable; // Defined in IKIP163

    receive() external payable;

    // Withdrawal
    function withdraw(address _recipient, uint256 _assets) external;

    function redeem(address _recipient, uint256 _shares) external;

    function cancelApprovedStakingWithdrawal(uint256 _requestId) external;

    function claim(uint256 _requestId) external;

    // Redelegation
    function redelegateByAssets(address _targetCnV3, uint256 _assets) external;

    function redelegateByShares(address _targetCnV3, uint256 _shares) external;

    // Sweep
    function sweep() external;

    /* ========== PUBLIC VIEWS ========== */

    function commissionTo() external view returns (address);

    function commissionRate() external view returns (uint256);

    function userRequestIds(address _owner, uint256 _index) external view returns (uint256);

    function requestIdToOwner(uint256 _requestId) external view returns (address);

    function getCurrentWithdrawalRequestState(uint256 _requestId) external view returns (WithdrawalRequestState);

    function getUserRequestCount(address _owner) external view returns (uint256);

    function getUserRequestIdsWithState(
        address _owner,
        WithdrawalRequestState _state
    ) external view returns (uint256[] memory);

    function getUserRequestIds(address _owner) external view returns (uint256[] memory);

    function maxRedeem(address _owner) external view returns (uint256);

    function maxWithdraw(address _owner) external view returns (uint256);

    function reward() external view returns (uint256); // Defined in IKIP163

    function totalAssets() external view returns (uint256);

    function convertToShares(uint256 _assets) external view returns (uint256);

    function convertToAssets(uint256 _shares) external view returns (uint256);

    function previewDeposit(uint256 _assets) external view returns (uint256);

    function previewWithdraw(uint256 _assets) external view returns (uint256);

    function previewRedeem(uint256 _shares) external view returns (uint256);
}
```

##### Constants

The PublicDelegation has constants related to commission.

- `MAX_COMMISSION_RATE`: Max commission rate. It will be 10,000. (= 100%)
- `COMMISSION_DENOMINATOR`: Commission denominator. It will be 10,000.

##### Methods

All math for managing a user&apos;s staking follows ERC4626 standard. Note that PD isn’t based on ERC20 assets, but native KLAY.

- `totalAssets()`: Total assets managed by PD. It contains reward.
- `maxRedeem(address owner)`: Max reedeemable shares of owner.
- `maxWithdraw(address owner)`: Max withdrawable KLAY of owner.
- `convertToAssets(uint256 shares)`: Expected KLAY when convert shares.
- `previewDeposit(uint256 assets)`: Expected shares when deposit assets of KLAY.
- `previewWithdraw(uint256 assets)`: Expected shares to withdraw assets of KLAY.
- `previewRedeem(uint256 shares)`: Expected KLAY when redeem shares.

To enable auto-compounding, PD stakes cumulative KLAY rewards to CNv3 automatically when the below functions are called or call `sweep` function explicitly.

```solidity
function updateCommissionTo(address _commissionTo);
```

It updates commission receiver address of PD. Previously accumulated commission must go to previous commission receiver.

The function validates the following requirement(s):

- The function MUST revert if caller is not an owner.

The function emits `UpdateCommissionTo` event.

```solidity
function updateCommissionRate(uint256 _commissionRate);
```

It updates commission rate of PD. Previously accumulated rewards must follow previous commission rate.

The function validates the following requirement(s):

- The function MUST revert if caller is not an owner.
- The function MUST revert if `_commissionRate` is higher than `MAX_COMMISSION_RATE`.

The function emits `UpdateCommissionRate` event.

```solidity
function stake() payable;
function stakeFor(address _recipient) payable;
```

Call `delegate` with `msg.value` to CNv3 internally. It must mint corresponding shares to user. `stakeFor` is used when `msg.sender != _recipient`.

| User gives | User receives                                                 |
| ---------- | ------------------------------------------------------------- |
| `KLAY`     | `previewDeposit(KLAY)`: Floored amount of `shares` (`pdKLAY`) |

The function validates the following requirement(s):

- The function MUST revert if user would receive 0 shares.
- The function MUST revert if delegate call reverts.

The function emits `Staked` event.

```solidity
function withdraw(address _recipient, uint256 _assets);
function redeem(address _recipient, uint256 _shares)
```

Call `approveStakingWithdrawal` to CNv3 internally. It must burn corresponding shares of user. User will use `withdraw` to withdraw the `exact amount of KLAY`, otherwise use `redeem` to withdraw the `exact amount of shares`. Note that user must use `redeem` to withdraw all the KLAY because total withdrawable KLAY is changed every block (1 second).

|                             | User gives (`pdKLAY`)                                             | User will receive (7-day lockup)                |
| --------------------------- | ----------------------------------------------------------------- | ----------------------------------------------- |
| `withdraw(recipient, KLAY)` | `previewWithdraw(KLAY)`: Ceiled amount of shares to withdraw KLAY | `KLAY`                                          |
| `redeem(recipient, shares)` | `shares`                                                          | `previewRedeem(shares)`: Floored amount of KLAY |

The function validates the following requirement(s):

- The function MUST revert if user requests to withdraw 0 KLAY.
- The function MUST revert if user withdraws more than staked.
- The function MUST revert if `approveStakingWithdrawal` call reverts.

The functions emit `RequestWithdrawal` event.

```solidity
function cancelApprovedStakingWithdrawal(uint256 _requestId);
```

Call `cancelApprovedStakingWithdrawal` to CNv3 internally. User’s shares must be revived accordingly. Note that KLAY in withdrawal requests didn’t receive any rewards.

| User gives | User receives                                                          |
| ---------- | ---------------------------------------------------------------------- |
| -          | `previewDeposit(KLAY in withdrawal request)`: Floored amount of shares |

The function validates the following requirement(s):

- The function MUST revert if a user attempts to withdraw another user’s request.
- The function MUST revert if `cancelApprovedStakingWithdrawal` call reverts.

The function emits `RequestCancelWithdrawal` event.

```solidity
function claim(uint256 _requestId);
```

Calls `withdrawApprovedStaking` to CNv3 internally. If the withdrawal was canceled, it must revive user’s shares accordingly. Note that KLAY in withdrawal requests didn’t receive any rewards.

|               | User gives | User receives                                                          |
| ------------- | ---------- | ---------------------------------------------------------------------- |
| Claim success | -          | `KLAY` in withdrawal request                                           |
| Claim failure | -          | `previewDeposit(KLAY in withdrawal request)`: Floored amount of shares |

The function validates the following requirement(s):

- The function MUST revert if a user attempts to claim another user’s request.
- The function MUST revert if `withdrawApprovedStaking` call reverts.

The function emits `Claimed` if withdrew successfully.

```solidity
function redelegateByAssets(address _targetCnV3, uint256 _assets);
function redelegateByShares(address _targetCnV3, uint256 _shares);
```

Call `redelegate` to CNv3 internally. It must burn corresponding shares of users. User will use `byAssets` to redelegate the `exact amount of KLAY`, otherwise use `byShares` to redelegate the `exact amount of shares`. Note that user must use `byShares` to redelegate all the KLAY same reason as `redeem`. User must receive the corresponding shares by PD connected to targetCNv3. In below tables, $\color{blue}{blue}$ is for target, while $\color{red}{red}$ is from.

|                                      | User gives                                                                       | User receives                                                                                                        |
| ------------------------------------ | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `redelegateByAssets(target, KLAY)`   | $\color{red}{previewWithdraw(KLAY)}$: Ceiled amount of shares to redelegate KLAY | $\color{blue}{previewDeposit(KLAY)}$: Floored amount of shares                                                       |
| `redelegateByShares(target, shares)` | `shares`                                                                         | $\color{blue}{previewDeposit(KLAY)}$: Floored amount of shares, where `KLAY` is $\color{red}{previewRedeem(shares)}$ |

The function validates the following requirement(s):

- The function MUST revert if `_targetCnV3` isn’t a valid CNv3 staking contract.
- The function MUST revert if current CNv3 doesn’t allow redelegation.
- The function MUST revert if a user redelegates more KLAY than staked.
- The function MUST revert if a user redelegates 0 KLAY.
- The function MUST revert if `redelegate` call reverts.

The functions emit `Redelegated` event.

### Core Logic

From `FORK_BLOCK`, the staking information for block `N` will be recorded in block `N-1` instead of `CalcStakingBlockNumber(N)`.

#### Parameters

| Constant     | Value |
| ------------ | ----- |
| `FORK_BLOCK` | TBD   |

```go
// In staking_manager.go
func GetStakingInfo(blockNum uint64) *StakingInfo {
	stakingBlockNumber := blockNum
	var stakingInfo *StakingInfo
	if isForkBlockEnabled(blockNum) {
        // After `FORK_BLOCK`, staking information for block `N` will be recorded in block `N-1`.
	    if blockNum &gt; 0 {
			stakingBlockNumber--
		}
		stakingInfo = GetStakingInfoForForkBlock(stakingBlockNumber)
	} else {
        // Before `FORK_BLOCK`, staking information for block `N` will be recorded in `CalcStakingBlockNumber(N)`.
		stakingBlockNumber = params.CalcStakingBlockNumber(blockNum)
		stakingInfo = GetStakingInfoOnStakingBlock(stakingBlockNumber)
	}
	return stakingInfo
}
```

| Function                                 | Description                                                                                                                                |
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `GetStakingInfo(blockNum)`               | Returns staking information needed to handle block `blockNum`.                                                                             |
| `GetStakingInfoForForkBlock(blockNum)`   | Returns staking information at block `blockNum`. If `blockNum` is before `FORK_BLOCK - 1`, return nil.                                     |
| `GetStakingInfoOnStakingBlock(blockNum)` | Returns staking information at staking block `blockNum`. If `blockNum` is not a staking interval block nor after `FORK_BLOCK`, return nil. |

## Rationale

### Non-transferable shares in PublicDelegation

Originally, the shares minted by PublicDelegation is based on ERC4626, one of ERC20 extensions. It means shares will be utilized as a ERC20 token (e.g., Liquidity Pool, Collaterals). This is of course a common use cases on many blockchains; LST. However, since different LSTs are issued by each GC, which causes significant liquidity fragmentation. Therefore, although `shares` are not transferable in this KIP, Klaytn will keep considering ways to activate staked KLAY while preventing liquidity fragmentation.

### Initial lockup and public delegation are mutually exclusive

All delegations must be through `PublicDelegation` if enabled. However, it conflicts with the existing initial lockup feature from CnStakingV2 because the initial lockup is staked directly into CnStakingV3 without `PublicDelegation`. To solve this issue, public delegation and initial lockup must be set to mutually exclusive. However, if both features must be used, there are alternative ways to achieve it:

1. Deposit initial lockup through `PublicDelegation` and implement additional logic for initial lockup (setup unlock time and amounts)
2. Maintain current initial lockup implementation. In `PublicDelegation`, calculate the shares considering the initial lockup of CnStakingV3.

First approach needs additional implementation in PD only for initial lockup. This raises the question of whether it&apos;s reasonable for PD to consider initial lockup. Developing an independent lockup contract for initial lockup is possible, but this causes additional security and maintenance issues.

The second approach makes PD logic more complicated. PD has to read the state of the initial lockup every time and make calculations, which is a permanent cost increase.

Overall, it’s determined that initial lockup and PD are best used in CNv3 where they are mutually exclusive and independent.

## Backward Compatibility

The existing CNv1 and CNv2 and previous custom public delegation services will still be available. However, it is strongly recommended to deploy CNv3 for new GCs and gradually move to CNv3 for original GCs.

## Security Considerations

### ERC4626 Inflation Attack

This is fundamentally due to that when using ERC20 as a base asset, contract cannot prevent tokens from being deposited directly into it. This only increases the underlying asset without issuing any shares, and can result in zero shares for other users to receive. However, since the PD in this is based on the KLAY, we can control all KLAY inflows by fallback.

## Implementation

TBA

## References

- [ERC4626](https://eips.ethereum.org/EIPS/eip-4626)
- [Inflation Attack](https://blog.openzeppelin.com/a-novel-defense-against-erc4626-inflation-attacks)
- [Cosmos Staking Module](https://docs.cosmos.network/v0.46/modules/staking/)

## Copyright

Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
</description>
        <pubDate>Tue, 30 Apr 2024 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-163</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-163</guid>
      </item>
    
      <item>
        <title>KIP Process Upgrade</title>
        <category>Meta</category>
        
        <description>## What is a KIP?

KIP stands for Kaia Improvement Proposal. A KIP is a design document providing information to the Kaia community, or describing a new feature for Kaia or its processes or environment. The KIP should provide a concise technical specification of the feature and a rationale for the feature. The KIP author is responsible for building consensus within the community via discussion on the developer forum and documenting dissenting opinions.

## KIP Rationale

We intend KIPs to be the primary mechanisms for proposing new standards and features, for collecting community technical input on an issue, and for documenting the design decisions that have gone into Kaia protocol and ecosystem. Because the KIPs are maintained as text files in a versioned repository, their revision history is the historical record of the feature proposal.
For Kaia’s builders and contributors, KIPs are a convenient way to track the progress of their implementation. Ideally each implementation maintainer would list the KIPs that they have implemented. This will give KIP users a convenient way to know the current status of a given implementation and its constituent libraries.

## KIP Types

There are in total four types of KIP:

- A **Core KIP** describes any change that affects Kaia protocol implementations, such as a change to the network protocol, an improvement in block or transaction validity rules, an improvement in storage-layer mechanism, an improvement in interface around client API/RPC specifications and standards, and also certain language-level standards like method names and contract ABIs, any change in Kaia’s tokenomics-related settings and financial parameters, or any change in GC block reward mechanism, etc. Core KIP would be seen requiring a consensus fork as well as changes that are not necessarily consensus critical but may be relevant to any core-related development. Core KIPs consist of two parts, a design document and implementation.
- An **Ecosystem KIP** describes any change falling under the broad category of ecosystem features, tools or environment used in Kaia development, such as a change on application layer related to proposed application standards/conventions, or any change or addition that affects the interoperability of applications using Kaia, or a change in SDK layer related to application-level standards and conventions, such as name registries, URI schemes, library/package formats, and wallet formats, or an introduction of new ecosystem tool that could be integrated to the Kaia chain with implementation know-hows, or an introduction of contract standards that would introduce ecosystem features, etc.
- A **Tokenization KIP** describes any change or introduction related to Kaia compatible tokens, such as an introduction of purpose-driven fungible token or non-fungible token and its minting policies in general.
- A **Meta KIP** describes a process surrounding Kaia or proposes a change to (or an event in) a process. Meta KIPs apply to areas other than the KIPs listed above. Examples include procedures, guidelines, and changes to the decision-making process. Any Meta KIP is also considered a Process KIP.

It is highly recommended that a single KIP contains a single key proposal or new idea. The more focused the KIP, the more successful it tends to be. A change to one client doesn’t require a KIP; a change that affects multiple clients, or defines a standard for multiple apps to use, does.

A KIP must meet certain minimum criteria. It must be a clear and complete description of the proposed enhancement. The enhancement must represent a net improvement. The proposed implementation, if applicable, must be solid and must not complicate the protocol unduly.

### Special requirements for Core KIPs

If a **Core KIP** mentions or proposes changes to the KLVM (Kaia Virtual Machine, forked from Byzantium EVM), it should refer to the instructions by their mnemonics and define the opcodes of those mnemonics at least once. A preferred way is the following:
```
REVERT (0xfe)
```

## KIP Work Flow

### Shepherding a KIP

Parties involved in the process are you, the champion or *KIP author*, the [*KIP reviewers*](#kip-reviewers), and the Kaia dev team.

Before you begin writing a formal KIP, you should vet your idea. Ask the Kaia community first if an idea is original to avoid wasting time on something that will be rejected based on prior research. It is thus recommended to open a discussion thread on [the Issues section of this repository](https://github.com/kaiachain/KIPs/issues) or directly over to [Kaia Dev Forum](https://devforum.kaia.io/c/english/kip/55).

In addition to making sure your idea is original, it will be your role as the author to make your idea clear to reviewers and interested parties, as well as inviting reviewers, developers, and the community to give feedback on the aforementioned channels. You should try and gauge whether the interest in your KIP is commensurate with both the work involved in implementing it and how many parties will have to conform to it. For example, the work required for implementing a Core KIP will be much greater than for others and the KIP will need sufficient interest from the Kaia client teams. Negative community feedback will be taken into consideration and may prevent your KIP from moving past the Draft stage.

### Core KIPs

For Core KIPs, given that they require client implementations to be considered **Final** (see “KIPs Process” below), you will need to either provide an implementation for clients or convince clients to implement your KIP.

In short, your role as the champion is to write the KIP using the style and format described below, shepherd the discussions in the appropriate forums, and build community consensus around the idea.

### KIP Process 

Following is the process that a successful KIP will move along:

```
[ IDEA ] -&gt; [ DRAFT ] -&gt; [ REVIEW ] -&gt; [ LAST CALL ] -&gt; [ ACCEPTED ] -&gt; [ FINAL ]
```

Each status change is requested by the KIP author and reviewed by the KIP reviewers. Use a pull request to update the status. Please include a link to where people should continue discussing your KIP. The KIP reviewers will process these requests as per the conditions below.

* **Idea** -- Once the champion has asked the Kaia community in [Dev Forum](https://devforum.kaia.io/c/english/kip/55) whether an idea has any chance of support, they will write a draft KIP as a [pull request](https://github.com/kaiachain/KIPs/pulls). Consider including an implementation if this will aid people in studying the KIP.
  * :arrow_right: Draft – If agreeable, a KIP reviewer will assign the KIP a number (generally the issue or PR number related to the KIP) and merge your pull request. The KIP reviewer will not unreasonably deny a KIP.
  * :x: Draft -- Reasons for denying draft status include being too unfocused, too broad, duplication of effort, being technically unsound, not providing proper motivation or addressing backwards compatibility.
* **Draft** -- Once the first draft has been merged, you may submit follow-up pull requests with further changes to your draft until such point as you believe the KIP to be mature and ready to proceed to the next status. A KIP in draft status must be implemented to be considered for promotion to the next status.
  * :arrow_right: Review – If agreeable, the KIP reviewer will assign the Review status and officially take the proposal under review.
  * :x: An unsuccessful review would result in material changes or substantial unaddressed technical complaints will cause the KIP to revert to Draft, all would be based on the peer review feedback by KIP Reviewers.
* **Review** -- A KIP Author marks a KIP as ready for and requesting Peer Review by Reviewers.
  * :arrow_right: Last Call – If agreeable, the KIP reviewer will assign the Last Call status and set a review end date (`review-period-end`), normally 14 days later. 
  * :x: -- A request for Last Call status will be denied if material changes are still expected to be made to the draft. We expect that KIPs only enter Last Call once.
* **Last Call** -- This KIP will be listed prominently as a pinned issue.
  * :arrow_right: Accepted – A successful Last Call without material changes or unaddressed technical complaints will become Accepted.
  * :x: -- A Last Call which results in material changes or substantial unaddressed technical complaints will cause the KIP to revert to Draft, all would be based on the peer review feedback by KIP Reviewers.
* **Accepted** -- This status signals that material changes are unlikely and the Kaia dev team should consider this KIP for inclusion. Their process for deciding whether to encode it into their core protocol as part of a hard fork or include the new ecosystem integration/feature is not part of the KIP process.
  * :arrow_right: Draft – The KIP can be decided to move it back to the Draft status at the discretion. E.g. a major, but correctable, flaw was found in the KIP by the reviewer committee.
  * :arrow_right: Rejected – The KIP can be decided to be marked as this KIP as Rejected at their discretion. E.g. a major, but uncorrectable, flaw was found in the KIP.
  * :arrow_right: Final – Standard Track Core KIPs must be implemented in any of Kaia clients before it can be considered Final. When the implementation is complete and adopted by the community, the status will be changed to &quot;Final&quot;.
* **Final** -- This KIP represents the current state-of-the-art. A Final KIP should only be updated to correct errata.

Other exceptional statuses include:

* **Active** -- Some Informational and Process KIPs may also have a status of “Active” if they are never meant to be completed. E.g. KIP-201 (this KIP).
* **Abandoned** -- This KIP is no longer pursued by the original authors or it may not be a (technically) preferred option anymore.
  * :arrow_right: Draft – Authors or new champions wishing to pursue this KIP can ask for changing it to Draft status.
* **Rejected** -- A KIP that is fundamentally broken or a Core KIP that was rejected by the Core Devs and will not be implemented. A KIP cannot move on from this state.
* **Superseded** -- A KIP which was previously Final but is no longer considered state-of-the-art. Another KIP will be in Final status and reference the Superseded KIP. A KIP cannot move on from this state.

## What belongs in a successful KIP?

Each KIP should include the following parts:

- Preamble - RFC 822 style headers containing metadata about the KIP, including the KIP number, a short descriptive title (limited to a maximum of 44 characters), and the author details. See [below](#kip-header-preamble) for details.
- Abstract - A short (~200 word) description of the technical issue being addressed.
- Motivation: Why is this KIP necessary? - The motivation is critical for KIPs that want to change the Kaia protocol. It should clearly explain why the existing protocol specification is inadequate to address the problem that the KIP solves. KIP submissions without sufficient motivation will be rejected outright.
- Specification - The technical specification should describe the syntax and semantics of any new feature. The specification should be detailed enough to allow competing, interoperable implementations for any of the current Kaia platforms.
- Rationale: How does this KIP achieve its goals? - The rationale fleshes out the specification by describing what motivated the design and why particular design decisions were made. It should describe alternate designs that were considered and related work, e.g. how the feature is supported in other languages. The rationale may also provide evidence of consensus within the community, and should discuss important objections or concerns raised during discussion.
- Backwards Compatibility (if applicable) - All KIPs that introduce backwards incompatibilities must include a section describing these incompatibilities and their severity. The KIP must explain how the author proposes to deal with these incompatibilities. KIP submissions without a sufficient backwards compatibility treatise may be rejected outright.
- Test Cases (if applicable) - Test cases for an implementation are mandatory for KIPs that are affecting consensus changes. Other KIPs can choose to include links to test cases if applicable.
- Implementations - The implementations must be completed before any KIP is given status “Final”, but it need not be completed before the KIP is merged as draft. While there is merit to the approach of reaching consensus on the specification and rationale before writing code, the principle of “rough consensus and running code” is still useful when it comes to resolving many discussions of API details.
- Optional Sections (if applicable) - May appear in any order, or with custom titles, at author and reviewer discretion:
  - Versioning: if Versioning is not addressed in Specification
  - Appendices
  - References
- Copyright Waiver - All KIPs must be in the public domain. See the bottom of this KIP for an example copyright waiver.

## KIP Formats and Templates

KIPs should be written in [markdown](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet) format. Image files should be included in a subdirectory of the `assets` folder for that KIP as follows: `assets/kip-N` (where **N** is to be replaced with the KIP number). When linking to an image in the KIP, use relative links such as `../assets/kip-201/image.png`.

## KIP Header Preamble

Each KIP must begin with an [RFC 822](https://www.ietf.org/rfc/rfc822.txt) style header preamble, preceded and followed by three hyphens (`---`). This header is also termed [&quot;front matter&quot; by Jekyll](https://jekyllrb.com/docs/front-matter/). The headers must appear in the following order. Headers marked with “*” are optional and are described below. All other headers are required.

` kip:` *KIP number* (this is determined by the KIP editor)

` title:` *KIP title*

` author:` *a list of the author&apos;s or authors&apos; name(s) and/or username(s), or name(s) and email(s). Details are below.*

` * discussions-to:` *an url pointing to the official discussion thread*

` status:` *Draft | Review | Last Call | Accepted | Final | Active | Abandoned | Rejected | Superseded*

` * review-period-end:` *date review period ends*

` type:` *Core | Ecosystem | Tokenization | Meta*

` created:` *date created on*

` * updated:` *comma separated list of dates*

` * requires:` *KIP number(s)*

` * replaces:` *KIP number(s)*

` * superseded-by:` *KIP number(s)*

` * resolution:` *an url pointing to the resolution of this KIP*

Headers that permit lists must separate elements with commas.

Headers requiring dates will always do so in the format of ISO 8601 (yyyy-mm-dd).

#### `author` header

The `author` header optionally lists the names, email addresses or usernames of the authors/owners of the KIP. Those who prefer anonymity may use a username only, or a first name and a username. The format of the author header value must be:

&gt; Kaia A. User &amp;lt;address@kaia.io&amp;gt;

or

&gt; Kaia A. User (@username)

if the email address or GitHub username is included, and

&gt; Kaia A. User

if the email address is not given.

#### `resolution` header

The `resolution` header is required for Standards Track KIPs only. It contains a URL that should point to an email message or other web resource where the pronouncement about the KIP is made.

#### `discussions-to` header

While a KIP is a draft, a `discussions-to` header will indicate the mailing list or URL where the KIP is being discussed. As mentioned above, examples for places to discuss your KIP include an issue in this repo or in a fork of this repo.

No `discussions-to` header is necessary if the KIP is being discussed privately with the author.

As a single exception, `discussions-to` cannot point to GitHub pull requests.

#### `type` header

The `type` header specifies the type of KIP: Core, Ecosystem, Tokenization and Meta.

#### `created` header

The `created` header records the date that the KIP was assigned a number. Both headers should be in yyyy-mm-dd format, e.g. 2024-12-3.

#### `updated` header

The `updated` header records the date(s) when the KIP was updated with &quot;substantial&quot; changes. This header is only valid for KIPs of Draft and Active status.

#### `requires` header

KIPs may have a `requires` header, indicating the KIP numbers that this KIP depends on.

#### `superseded-by` and `replaces` headers

KIPs may also have a `superseded-by` header indicating that a KIP has been rendered obsolete by a later document; the value is the number of the KIP that replaces the current document. The newer KIP must have a `replaces` header containing the number of the KIP that it rendered obsolete.

## Linking to other KIPs

References to other KIPs should follow the format `KIP-N` where **N** is the KIP number you are referring to. Each KIP that is referenced in an KIP MUST be accompanied by a relative markdown link the first time it is referenced, and MAY be accompanied by a link on subsequent references. The link MUST always be done via relative paths so that the links work in this GitHub repository, forks of this repository, the main KIPs site, mirrors of the main KIP site, etc. For example, you would link to this KIP as `./kip-201.md`.

## Auxiliary Files

KIPs may include auxiliary files such as diagrams. Such files must be named KIP-XXXX-Y.ext, where “XXXX” is the KIP number, “Y” is a serial number (starting at 1), and “ext” is replaced by the actual file extension (e.g. “png”).

## Transferring KIP Ownership

It occasionally becomes necessary to transfer ownership of KIPs to a new champion. In general, we&apos;d like to retain the original author as a co-author of the transferred KIP, but that&apos;s really up to the original author. A good reason to transfer ownership is because the original author no longer has the time or interest in updating it or following through with the KIP process, or has fallen off the face of the &apos;net (i.e. is unreachable or isn&apos;t responding to email). A bad reason to transfer ownership is because you don&apos;t agree with the direction of the KIP. We try to build consensus around a KIP, but if that&apos;s not possible, you can always submit a competing KIP.

If you are interested in assuming ownership of a KIP, send a message asking to take over, addressed to both the original author and the KIP reviewer. If the original author doesn’t respond to email in a timely manner, the KIP reviewer will make a unilateral decision (it’s not like such decisions can’t be reversed :)).

## KIP Roles

To encourage greater community participation with an effective KIP process framework as the foundation of open-source contribution to Kaia chain and ecosystem, we propose to have the following role structure:

- KIP Reviewer
- KIP Author
- Dev Team

### KIP Reviewers

The current KIP reviewers are:

` * Aidan Kwon (@aidan-kwon)`

` * Ollie Jeong (@blukat29)`

` * Iron Cho (@ironcho)`

` * Jack Jin (@kjeom)`

` * Ashwani Modi (@modiashwani)`

` * Scott Lee (@scott-klaytn)`

` * Neo Yiu (@neoofkaia)`

## KIP Reviewer Responsibilities

For each new KIP that comes in, a reviewer does the following:

- Read the KIP to check if it is ready: sound and complete. The ideas must make technical sense, even if they don&apos;t seem likely to get to final status.
- The title should accurately describe the content.
- Check the KIP for language (spelling, grammar, sentence structure, etc.), markup (Github flavored Markdown), code style

If the KIP isn&apos;t ready, the reviewer will send it back to the author for revision, with specific instructions.

Once the KIP is ready for the repository, the KIP reviewer will:

- Assign a KIP number (generally add “200” to the respective pull request number)
- Merge the corresponding pull request
- Send a message back to the KIP author with the next step

Many KIPs are written and maintained by developers with write access to the Kaia codebase. The KIP reviewers monitor KIP changes, and correct any structure, grammar, spelling, or markup mistakes identified.

## KIP Author

KIP author is any community member of Kaia blockchain who can present a proposal and start the KIP process.

## KIP Author Responsibilities

For each of the new KIPs that comes in, an author does the following:

- Articulating the proposed changes clearly and concisely starting from discussion threads on Kaia Dev Forum
- Explaining the expected effects of the proposed changes
- Encouraging community participation and engaging in discussions around the proposal
- Actively seeking feedback from other community members
- Checking alignment with the community and the strategic focuses of Kaia ecosystem

And any author of KIP is expected to:

- Draft, edit and refine the KIP using the provided template
- Follow the instructions provided in the GitHub/KIPs documentation
- Collect and incorporate feedback from the community and reviewers
- Document alternative opinions, the options considered and other discussed topics
- Champion the KIP through each stage of the KIP process
- Coordinate with Kaia’s  Dev Team for a smooth KIP implementation, being included in protocol and ecosystem level

## Dev Team

Dev Team refers to the Kaia’s core development team, ecosystem development team, contributors and any other active participants that are making updates, parameter changes, and upgrades to the Kaia protocol itself.

## Dev Team Responsibilities

For each of the new KIPs that comes in, the dev team does the following:

- Reviewing KIP’s implementation plan
- Executing the implementation plan of approved KIPs

And any author of KIP is expected to:

- Assess the feasibility of the KIP’s implementation plan and provide feedback to the KIP Author
- Ensure correct execution of the implementation plan in a timely manner
- Communicate and explain any delays and other important decisions about the implementation with the KIP Author and community

## KIP Numbers

When referring to KIP with any other category, it must be written in the hyphenated form `KIP-X` where **X** is that KIP’s assigned number. A default KIP number would be starting with adding “200” to the respective pull request number.

## History

This document was derived heavily from [Ethereum&apos;s EIP-1](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1.md) written by Martin Becze, Hudson Jameson, et al, as the first version, later specified with the Kaia’s KIP framework and process for Kaia blockchain and ecosystem exclusively.

[Ethereum&apos;s EIP-1](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1.md) was derived heavily from [Bitcoin&apos;s BIP-0001](https://github.com/bitcoin/bips) written by Amir Taaki which in turn was derived from [Python&apos;s PEP-0001](https://www.python.org/dev/peps/). In many places text was simply copied and modified.

The authors of the documents are not responsible for its use in the Kaia Improvement Proposal, and should not be bothered with technical questions specific to Kaia or the KIP. Please direct all comments to the KIP reviewers.

## Copyright

Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
</description>
        <pubDate>Tue, 03 Dec 2024 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-201</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-201</guid>
      </item>
    
      <item>
        <title>Reduce calldata gas cost and introduce floor data gas</title>
        <category>Core</category>
        
          <comments>https://github.com/kaiachain/kips/issues/22</comments>
        
        <description>## Abstract

Adjust Kaia’s transaction data (calldata) gas cost model to align with the two EIPs: [EIP-7623](https://eips.ethereum.org/EIPS/eip-7623) for introducing calldata floor gas and [EIP-2028](https://eips.ethereum.org/EIPS/eip-2028) for reducing calldata cost. By doing so, Kaia can achieve better compatibility with Ethereum’s post-Prague ecosystem and toolchain.

## Motivation

After Ethereum’s Prague hard fork, which includes [EIP-7623](https://eips.ethereum.org/EIPS/eip-7623), many Ethereum-oriented tools and SDKs assume the updated calldata pricing model.
If Kaia maintained its previous gas cost rules, developers using Ethereum SDKs or tools (e.g. ERC-4337 bundlers) would encounter mismatches in gas estimates and transaction validity.
Adopting the [EIP-7623](https://eips.ethereum.org/EIPS/eip-7623) calldata pricing model in Kaia eliminates these incompatibilities, allowing seamless use of Ethereum ecosystem software on Kaia.

## Specification

| Parameter | Value |
|-|-|
| `STANDARD_TOKEN_COST` | `4` |
| `TOTAL_COST_FLOOR_PER_TOKEN` | `10` |

Let `tokens_in_calldata = zero_bytes_in_calldata + nonzero_bytes_in_calldata * 4`.

Let `isContractCreation` be a boolean indicating the respective event.

Let `execution_gas_used` be the gas used for EVM execution with the gas refund subtracted.

Let `INITCODE_WORD_COST` be 2 as defined in [EIP-3860](https://eips.ethereum.org/EIPS/eip-3860).

Let `tx_gas` be the base intrinsic gas for the transaction type (default 21000, additional 10000 for fee-delegated transactions or 15000 for fee-delegation with ratio transactions).

Let `sig_validate_gas` be the gas used for signature validation (gas used to verify signatures for fee-delegated transactions or AccountKey transactions).

```
tx.gasUsed = (
    tx_gas
    + sig_validate_gas
    + max(
        STANDARD_TOKEN_COST * tokens_in_calldata
        + execution_gas_used
        + isContractCreation * (32000 + INITCODE_WORD_COST * words(calldata)),
        TOTAL_COST_FLOOR_PER_TOKEN * tokens_in_calldata
    )
)
```

The transaction must pay at minimum `TOTAL_COST_FLOOR_PER_TOKEN * tokens_in_calldata` gas for the calldata portion via the floor cost. If the standard cost (including execution) is higher than the floor-based cost, that higher cost applies; otherwise, the floor cost takes precedence, ensuring data-heavy, low-compute transactions pay more.

## Rationale

Because Kaia supports multiple customized transaction types (e.g., fee-delegated transactions), intrinsic gas is split into `tx_gas` and `sig_validate_gas`. This ensures that gas used for the transaction type and signature validation is calculated separately. As a result, `tx_gas` and `sig_validate_gas` are outside of the maximum function, so that we can apply the floor cost only to the calldata portion.

Other than that, we follow the [EIP-7623](https://eips.ethereum.org/EIPS/eip-7623) spec for the calldata gas cost calculation. The numbers in [EIP-7623](https://eips.ethereum.org/EIPS/eip-7623) were chosen to reduce the maximum block size while minimizing the impact on regular transactions. However, Kaia does not impose a block gas limit, so that particular rationale does not apply here. Instead, we adopt these parameters primarily for compatibility with Ethereum.

## Backward Compatibility

The calldata gas repricing is backwards incompatible since the hardfork.

SDKs and Wallets will be able to continue their operations with no change as they usually depend on the estimated gas cost from the `eth_estimateGas` API.

## References

- [EIP-7623](https://eips.ethereum.org/EIPS/eip-7623)
- [EIP-2028](https://eips.ethereum.org/EIPS/eip-2028)

## Copyright

Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
</description>
        <pubDate>Fri, 18 Oct 2024 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-223</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-223</guid>
      </item>
    
      <item>
        <title>Consensus liquidity for Kaia</title>
        <category>Core</category>
        
          <comments>https://github.com/kaiachain/kips/issues/35</comments>
        
        <description># Table of contents

- [Simple Summary](#simple-summary)
- [Abstract](#abstract)
- [Motivation](#motivation)
- [Specification](#specification)
  - [Terminology](#terminology)
  - [Overview](#overview)
  - [Configuration](#configuration)
  - [CLRegistry](#clregistry)
  - [StakingTrackerV2](#stakingtrackerv2)
  - [CLDEX](#cldex)
  - [Consensus](#consensus)
  - [On-chain Governance](#on-chain-governance)
- [JSON-RPC API](#json-rpc-api)
- [Rationale](#rationale)
- [Backward Compatibility](#backward-compatibility)
- [Copyright](#copyright)

## Simple Summary

Introduce a consensus liquidity mechanism for Kaia. The staked KAIA through the consensus liquidity will contribute to the consensus and [on-chain governance](https://kips.kaia.io/KIPs/kip-81) of Kaia.

## Abstract

This KIP introduces a consensus liquidity mechanism where KAIA and CL Token holders can participate in Kaia network through liquidity provision. Users stake KAIA tokens in the native decentralized exchange (CLDEX), simultaneously providing market liquidity and securing the network. Stakers earn both swap fees and block rewards, creating an efficient capital utilization.

The eligibility of GC remains unaffected by consensus liquidity, which means it still requires maintaining 5M KAIA staked solely through `CnStaking` contract, as is currently the case.

## Motivation

The current staking model often leads to capital inefficiency, where staked tokens remain idle. This KIP addresses this limitation by introducing consensus liquidity, which serves multiple purposes:

1. **New Utility**: Staked KAIA simultaneously secure the network and provide DEX liquidity, increasing the token utility.

2. **Enhanced Network Security**: By tying liquidity provision to consensus, we can expect more KAIA staked for the network, which enhances the network security.

3. **Sustainable Tokenomics**: The dual reward structure (swap fees + block rewards) creates a new utility and burn mechanism, building a sustainable tokenomics for Kaia and CL Token.

## Specification

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.

### Terminology

| Term                     | Description                                                                                              |
| ------------------------ | -------------------------------------------------------------------------------------------------------- |
| CnStaking                | The existing staking contract for Kaia. Usually referred as `CnStakingV2` or `CnStakingV3`               |
| Consensus Liquidity (CL) | A mechanism where KAIA and CL Token holders can participate in Kaia network through liquidity provision. |
| CLDEX                    | A decentralized exchange (DEX) on Kaia that allows users to trade KAIA and CL Token.                     |
| CL Token                 | A token pair with KAIA.                                                                                  |
| LP Token                 | A token representing the liquidity provider&apos;s share in the CLDEX.                                        |

### Overview

There&apos;ll be modified and new smart contracts to be developed.

![CL Overview](/assets/kip-226/KIP-226-1.png)

- `CLRegistry`: A new registry for CLDEX. Manage the information of CLDEXs for `StakingTrackerV2` and Kaia core.
- `CLDEX`: A new smart contract for CLDEX. It consists of router, factory, and pool.
- `StakingTrackerV2`: A modified [staking tracker](https://kips.kaia.io/KIPs/kip-81) to track the staked KAIA in CLDEX for the governance. The existing `StakingTracker` contract will be deprecated.

The detailed information of each smart contract is described in the following sections.

### Configuration

The KIP-226 shall be used since processing `PRAGUE_FORK_BLOCK_NUMBER` block. Please note that the staking information at previous block will be used to process current block since the Kaia hardfork.

| config                     | value |
| -------------------------- | ----- |
| `PRAGUE_FORK_BLOCK_NUMBER` | TBD   |

### CLRegistry

The `CLRegistry` contract must implement the `ICLRegistry` interface:

```solidity
interface ICLRegistry {
    struct CLInfo {
        /// @dev The node ID of the validator
        address nodeId;
        /// @dev The governance committee ID of the validator
        uint256 gcId;
        /// @dev The address of the CLDEX pool
        address clPool;
    }

    /// @dev Returns the CL information of all registered validators
    /// @return The CL information of all registered validators
    function getAllCLs() external view returns (address[] memory nodeIds, uint256[] memory gcIds, address[] memory clPools);
}
```

The `CLRegistry` will be registered in [KIP-149](https://kips.kaia.io/KIPs/kip-149) system registry with activation of `PRAGUE_FORK_BLOCK_NUMBER - 1`.

### StakingTrackerV2

The all external functions of `StakingTrackerV2` contract must have same selector and semantics as the existing `StakingTracker` contract to be compatible with the existing contracts.

The additional tracking processes of `StakingTrackerV2` are as follows:

1. Creating a new tracker: When creating a new tracker, the `StakingTrackerV2` contract must call `CLRegistry.getAllCLs()` to populate the CL information.
2. Updating the tracker: When the staked KAIA in `CLPool` changes, the `StakingTrackerV2` contract must update the staked KAIA for the corresponding validator.

The `StakingTrackerV2` will be registered in [KIP-149](https://kips.kaia.io/KIPs/kip-149) system registry with activation of `PRAGUE_FORK_BLOCK_NUMBER`.

#### Voting Power Eligibility

The validator is eligible for voting power if the staked KAIA in `CnStaking` is greater than or equal to 5M.

For example:

- Validator A: CnStaking (6M) + CLPool (5M) = 11M total stake, 2 voting power
- Validator B: CnStaking (4M) + CLPool (10M) = 14M total stake, 0 voting power

### CLDEX

The CLDEX is a decentralized exchange on Kaia for consensus liquidity based on UniswapV2&apos;s CPMM model and implementation. The liquidity providers (LPs) deposit KAIA and CL Token in the CLDEX and earn swap fees and block rewards. It consists of router, factory, and pool contracts.

![CLDEX Overview](/assets/kip-226/KIP-226-2.png)

#### Staking Requirements

- All LP tokens in CLDEX MUST be subject to a minimum staking period of 7 days from the moment of provision, without exception. This differs from the traditional CnStaking withdrawal process as:

  - The 7-day period begins automatically upon LP token creation
  - No separate withdrawal request is required
  - Additional staking or partial withdrawal is allowed but it&apos;ll reset the 7-day period
  - The transfer of LP Token leads to resettlement of the 7-day period of the sender

It makes the calculation of the staked KAIA in CLDEX more easier since the all KAIA in CLDEX can be considered as staked.

#### Pair Creation and Management

1. CL Token pairs MUST be approved through governance before being listed on CLDEX
2. No duplicate pair is allowed on CLDEX. Only one pair per CL Token is allowed.
3. Only designated owner addresses SHALL have the authority to create new pairs
4. During the initial stability period:
   - Liquidity provision SHALL be restricted to designated addresses
   - After stability is confirmed, liquidity provision SHALL be opened to all participants
   - Swap SHALL be available to all participants since the beginning

#### Block Rewards

The CLDEX will receive block rewards from the Kaia core for contributing to the consensus. Note that the block rewards will be directly injected into the CLPair (CLPool) contract, similar to the [Public Delegation](https://kips.kaia.io/KIPs/kip-163) contract.

1. Block rewards for CLDEX SHALL be distributed based on:
   - The proportional LP Token of each staker
   - Rewards SHALL be distributed per second to eligible stakers
   - No lockup period for rewards

#### StakingTrackerV2 Interaction

As `CnStaking` notifies its staked KAIA changes to `StakingTracker`, the CLDEX must notify to `StakingTrackerV2` whenever the staked KAIA in CLDEX changes. For example, based on UniswapV2&apos;s implementation, the target functions must call `stakingTrackerV2.refreshStake(address(this))`.

- `CLDEX.mint`: Used when providing liquidity
- `CLDEX.burn`: Used when removing liquidity
- `CLDEX.swap`: Used when swapping tokens

#### Commission

CLDEX commissions are derived from two sources:

1. Swap fees from CLPool
2. Block rewards from CLPool

**Swap Fee Commission**:

- Commissions can be collected from swap fees
- Burn ratios can be configurable by token type (e.g., KAIA: 10%, CL Token: 5%)

**Block Reward Commission**:

- Commissions can be collected from block rewards

For both commission types, the following parameters are configurable:

- Commission receiver addresses
- Split ratios for each receiver address
- Burn ratios

### Consensus

Since the `PRAGUE_FORK_BLOCK_NUMBER`, staked KAIA will be tracked through two mechanisms:

1. **CnStaking**

   - Existing staking mechanism through the `CnStaking` contract

2. **Consensus Liquidity**

   - New staking mechanism through CLDEX liquidity provision

Similar to voting power requirements, validators must maintain at least 5M KAIA staked in CnStaking to be eligible to propose blocks.
The total effective staked amount for staking rewards distribution is calculated as:

```
totalEffectiveStakedKAIA = staked KAIA in CnStaking + staked KAIA in CLDEX
```

#### Block Rewards Distribution

The total block reward for each validator will be calculated based on [KIP-82](https://kips.kaia.io/KIPs/kip-82), while the split ratio between `CnStaking` and `CLDEX` will be proportional to their respective staked KAIA amounts.

### On-chain Governance

The on-chain governance will be maintained except using `StakingTrackerV2` instead of `StakingTracker`.

#### StakingTracker Replacement Process

1. Deploy the new `StakingTrackerV2` contract
2. Propose a `StakingTracker` replacement on Voting
3. After the proposal is approved, the new `StakingTrackerV2` contract will be set for Voting
4. Replace all `StakingTracker` contracts of `CnStaking` with `StakingTrackerV2`

## JSON-RPC API

### `kaia_getStakingInfo`

The new field `clStakingInfo` will be added to the response of `kaia_getStakingInfo` since the `PRAGUE_FORK_BLOCK_NUMBER` to provide the CLStaking information if exists.

- Example

  ```json
  // Request
  curl -X POST -H &quot;Content-Type: application/json&quot; --data &apos;{&quot;jsonrpc&quot;:&quot;2.0&quot;, &quot;method&quot;:&quot;kaia_getStakingInfo&quot;, &quot;params&quot;:[&quot;latest&quot;],&quot;id&quot;:1}&apos; http://localhost:8551
  // Response
  {
  &quot;jsonrpc&quot;:&quot;2.0&quot;,
  &quot;id&quot;:1,
  &quot;result&quot;: {
      KIRAddr: &quot;0x6c38302e00dbac91712d93ab94b4a827698be6f1&quot;,
      PoCAddr: &quot;0xc8757a6b5b427ddcd5781c9faa5e41318d32336a&quot;,
      blockNum: 109,
      clStakingInfos: [{
            clNodeId: &quot;0x4e37f45ab0385782afbbc713f4b0d5f936ba2201&quot;,
            clPoolAddr: &quot;0x07987c97befca39f6fcf884271cc65f85d8d1a5b&quot;,
            clStakingAmount: 5000000
      }, {
            clNodeId: &quot;0x681f56e59003172201d155ca8a5dbe0ad0993641&quot;,
            clPoolAddr: &quot;0xe1f42e74dbf400affd54553a7c258b88ab3e2a8f&quot;,
            clStakingAmount: 10000000
      }],
      councilNodeAddrs: [&quot;0x4e37f45ab0385782afbbc713f4b0d5f936ba2201&quot;, &quot;0x681f56e59003172201d155ca8a5dbe0ad0993641&quot;, &quot;0x7fcd59726eb6560f385c195ff33db9385aadf644&quot;, &quot;0x15245589f68c14690cbcc3131256aeb594a006f7&quot;],
      councilRewardAddrs: [&quot;0x1ffdee22fa4df55d66dea1d9435343be2c0d3b12&quot;, &quot;0xf411483d602620203bbfc9ba87e38bcfb6430b90&quot;, &quot;0x2836a57c4fc95537ba2c3b1587d2a7d195a278e6&quot;, &quot;0x6b4679e1a0aa81ffcab3e0b70044b3c0928b7f44&quot;],
      councilStakingAddrs: [&quot;0xcf24c0f2e12c534046f32d1bd072e9eabbe1012d&quot;, &quot;0xd08ace335231e523c5cf7c0b1078a7365f3a606d&quot;, &quot;0x2c701c0bf6792a4643dc095547494357b92b5d72&quot;, &quot;0x4539ccc933283507d11aff505ea3fb47fad1d282&quot;],
      councilStakingAmounts: [5000000, 10000000, 15000000, 20000000],
      gini: 0.13,
      kcfAddr: &quot;0x6c38302e00dbac91712d93ab94b4a827698be6f1&quot;,
      kefAddr: &quot;0x6c38302e00dbac91712d93ab94b4a827698be6f1&quot;,
      kffAddr: &quot;0xc8757a6b5b427ddcd5781c9faa5e41318d32336a&quot;,
      kifAddr: &quot;0xc8757a6b5b427ddcd5781c9faa5e41318d32336a&quot;,
      useGini: false
      }
  }
  ```

## Rationale

### Set activation to `PRAGUE_FORK_BLOCK_NUMBER - 1` for `CLRegistry`

The Kaia&apos;s using previous block&apos;s staking information to process current block since the Kaia hardfork. It makes the `CLRegistry` at `PRAGUE_FORK_BLOCK_NUMBER - 1` to be required to track the staked KAIA in CLDEX at `PRAGUE_FORK_BLOCK_NUMBER` if exists.

Please note that the `StakingTrackerV2` will also populate the staked KAIA in CLDEX at `PRAGUE_FORK_BLOCK_NUMBER - 1` if transaction creating a new tracker is executed at exact `PRAGUE_FORK_BLOCK_NUMBER - 1` block, which is not feasible.

### Separate Reward Address

The validator only has one reward address even if it has multiple `CnStaking` contracts. But this KIP will break that invariant by separating the reward address for `CnStaking` and `CLDEX`. It&apos;s natural to separate the reward address since consensus liquidity is a new staking mechanism and has different form and reward profile.

### Minimum Staking Period

The decision not to implement a 7-day waiting period after unstaking request (like in CnStaking) was made for several reasons. From a consensus perspective, since Kaia&apos;s validators will be selected as proposer randomly based on [KIP-146](https://kips.kaia.io/KIPs/kip-146), requiring 5M in CnStaking provides sufficient protection against consensus instability. From a governance perspective, while it&apos;s important to prevent sudden voting power shifts in response to specific proposals, this can be adequately addressed through the minimum staking period alone. Additionally, LP assets receive comparatively lower block rewards relative to their invested assets compared to CnStaking, and are subject to impermanent loss risks. Therefore, to promote CL adoption through the ecosystem, the requirements were made more lenient to allow users to utilize CL more easily.

### 5M KAIA in CnStaking to be validator

As mentioned in the [Minimum Staking Period](#minimum-staking-period), the 5M KAIA in CnStaking is to prevent the consensus instability. It&apos;ll act as a safety margin for network stability. Even if the validator is not malicious, the staked KAIA in CLDEX can be extremely volatile due to the external market conditions. It&apos;s not reasonable to rely solely on the volatile staked KAIA.

## Backward Compatibility

### StakingTrackerV2

The `StakingTrackerV2` contract will support same functions as the existing `StakingTracker` contract to be compatible with the existing contracts.

## References

- [Enrinshed Liquidity](https://docs.initia.xyz/about/enshrined-liquidity-and-staking)
- [Proof of Liquidity](https://docs.berachain.com/learn/what-is-proof-of-liquidity)

## Copyright

Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
</description>
        <pubDate>Thu, 07 Nov 2024 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-226</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-226</guid>
      </item>
    
      <item>
        <title>Candidate and Validator Evaluation</title>
        <category>Core</category>
        
          <comments>https://github.com/kaiachain/kips/issues/84</comments>
        
        <description>## Simple Summary

This proposal presents VRank, a framework for quantitatively assessing the performance and stability of candidates and validators in the Kaia Chain network.

## Abstract

As Kaia Chain transitions to a permissionless network structure, it is critical to hold validators more accountable. They play an important role in ensuring that the network runs smoothly, securely, and without problems. This KIP introduces VRank, a Validator Reputation Evaluation Framework that quantitatively assesses the performance and stability of both candidates and validators. VRank aims to ensure that nodes involved in the consensus mechanism are trustworthy and capable of meeting the security requirements of the permissionless Kaia Chain network.

## Introduction

It is planned for the Kaia Chain network to change from a Permissioned network to a Permissionless network. In a permissionless network, anyone can become a validator without being approved first. This makes the network more decentralized, safe, and open to everyone. This change fits with Kaia Chain&apos;s goal of making the blockchain ecosystem more open and strong. Please refer to [KGP-4: Permissionless Kaia Chain](https://govforum.kaia.io/t/kgp-4-permissionless-klaytn/135) for more thorough details for switching to a permissionless network.

## Motivation

In decentralized networks employing blockchain technology, the reliability and performance of validator nodes are crucial for maintaining network stability, security, and efficiency. Validator nodes propose and validate new blocks, ensuring ledger integrity and establishing trust among participants through consistent operation.

However, not all validator nodes operate at optimal efficiency. Certain individuals may experience frequent failures or delays, while others may exhibit malicious behavior, either deliberately or due to external pressures. These problems may lead to delays in the network, a chance of forks, and higher susceptibility to attacks, such as double-spending and censorship.

The current systems for evaluating validator performance may ineffectively penalize persistent underperformance or malicious behavior, and they fail to consistently encourage optimal performance incentives. A thorough evaluation system is essential to precisely evaluate the reliability of validator nodes, discourage inadequate performance, and improve the overall integrity of the network.

This proposal introduces an innovative evaluation framework for candidates and validators, highlighting measurable performance metrics. Our objective is to create a more resilient and fair framework for assessing node performance by defining metrics such as the Proposal Failure Score (PFS) and Candidate Failure Score (CFS). This framework aims to identify underperforming or malicious nodes, which helps preserve high standards among validators.

The framework promotes consistent uptime and reliability. Validators have an incentive to maintain the stability and responsiveness of their nodes, thereby maintaining the performance of the network.

## Specification

### Parameters

| Constant                    | Value/Definition                                                        |
| :-------------------------- | :---------------------------------------------------------------------- |
| `FORK_BLOCK`                | TBD                                                                     |
| `CANDIDATE_MSG_TIMEOUT`     | Protocol parameter (milliseconds). Default = 500ms.                     |
| `EPOCH_LENGTH`              | 86,400 blocks (approximately 1 day, assuming 1-second block time)       |
| `MAX_BYZANTINE_NODES` (`F`) | Calculated as `F = (n - 1) // 3`, where `n` is the number of validators |
| `PFS_THRESHOLD`             | 2 (max proposal failures per epoch)                                     |
| `CFS_THRESHOLD`             | 300 (max candidate failures per epoch)                                  |

### Data Structures and Protocol Primitives

#### Block Header Extension (VRank)

Starting from `FORK_BLOCK`, the block header includes a new field `VRank`.
The `VRank` field contains `RLPEncode(cfReport)` or `nil` if `cfReport` is empty.

```go
type Header struct {
    ParentHash   common.Hash
    // ... existing fields ...
    Extra        []byte
    Governance   []byte
    Vote         []byte
    BaseFee      *big.Int
    RandomReveal []byte
    MixHash      []byte
    VRank        []byte  // New field
}
```

#### Reports (pfReport, cfReport)

Both `pfReport` and `cfReport` are per-block data structures. A node&apos;s presence in either report is undesirable: it indicates a failure, and the node may be penalized in future epoch evaluations.

**pfReport** (Proposal Failure Report): Extractable from `header.Extra`. Contains the list of proposers who induced round-change during the consensus of the block.

Format: `pfReport(N) -&gt; [proposerAddrRound0, proposerAddrRound1, ...]` with at most one entry per validator (`validator(N)`).

**cfReport** (Candidate Failure Report): Encoded in `header.VRank` at block `N` for target block `N-1`.
Contains the list of candidates (nodes in `CandTesting`) that failed to send a valid `VRankCandidate` message on-time for block `N-1`.
If `N % k*EPOCH_LENGTH == 0` (epoch start), `cfReport(N)` MUST be empty.

Format: `cfReport(N) -&gt; [candidateAddr1, candidateAddr2, ...]` with at most one entry per candidate of previous block (`candidate(N-1)`).

#### VRankPreprepare

`VRankPreprepare` is a message type sent by the proposer of block `N` to all candidates under `CandTesting` after having sent Istanbul Preprepare messages to consensus participants. It triggers candidates to respond with `VRankCandidate`. The timeout for candidate response is `CANDIDATE_MSG_TIMEOUT` (default 500ms).

```go
type VRankPreprepare struct {
	Block *types.Block
	View  *istanbul.View
}
```

#### VRankCandidate

`VRankCandidate` is a message type sent by each candidate (node in `CandTesting`) to all validators under `ValActive` upon receiving `VRankPreprepare`. A candidate must send `VRankCandidate` within `CANDIDATE_MSG_TIMEOUT` of the counterparty&apos;s `preprepared_time` to be counted as on-time.

**Signature scheme**: The `signature` MUST be produced with the candidate&apos;s validator signing key over `keccak256(&quot;VRANK_CANDIDATE_V1&quot; || chain_id || block_number || round || block_hash)`, with an unambiguous canonical encoding of each field.

```go
type VRankCandidate struct {
	BlockNumber uint64
	Round       uint8
	BlockHash   common.Hash
	Sig         []byte
}
```

### Consensus Protocol Integration

VRank runs in parallel with consensus. Per block, reports (`pfReport` and `cfReport`) are produced during consensus and committed in the next block header.

#### Proposer of block N

1. After having sent Istanbul Preprepare messages to consensus participants, the proposer MUST send `VRankPreprepare` to all candidates in `CandTesting`.
2. The round information is recorded in `header.Extra` as part of the existing consensus. If the proposer fails to propose and a round change occurs, the failed proposer&apos;s address is recorded in `pfReport(N)`.

#### Validators during consensus for block N

1. When block `N` enters the `preprepared` pBFT state, each validator MUST record `preprepared_time`.
2. Each validator MUST collect `VRankCandidate` messages from candidates in `CandTesting` and record each message&apos;s arrival time.
3. If a validator receives more than one `VRankCandidate` from the same candidate for the same view (block number `N` and round `R`), only the first valid message MUST be accepted; subsequent messages MUST be ignored.
4. A candidate is counted as on-time if the message is valid and either (a) it arrives before `preprepared_time`, or (b) `arrival_time - preprepared_time ≤ CANDIDATE_MSG_TIMEOUT`. Otherwise, it will be recorded in `cfReport(N+1)`.

#### Candidates (nodes in CandTesting)

1. Upon receiving `VRankPreprepare` for block `N`, each candidate MUST broadcast `VRankCandidate` to all validators in `ValActive`.
2. To be counted as on-time, the `VRankCandidate` MUST arrive at each validator within `CANDIDATE_MSG_TIMEOUT` of that validator&apos;s `preprepared_time` for block `N`.

#### Proposer of block N+1

1. When proposing block `N+1`, the proposer MUST build `header.VRank` from `cfReport(N)`.
2. `cfReport(N+1)` MUST include each candidate (in `CandTesting` at block `N`) who either (a) did not send a valid `VRankCandidate` for block `N` on-time, or (b) sent an invalid message.
3. The proposer MUST encode `cfReport` in `header.VRank` as `RLPEncode(cfReport)` or `nil` if empty.
4. Candidates in `cfReport` are counted as failures for CFS aggregation.

#### Block Validation

After `FORK_BLOCK`, validators MUST validate the newly added `VRank` field in the block header.
The values of the subfields (`cfReport`) are used to evaluate node performance using the components of the VRank framework.
Given a header with number `N`:

- `header.VRank` MUST be `nil` or `RLPEncode(cfReport(N))`.
- `cfReport(N)` MUST contain at most one entry per candidate ID. Each entry must be a candidate address from `candidates(N-1)`.

### Failure Scores (PFS, CFS)

Each score is per epoch, computed from `pfReport` and `cfReport` in epoch blocks. Higher values indicate worse performance; zero indicates no failures.

**Proposal Failure Score (PFS)**: For a given block number `N`, PFS MUST be computed from `pfReport(b)` for blocks `x ∈ [epochStart(N), N]`. For each validator, count how many times the validator appears across all `pfReport`s in the epoch (each round change adds one entry). PFS maps each validator address to its total proposal failure count.

Format: `pfs(N) -&gt; map[proposerAddr]score`

**Candidate Failure Score (CFS)**: For a given block number `N`, CFS MUST be computed from `cfReport(b)` for blocks `b ∈ [epochStart(N), N]` (note that `cfReport(epochStart(N))` is empty).
For each candidate `C` and reporter (proposer of block `N`): if `C` is in `cfReport(N)`, that counts as 1 failure.
For each candidate, sum failures per reporter over the epoch, discard the highest `F` reporter totals (Byzantine filtering), and sum the remainder to obtain CFS.

Format: `cfs(N) -&gt; map[candidateAddr]score`

**Example: Byzantine filtering in CFS**

_Example 1: Short epoch_

```
epoch = 5
len(validator) = 4
len(candidates) = 3
F = 1

proposer(5)=P1, cfReport(5)=[]
proposer(6)=P2, cfReport(6)=[]
proposer(7)=P3, cfReport(7)=[C1,C2,C3]
proposer(8)=P4, cfReport(8)=[C1,C2]
proposer(9)=P4, cfReport(9)=[C1,C2]
```

Aggregated `cfReport(N)` where N ∈ \[5, 9\]:

&lt;table&gt;
&lt;tr&gt;
  &lt;th rowspan=&quot;2&quot;&gt;Candidate \ Reporter&lt;/th&gt;
  &lt;th colspan=&quot;4&quot;&gt;raw data (cfReport)&lt;/th&gt;
  &lt;th colspan=&quot;3&quot;&gt;summary (CFS)&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;th&gt;P1&lt;/th&gt;&lt;th&gt;P2&lt;/th&gt;&lt;th&gt;P3&lt;/th&gt;&lt;th&gt;P4&lt;/th&gt;
  &lt;th&gt;Total&lt;/th&gt;&lt;th&gt;Filtered&lt;/th&gt;&lt;th&gt;Byzantine filtering&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;C1&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;P4 is not counted&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;C2&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;P4 is not counted&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;C3&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;P3 is not counted&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;

_Example 2: Byzantine behavior_

Consider a network with 10 validators (proposers P1–P10) and 5 candidates (C1–C5).
The table shows how many times each candidate appears in `cfReport(N)` when each proposer produced a report (i.e., failures reported per candidate per reporter).
P8, P9, and P10 report abnormally high counts for C1–C3, suggesting Byzantine behavior.
With `F = 3`, we discard the highest 3 reporter totals per candidate and sum the remainder to obtain the filtered CFS.

&lt;table&gt;
&lt;tr&gt;
  &lt;th rowspan=&quot;2&quot;&gt;Candidate \ Reporter&lt;/th&gt;
  &lt;th colspan=&quot;10&quot;&gt;raw data (cfReport)&lt;/th&gt;
  &lt;th colspan=&quot;3&quot;&gt;summary (CFS)&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;th&gt;P1&lt;/th&gt;&lt;th&gt;P2&lt;/th&gt;&lt;th&gt;P3&lt;/th&gt;&lt;th&gt;P4&lt;/th&gt;&lt;th&gt;P5&lt;/th&gt;&lt;th&gt;P6&lt;/th&gt;&lt;th&gt;P7&lt;/th&gt;&lt;th&gt;P8&lt;/th&gt;&lt;th&gt;P9&lt;/th&gt;&lt;th&gt;P10&lt;/th&gt;
  &lt;th&gt;Total&lt;/th&gt;&lt;th&gt;Filtered&lt;/th&gt;&lt;th&gt;Byzantine filtering&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;C1&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;14&lt;/td&gt;&lt;td&gt;12&lt;/td&gt;&lt;td&gt;15&lt;/td&gt;&lt;td&gt;34&lt;/td&gt;&lt;td&gt;12&lt;/td&gt;&lt;td&gt;32&lt;/td&gt;&lt;td&gt;20&lt;/td&gt;&lt;td&gt;8640&lt;/td&gt;&lt;td&gt;8637&lt;/td&gt;&lt;td&gt;8634&lt;/td&gt;&lt;td&gt;26050&lt;/td&gt;&lt;td&gt;&lt;strong&gt;139&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;exclude reports from P8,P9,P10&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;C2&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;48&lt;/td&gt;&lt;td&gt;10&lt;/td&gt;&lt;td&gt;59&lt;/td&gt;&lt;td&gt;33&lt;/td&gt;&lt;td&gt;49&lt;/td&gt;&lt;td&gt;49&lt;/td&gt;&lt;td&gt;41&lt;/td&gt;&lt;td&gt;8640&lt;/td&gt;&lt;td&gt;8637&lt;/td&gt;&lt;td&gt;8634&lt;/td&gt;&lt;td&gt;26200&lt;/td&gt;&lt;td&gt;&lt;strong&gt;289&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;exclude reports from P8,P9,P10&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;C3&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;48&lt;/td&gt;&lt;td&gt;22&lt;/td&gt;&lt;td&gt;40&lt;/td&gt;&lt;td&gt;41&lt;/td&gt;&lt;td&gt;44&lt;/td&gt;&lt;td&gt;27&lt;/td&gt;&lt;td&gt;61&lt;/td&gt;&lt;td&gt;8640&lt;/td&gt;&lt;td&gt;8637&lt;/td&gt;&lt;td&gt;8634&lt;/td&gt;&lt;td&gt;26194&lt;/td&gt;&lt;td&gt;&lt;strong&gt;283&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;exclude reports from P8,P9,P10&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;C4&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;50&lt;/td&gt;&lt;td&gt;29&lt;/td&gt;&lt;td&gt;45&lt;/td&gt;&lt;td&gt;30&lt;/td&gt;&lt;td&gt;23&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;42&lt;/td&gt;&lt;td&gt;56&lt;/td&gt;&lt;td&gt;56&lt;/td&gt;&lt;td&gt;64&lt;/td&gt;&lt;td&gt;397&lt;/td&gt;&lt;td&gt;&lt;strong&gt;221&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;exclude reports from P8,P9,P10&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;C5&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;71&lt;/td&gt;&lt;td&gt;34&lt;/td&gt;&lt;td&gt;62&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;11&lt;/td&gt;&lt;td&gt;20&lt;/td&gt;&lt;td&gt;18&lt;/td&gt;&lt;td&gt;30&lt;/td&gt;&lt;td&gt;19&lt;/td&gt;&lt;td&gt;13&lt;/td&gt;&lt;td&gt;283&lt;/td&gt;&lt;td&gt;&lt;strong&gt;116&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;exclude reports from P1,P2,P3&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;

_Note: Each `cfReport(N)` is in the header of block `N` and reports on target block `N - 1`._

### Score thresholds

A node must meet the following stability requirements to participate in consensus:

- **Block proposal participation**: At most `PFS_THRESHOLD` proposal failures per epoch.
- **Downtime**: Less than 0.5% downtime per epoch (fewer than 432 blocks missed).

Violations:

- a validator exceeding `PFS_THRESHOLD` in an epoch is classified as not qualified.
- a candidate exceeding `CFS_THRESHOLD` in an epoch is classified as not qualified.

The handling of not-qualified nodes is specified in [KIP-286](https://kips.kaia.io/KIPs/kip-286).

## Rationale

### Choice of CFS_THRESHOLD

Historical data from vrank logs indicates that most healthy nodes pass the evaluation with CFS well below 300 per epoch. The threshold of 300 is therefore set to distinguish underperforming or unstable candidates while allowing normal nodes to qualify for validator promotion.

### Importance of Mitigating Malicious Behavior

**Byzantine Nodes**  
In a permissionless environment, some validators may act maliciously, attempting to disrupt the network or unfairly penalize honest nodes. It is assumed that up to one-third of the validators may behave maliciously.

**Filtering Mechanisms**  
To mitigate the impact of malicious validators, the highest `F` failure reports are excluded in CFS calculations. This ensures that the actions of a few Byzantine nodes do not distort the evaluation of honest candidates.

**Robust Scoring Algorithms**  
VRank&apos;s design ensures that honest nodes are not unfairly penalized due to the actions of Byzantine nodes.

### Importance of the 500ms Deadline

**Ensuring Consensus Responsiveness**

The **500ms** deadline for `CANDIDATE_MSG_TIMEOUT` ensures that candidates respond promptly, supporting the network&apos;s goal of generating blocks every second.

**Regional Centralization**

Candidate-to-validator latency depends on distance: candidates near the validator cluster (where most validators are located) observe shorter latency; those farther away observe longer latency.
A short timeout would favor candidates collocated with the validator majority and effectively exclude those at greater distance, reinforcing regional concentration.
This carries significant risks: (a) a regional network outage could affect a large fraction of the validator set, and (b) operators outside the cluster face a higher barrier to participation.  
A longer timeout (e.g., 500ms) allows candidates farther from the cluster to participate, improving decentralization and resilience.

**Block Time Impact**

A longer timeout does not necessarily slow block production. Block progression is driven by the proposer&apos;s Istanbul Preprepare being sent and committed on time; `VRankCandidate` is an auxiliary evaluation message collected in parallel. The 500ms window accounts for global network latency variations without unfairly penalizing distant candidates, while the primary consensus path remains unaffected.

**Design Choice**

Given that regional centralization poses a greater risk than the marginal impact of a 500ms evaluation window, the design favors a longer timeout to support geographic diversity.

### The omission of signatures in cfReport

If we required valid `VRankCandidate` messages (with signatures) to be included in `cfReport`, then each entry would need a verifiable signature.
However, the proposer has the authority to include any candidate in `cfReport` regardless.
A malicious proposer could intentionally omit a candidate&apos;s valid signature and claim that the candidate did not send any message—thereby falsely penalizing an honest candidate (a **false positive**).

Including signatures would block false negatives (a proposer could not falsely claim a candidate failed when they actually sent a valid message).
However, if the proposer is an accomplice of the candidate, they could collude to omit the candidate from `cfReport` even when the candidate failed—bypassing the signature check.

Given that signatures cannot fully prevent manipulation in either direction, and that signatures add significant size to the report, we decided to simplify: `cfReport` is a list of candidate addresses only (no signatures). The Byzantine filtering in CFS (excluding the highest `F` reporter totals) mitigates the impact of malicious proposers.

### The exclusion of pfReport from header.VRank

`pfReport` is extracted from `header.Extra` rather than stored in `header.VRank`. Round-change information is recorded during consensus, before the block is finalized. If `pfReport` were written into `header.VRank` upon each round change, the header would need to be updated mid-consensus. Supporting such updates would require substantial changes to the current implementation. The `Extra` field is already populated during consensus with round-change data, so `pfReport` is derived from there instead.

### Empty `CfReport(k*EPOCH_LENGTH)`

The validator set changes every `EPOCH_LENGTH`, so there may be new validators at block `k*EPOCH_LENGTH` that were not validators at block `k*EPOCH_LENGTH - 1`.
Those new validators did not participate in consensus for block `k*EPOCH_LENGTH - 1` and therefore could not have collected `VRankCandidate` messages.
The proposer of block `k*EPOCH_LENGTH` may be such a new validator, so they cannot produce a valid `cfReport(k*EPOCH_LENGTH)`.
Hence the `vrank` field in the header at `k*EPOCH_LENGTH` MUST be `nil`.

## Backward Compatibility

The introduction of VRank does not affect existing nodes before `FORK_BLOCK`. Nodes operating prior to `FORK_BLOCK` will continue to function as before. After `FORK_BLOCK`, the new `vrank` field and associated validation processes come into effect.

## Security Considerations

### Handling Byzantine Nodes

**Assumption of One-Third Malicious Validators**  
We accept the standard Byzantine fault tolerance assumption that up to one-third of validators may behave maliciously. Kaia Chain relies on the assumption that less than one-third of participants are malicious to ensure safety and liveness. VRank&apos;s scoring mechanism is designed with this threshold in mind, allowing the network to function correctly even in the presence of some malicious actors.

**Limitations and Contingencies**

If the number of malicious validators exceeds one-third, the network&apos;s ability to reach consensus and maintain integrity may be compromised.

**Justification for the Assumption**  
While it&apos;s challenging to prevent all malicious activity, assuming that up to one-third of validators could be compromised provides a practical balance between security and network performance.

## Implementation

TBD

## Appendix: Node Models

The following node models were considered when designing VRank and defining a stable node. They describe the philosophy behind the scoring thresholds and help clarify the types of behavior VRank aims to distinguish.

VRank categorizes nodes into four models to evaluate their performance and stability:

| Node                                                | Performance   | Impact                                                          |
| :-------------------------------------------------- | :------------ | :-------------------------------------------------------------- |
| **Uptime \&gt; 99.5%, No network issues**              | **Excellent** | **Contribute to network stability**                             |
| **Uptime about 99.5% temporally unstable**          | **Good**      | **May delay block time**                                        |
| **Uptime \&lt; 99.5%**                                 | **Not good**  | **May fail to propose a block**                                 |
| **Halts continuously regardless uptime**            | **Bad**       | **May affect consensus if consists of nodes experiencing this** |
| **Uptime \&gt; 99.5%, try to destabilize the network** | **N/A**       | **Threat network integrity**                                    |

1. **Node A: Stable Node**

   **Characteristics**: Capable of performing validation duties with optimal performance and stability.

   **Impact on Network**: Contributes positively to network stability and performance.

2. **Node B: Temporarily Unstable Node**

   **Characteristics**: Experiences brief, frequent network disruptions that last a few seconds.

   **Impact on Network**: May delay block creation if selected as a proposer but does not cause a round change.

3. **Node C: Intermittently Stopping Node**

   **Characteristics**: Experiences longer network disruptions (tens of seconds).

   **Impact on Network**: May fail to propose a block when selected as a proposer, resulting in round changes and significant delays.

4. **Node M: Malicious Node**

   **Characteristics**: Intentionally attempts to destabilize the network through malicious actions.

   **Impact on Network**: Threatens network security and integrity. VRank aims to mitigate the influence of such nodes.

## Copyright

Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
</description>
        <pubDate>Tue, 07 Jan 2025 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-227</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-227</guid>
      </item>
    
      <item>
        <title>SetCode for EOA</title>
        <category>Core</category>
        
          <comments>TBD</comments>
        
        <description>## Abstract

Add a new transaction type to assign a code to an EOA. This KIP focuses on describing the difference from [EIP-7702](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7702.md).

## Motivation

Improve user experience by allowing account abstraction features to already existing EOAs. Previously, for EOA owners to benefit from ERC-4337 interface, they had to create a new smart account and migrate all assets and privileges. With EIP-7702, users can attach code to an existing EOA in-place.

## Specification

### Parameters

| Parameter | Value |
|-|-|
| `EMPTY_ROOT_HASH` | `0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421` |
| `EMPTY_CODE_HASH` | `0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470` |
| `SET_CODE_TX_TYPE` | `0x04` |
| `ENVELOPED_SET_CODE_TX_TYPE` | `0x7804` |

### SetCode Transaction

Introduce the SetCode transaction type where its RLP encoding is defined below.

```
0x7804 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, value, data, access_list, authorization_list, signature_y_parity, signature_r, signature_s])

authorization_list = [[chain_id, address, nonce, y_parity, r, s], ...]
```

The fields follow the same semantics as EIP-7702.

Its consensus type prefix (that affects `block.transactionRoot`) is `ENVELOPED_SET_CODE_TX_TYPE (0x7804)` and its eth namespace API type prefix is `SET_CODE_TX_TYPE (0x04)`. See “Changes to API” for clarification.

#### Behavior

The transaction’s behavior follows that of [EIP-7702](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7702.md) except for the following:

For each authorization tuple `[chain_id, address, nonce, y_parity, r, s]`,
- After recovering the authority,
  - Fetch the AccountKey field of the recovered authority account.
  - Verify that the AccountKey is of type `AccountKeyTypeLegacy`. If verification fails, immediately stop processing the tuple and continue to the next tuple in the list.
- When setting the code of the authority,
  - Also set its codeInfo to `0x11 (VmVersion1 &amp; EVM)`
  - As a special case, if address is `0x0000000000000000000000000000000000000000`, reset the authority account&apos;s codeInfo to `0x00`.

#### Delegation Designation

Semantics of `EXTCODESIZE`, `EXTCODECOPY`, `EXTCODEHASH`, `CALL`, `CALLCODE`, `STATICCALL`, `DELEGATECALL` opcodes follows EIP-7702.

#### Gas costs

The intrinsic gas cost of a SetCode transaction follows the EIP-7702, meaning it inherits [EIP-2930](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2930.md) and [EIP-7623](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7623.md). Note that the EIP-7623 rule is realized by [KIP-223](https://github.com/kaiachain/kips/blob/main/KIPs/kip-223.md).

#### Transaction Origination

Since Kaia hasn&apos;t been explicitly enforcing [EIP-3607](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3607.md), there is no need to modify transaction origin restriction.

Do follow the EIP-7702’s behavior where if a transaction’s destination has a delegation, also warm its target

### Account State

The account RLP encoding for the world state trie needs to change.

```
EOA&apos;s compact encoding: 0x01 || rlp([nonce, balance, humanReadable, accountKey])
EOA&apos;s full encoding:    0x01 || rlp([[nonce, balance, humanReadable, accountKey], storageRoot, codeHash, codeInfo])
SCA&apos;s encoding:         0x02 || rlp([[nonce, balance, humanReadable, accountKey], storageRoot, codeHash, codeInfo])
```

Now both EOAs and SCAs hold the same set of 7 fields: `nonce`, `balance`, `humanReadable`, `accountKey`, `storageRoot`, `codeHash`, and `codeInfo`.

When an EOA is RLP encoded, the compact encoding is used when the account has storageRoot = EMPTY_ROOT_HASH, codeHash = EMPTY_CODE_HASH, and codeInfo = 0x0. Conversely, when decoding an RLP-encoded EOA, the compact encoding implies that the account has storageRoot = EMPTY_ROOT_HASH, codeHash = EMPTY_CODE_HASH, and codeInfo = 0x0.

If an EOA’s delegation was cleared by a SetCode transaction with authorization.address = 0x0, set its CodeHash to EMPTY_CODE_HASH as per EIP-7702, and also set its CodeInfo to 0x0. Note that the account does not necessarily return to compact encoding. It will be compactly encoded only if storageRoot is EMPTY_ROOT_HASH.

An EOA with codeHash = EMPTY_CODE_HASH can be called “EOA without code” and an EOA with codeHash != EMPTY_CODE_HASH can be called “EOA with code”. Note that EOA without code doesn’t always mean it will be compactly encoded.

### Interaction with other transaction types

The `to` address of the following transaction types MUST be an EOA without code:

- `TxTypeValueTransfer`
- `TxTypeFeeDelegatedValueTransfer`
- `TxTypeFeeDelegatedValueTransferWithRatio`
- `TxTypeValueTransferMemo`
- `TxTypeFeeDelegatedValueTransferMemo`
- `TxTypeFeeDelegatedValueTransferMemoWithRatio`

The `from` address of the following transaction types MUST be an EOA without code:

- `TxTypeAccountUpdate`
- `TxTypeFeeDelegatedAccountUpdate`
- `TxTypeFeeDelegatedAccountUpdateWithRatio`

The `to` address of the following transaction types MUST be an EOA with code or an SCA:

- `TxTypeSmartContractExecution`
- `TxTypeFeeDelegatedSmartContractExecution`
- `TxTypeFeeDelegatedSmartContractExecutionWithRatio`

Note that whether the emptiness of storageRoot does not matter in transaction type restrictions.

### Changes to APIs

#### eth_getRawTransaction, eth_sendRawTransaction

eth_getRawTransaction returns the raw transaction RLP prefixed with `SET_CODE_TX_TYPE (0x04)` and eth_sendRawTransaction accepts such raw transaction.

#### kaia_getRawTransaction, kaia_sendRawTransaction

kaia_getRawTransaction returns the raw transaction RLP prefixed with `ENVELOPED_SET_CODE_TX_TYPE (0x7804)` and kaia_sendRawTransaction accepts such raw transaction.

#### eth_getBlock*, eth_getTransaction*, eth_getTransactionReceipt

For SetCode transaction type, eth namespace APIs that return the transaction fields MUST show the `authorizationList` field and its `type: &quot;0x04&quot;`.

#### kaia_getBlock*, kaia_getTransaction*, kaia_getTransactionReceipt

For SetCode transaction type, kaia namespace APIs that return the transaction fields MUST show the `authorizationList` field and its `typeInt: 30724`, and `type: &quot;TxTypeEthereumSetCode&quot;`.

#### eth_getCode, eth_getStorageAt, kaia_getCode, kaia_getStorageAt

APIs querying the code and storage of an account MUST work correctly for EOAs.

#### kaia_getAccount, kaia_isContractAccount

For both EOAs and SCAs, its kaia_getAccount result SHOULD include storageRoot, codeHash, codeFormat, vmVersion fields.

For both EOAs and SCAs, kaia_isContractAccount SHOULD return true if its codeHash is not EMPTY_CODE_HASH.

### Changes to SDK

Before an SDK signs an authorization tuple, the SDK MAY verify that the authority’s AccountKey is of type AccountKeyTypeLegacy. If the SDK does not verify, it is the responsibility of the SDK user (e.g. Wallet) to make sure the authority is eligible to use the SetCode transaction.

## Rationale

### Extending the EOA instead of converting to SCA

Because the feature is called “Set EOA account code”, there could be a need to distinguish between EOA with code and an SCA.

### EOAs with updated AccountKeys cannot sign authorization

In Kaia, EOAs may change the transaction signing permissions by sending an AccountUpdate transaction. But in this KIP, we strictly let the original private key (i.e. the one that AccountKeyLegacy accepts) to sign the SetCode authorization. Decoupling the two permissions will be confusing, so EOAs with code must have AccountKeyLegacy at all times.

### EOA RLP formats depend on field values

For backward compatibility, we have to retain the compact encoding.

If we choose to select the encoding depending on hardfork instead, then all trie handling code must be fork-aware, and coding in that is non-trivial.

### Kaia transaction type restrictions

ValueTransfer transaction types cannot be sent to EOAs with code. Kaia’s ValueTransfer transaction types have the semantics of simply increasing the balance of the destination EOA. If ValueTransfer can target EOA with code, the balance increase would bypass the EOA’s fallback() or receive() function. Opening a way to bypass the fallback may have an unknown security implication, thus prohibiting such a case.

SmartContractExecution transaction types can be sent to EOAs with code. Kaia’s SmartContractExecution transaction types had the semantics of executing the code of the destination SCA. Now that EOAs with code has a code, SmartContractExecution transactions indeed can invoke them.

AccountUpdate transaction types cannot be sent to EOAs with code. The reason is the same as why EOAs with updated AccountKeys cannot sign authorization.

## Backwards Compatibility

The stateRoot calculation is backward compatible because we retain the EOA’s compact encoding.

The semantics of Ethereum typed transactions are backward compatible. If the target account has nonempty code, the code is executed.

Some wallets and dapps may want to determine if a given address is contract or not, to select between ValueTransferTx and SmartContractExecutionTx. In this case, they can keep using the `kaia_getAccount` or `kaia_isContractAccount` APIs. If the APIs return an empty code, they must choose ValueTransferTx. Otherwise, the target is contract so they must use SmartContractExecutionTx.

## Implementation

[implementation](https://github.com/kaiachain/kaia/tree/v2.0.0-rc.5)

## References

- [EIP-7702](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7702.md)

## Copyright

Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
</description>
        <pubDate>Fri, 15 Nov 2024 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-228</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-228</guid>
      </item>
    
      <item>
        <title>Storage Compression</title>
        <category>Core</category>
        
          <comments>https://devforum.kaia.io/t/discussion-on-kip-237/8061/2</comments>
        
        <description>## Abstract
Compress the block header, body, and receipts data in its persistent form to reduce disk usage.
With an efficient data structure, historic data remains accessible with minimal query overhead from the lossless-compressed database.

## Motivation
Kaia has been focusing on optimizing the state trie storage through [state migration and live pruning](https://docs.kaia.io/learn/storage/state-pruning/).
While these optimizations dealt with the most significant part of the database, there is still potential to further reduce the other parts of the database.
This KIP reduces the storage footprint of the following three data types:
- Header: RLP-encoded block header. Most header fields are repeated across headers.
- Body: RLP-encoded transaction bodies in a block. Transaction calldatas tend to contain many zero bytes and the contents are often similar between different transactions.
- Receipts: RLP-encoded transaction receipts in a block. Receipt event logs tend to contain many zero bytes and the contents are often similar between different transactions.

The three data types are generated every time a new block is inserted.
They consume a substantial amount of storage. As of 2024-12-02, In the migrated mainnet chaindata, the header, body, and receipts account for 10.4%, 28.1%, and 40.9% of the total size, respectively, which together occupy 79.4% of the node&apos;s disk space.
This illustrates that the state trie is no longer a dominant data in the full-node with pruned states.
This KIP proposes to compress the three data types to reduce the total disk size, thereby making the node operation more efficient and affordable.

## Specification

### Compression
A node periodically compresses storage in a background thread and removes the original data once it is confirmed safe to do so.
Data is compressed in units called &quot;chunks&quot;. Selecting a good chunk size is described in [Appropriate Chunk Size](#rationale).

### Decompression
Decompression is required when a node receives a query (header, body, or receipts).
The node first searches for the corresponding data in the original database.
If the data is not found, it locates the decompressed chunk and performs the decompression. Finding a corresponding chunk is O(1) as we store chunk with a key `to-from`.
Below shows an example how to store and retreive a chunk.
```
[from-to] --&gt; [to-from]
1001-2000 --&gt; 2000-1001
2001-3000 --&gt; 3000-2001
3001-4000 --&gt; 4000-3001
```
If a header 2551 is queried, the key-value iterator immediately hits the `3000-2001` key becuase of the ascending order iteration.
Once found and decompresse it, the decompressed chunk is cached for later use.

### Retention
The compression feature can retain uncompressed data for at least N retention blocks, enabling fast retrieval without decompression overhead.

### Evaluation
Evaluation on a [state-migrated mainnet (as of 2024-12-02)](https://packages.kaia.io/mainnet/chaindata/) showed a significant reduction in its disk size. Body and receipts showed high compression rates thanks to their high data repetition.

| Data | Uncompressed (GB) | Compressed (GB) | Reduction (%) |
| ------- | ------- | ------- | ------- |
| Header | 410 | 261 | 36.3% |
| Body | 1100 | 267 | 75.7% |
| Receipts | 1600 | 179 | 88.8% |
| Others | 1100 | 1100 | 0% |
| Total | 4200 | 1737 | 57.21% |

It took 3 days for background compression threads to complete.

## Rationale

### Appropriate Chunk Size
An optimal chunk size should balance a high compression rate with minimal decompression overhead.
Determining the ideal chunk capacity remains an open problem. However, our experiments suggest that a 1MB chunk size offers a favorable trade-off.

![compression-size](/assets/kip-237/chunk-selecting-compression.png)
![decompression](/assets/kip-237/chunk-selecting-decompression.png)

Currently, the chunk size is fixed at 1MB and cannot be configurable.

### Compression Algorithm
The default algorithm selected for compression and decompression is [Zstd](https://github.com/klauspost/compress).
Zstd demonstrates [strong performance compared to other schemes](https://docs.google.com/spreadsheets/d/1nuNE2nPfuINCZJRMt6wFWhKpToF95I47XjSsc-1rbPQ/edit?gid=1088551794#gid=1088551794).
We tested all [available encoder options](https://github.com/klauspost/compress/blob/v1.17.11/zstd/encoder_options.go#L146) but found no significant differences. The default mode remains fixed.

## Backwards Compatibility
An existing database can be compressed without node shutdown.
APIs querying the historic blocks, transactions, and receipts must work correctly because the data are not lost.

## Implementation
https://github.com/kaiachain/kaia/pull/155

## Copyright
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).

</description>
        <pubDate>Thu, 26 Dec 2024 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-237</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-237</guid>
      </item>
    
      <item>
        <title>EXTCODEHASH opcode for Kaia</title>
        <category>Core</category>
        
          <comments>https://github.com/kaiachain/kips/issues/38</comments>
        
        <description>## Simple Summary

Clarifies and aligns the implementation of `EXTCODEHASH` opcode with [EIP-1052](https://eips.ethereum.org/EIPS/eip-1052) and [EIP-2929](https://eips.ethereum.org/EIPS/eip-2929).

## Abstract

This KIP explains the historical implementation differences of `EXTCODEHASH` in Kaia compared to Ethereum. It also specifies the complete alignment with EIPs starting from the Prague hardfork.

## Motivation

The `EXTCODEHASH` opcode in Kaia has had implementation differences from EIPs. These difference was fixed in the Cancun hardfork but require further alignment, particularly regarding gas costs after the Prague hardfork. This KIP aims to provide clear explanation of the changes and ensure complete compatibility with EIPs.

## Specification

### Parameters

| Parameter         | Value                                                                |
| ----------------- | -------------------------------------------------------------------- |
| `ZERO_CODE_HASH`  | `0x0000000000000000000000000000000000000000000000000000000000000000` |
| `EMPTY_CODE_HASH` | `0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470` |

### EIP-1052

The `EXTCODEHASH` opcode according to EIP-1052 should return:

- For non-existent accounts: `ZERO_CODE_HASH`
- For accounts with no code: `EMPTY_CODE_HASH`

In Kaia, the `EXTCODEHASH` opcode returns:

| Hardfork      | Non-existent      | No code           | Note                                                     |
| ------------- | ----------------- | ----------------- | -------------------------------------------------------- |
| Before Cancun | `EMPTY_CODE_HASH` | `EMPTY_CODE_HASH` | Wrong EIP-1052 implementation for non-existent accounts. |
| Cancun        | `ZERO_CODE_HASH`  | `EMPTY_CODE_HASH` | Fully compatible with EIP-1052.                          |
| Prague        | `ZERO_CODE_HASH`  | `EMPTY_CODE_HASH` |                                                          |

### EIP-2929

The constant gas cost for `EXTCODEHASH` opcode, which is same as `WarmStorageReadCostEIP2929`, should be 100 after the Kore hardfork which introduced EIP-2929.

In Kaia, the `EXTCODEHASH` opcode has the following gas costs:

| Hardfork    | Constant gas cost | Note                                                     |
| ----------- | ----------------- | -------------------------------------------------------- |
| Before Kore | 400               | `ExtcodeHashGasConstantinople`                           |
| Kore        | 100               | Brought EIP-2929.                                        |
| Cancun      | 400               | Fixed `EXTCODEHASH`, but accidentally shadowed EIP-2929. |
| Prague      | 100               | Fully compatible with EIP-2929.                          |

## Backwards Compatibility

The fix of gas cost for `EXTCODEHASH` opcode is backwards incompatible since the Prague hardfork. Note that the EIP-1052 implementation was already fixed in the Cancun hardfork.

## References

[EIP-1052](https://eips.ethereum.org/EIPS/eip-1052)
[EIP-2929](https://eips.ethereum.org/EIPS/eip-2929)

## Copyright

Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
</description>
        <pubDate>Fri, 27 Dec 2024 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-239</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-239</guid>
      </item>
    
      <item>
        <title>Transaction Bundle</title>
        <category>Core</category>
        
          <comments>https://devforum.kaia.io/t/discussion-on-kip-245/8074</comments>
        
        <description>## Abstract

This KIP introduces a transaction bundling mechanism that enables all-or-nothing atomicity on multiple transactions, provides timeout exemption, and facilitates injection of proposer-signed transactions.

## Motivation

The upcoming features in Kaia require a systematic approach that supports the following:

- Atomicity of multiple transactions and removal of transactions on failure,
- Timeout exemption to enhance atomicity,
- Injecting transactions from proposers.

## Specification

### Bundle properties

Transaction bundling has three properties: atomicity, timeout exemption, and proposer injection.

#### Atomicity

A transaction bundle enables them to be executed atomically, meaning that all transactions within the bundle are processed together as a single unit. This atomicity ensures that either all transactions in the bundle are successfully executed, or none of them are included in the block —- an all-or-nothing guarantee.

```
bundle = [ Tx1, Tx2, Tx3 ]
if all succeeded:
  insert Tx1, Tx2, Tx3 to block.
else: # any failed
  discard Tx1, Tx2, Tx3.
```

#### Timeout Exemption

Ethereum enforces a per-block gas limit, while Kaia enforces a per-block execution time limit (currently set as 250ms).
The timer starts when the proposer starts executing transactions.
If the total execution time reaches 250ms while a transaction is being processed, the execution halts immediately and that transaction, along with any remaining transactions, is discarded.

A transaction bundle is exempt from timeout restrictions, ensuring that all transactions within the bundle are fully processed.

```
bundle = [ Tx1 Tx2 Tx3 ]
if timeout occurred during execution of the bundle:
  defer the timeout handler until Tx3 has finished executed
```

#### Proposer injection

Block proposers can reorder transactions to generate bundles and inject _transaction generators_ into the bundle during its creation. The rationale is that, after a bundle rollback, the nonces of proposer-generated transactions must be updated. A transaction generator facilitates _lazy transaction generation_, allowing transactions to be created dynamically just before execution.

```
bundle = [ G1 Tx1 Tx2 Tx3 G2 ]
Before executing G1 and G2, execute `G1(nonce)` and `G2(nonce)` respectively to get actual transactions
```

#### TargetTxHash

Target transaction exists in a bundle to indicate the position of the bundle in the block. If `TargetRequired` is false, the `TargetTxHash` can be updated to a new transaction. But when `TargetRequired` is true, the bundle must be executed only:

1. After the target transaction.
2. If the target transaction has `ReceiptStatusSuccessful` status.

If both is not satisfied, the bundle must be discarded.

### Builder Module

#### Module Interface

The module consists of utility pure functions. Data structures are defined as follows:

```
type TxGenerator func(nonce uint64) (*types.Transaction, error)

type TxOrGen struct {
	concreteTx  *types.Transaction
	txGenerator TxGenerator
	Id          common.Hash
}

type Bundle struct {
	// each element can be either *types.Transaction, or TxGenerator
	BundleTxs []*TxOrGen

	// BundleTxs is placed AFTER the target tx. If empty hash, it is placed at the very front.
	TargetTxHash common.Hash

  // If true, the bundle must be executed after the target tx.
  TargetRequired bool
}
```

We define the user interface of the bundling feature (e.g., Gasless module):

```
interface TxBundlingModule {
    // The function finds transactions to be bundled.
    // New transactions can be injected.
    // returned bundles must not have conflict with `prevBundles`.
    // `txs` and `prevBundles` is read-only; it is only to check if there&apos;s conflict between new bundles.
    ExtractTxBundles(txs []*types.Transaction, prevBundles []*Bundle) []*Bundle
}
```

#### Bundle Generation Logic

Each module’s bundling mechanism can be pipelined.
Each module must ensure that it does not disrupt the previous bundle (`prevBundles`).
Specifically,

- Each module must ensure that a transaction does not belong to more than two bundles.
- Each module must ensure that only one bundle has `TargetRequired` set to true with same `TargetTxHash`.
- Each module must select consecutive transactions from the arrayified transaction list. If `BundleTxs` only contains generator, the `TargetTxHash` can be any transaction.
- Each module can only append transactions to `prevBundles`.
- Each module must not change `TargetTxHash` of `prevBundles`.

```
// work.ApplyTransactions()

# txs are sorted by price and nonce and timestamp by arrayifying `txs *types.TransactionsByPriceAndNonce`
txs = arrayify(txs) # [ tx1 tx2 ... ]
bundles = []
modules = [ m1 m2 ... ] # modules that implement TxBundlingModule

for module in modules:
  bundles.append(module.ExtractTxBundles(txs, bundles))
builder.IncorporateBundleTx(txs, bundles) []tx
```

```
arrayified_txs = [ tx1 tx2 tx3 tx4]

bundles = [ bundle{BundleTxs=[tx3, tx4], TargetTxHash=tx1, TargetRequired=false} ] // not allowed since there&apos;s a gap between tx1 and tx3
bundles = [ bundle{BundleTxs=[tx2, tx4], TargetTxHash=tx1, TargetRequired=false} ] // not allowed since there&apos;s a gap between tx2 and tx4
```

For example, if Gasless module and Auction module are involved in generating bundles:

```
unsorted txs: [ 2 4 T 1 3 A S ]
- A: GaslessApproveTx
- S: GaslessSwapTx
- T: AuctionTargetTx
- LG: LendTxGenerator
- BG: BackrunTxGenerator

bundles = []

   after executing gasless.ExtractTxBundles(txs, bundles)

bundles = [ bundle{BundleTxs=[LG, A, S], TargetTxHash=3, TargetRequired=false} ]

   after executing auction.ExtractTxBundles(txs, bundles)

bundles = [ bundle{BundleTxs=[LG, A, S], TargetTxHash=3, TargetRequired=false},
            bundle{BundleTxs=[BG2],       TargetTxHash=S, TargetRequired=true},
            bundle{BundleTxs=[BG1],       TargetTxHash=T, TargetRequired=true},
          ]

txs after incorporating bundles:     [ 1 2 T [ BG1 ] 3 [ LG A S ] [ BG2 ] 4 ]
```

When there&apos;re multiple bundles with same `TargetTxHash`, the bundle with `TargetRequired` set to true must be executed after the target tx, and the other bundle&apos;s must be correctly ordered. Currently, the auction module is the only module that sets `TargetRequired` to true.

```
txs = [ 1 2 3 A S 4]

In the Auction module, there&apos;s a BG targeting 3.

bundles = []

  after executing gasless.ExtractTxBundles(txs, bundles)

bundles = [ bundle{BundleTxs=[LG, A, S], TargetTxHash=3, TargetRequired=false} ]

  after executing auction.ExtractTxBundles(txs, bundles)

bundles = [ bundle{BundleTxs=[LG, A, S], TargetTxHash=3, TargetRequired=false},
            bundle{BundleTxs=[BG],       TargetTxHash=3, TargetRequired=true},
          ]

  after coordinating target tx of bundles

bundles = [ bundle{BundleTxs=[BG],       TargetTxHash=3, TargetRequired=true},
            bundle{BundleTxs=[LG, A, S], TargetTxHash=BG, TargetRequired=false}, // BG becomes the target tx
          ]

txs after incorporating bundles:     [ 1 2 T 3 [ BG ] [ LG A S ] 4 ]
```

### Handling Bundle Failure

#### Rollback

Originally, a snapshot is generated before executing each transaction.
For bundles, we take a snapshot before executing a bundle, and if any transaction fails, we recover the state to the last snapshot.

```
// work.commitBundleTransaction(bundle, ...)
preBundleSnapshot = env.state.Snapshot()
for tx in bundle:
  ApplyTransaction(tx)
  if tx failed:
    RevertToSnapshot(preBundleSnapshot)
    return
append all txs and receipts in bundle to env.txs and env.receipts
```

#### Pop transactions

Currently, if transaction execution fails, there are cases when all dependent transactions are removed (from `ApplyTransactions` loop) as they cannot be executed in this block.
Such cases include gas limit reached, too high nonce, unsupported transaction type, etc.
Likewise, if a transaction in a bundle fails to execute, all dependent transactions must be removed.
Transactions are dependent if they have the same sender, or they are in the same bundle.

An example of chained pops:

```
txs     = [ G1 A.T1 B.T1 A.T2 B.T2 C.T1 ]
bundle  = [ G1 A.T1 ]
bundle2 = [ B.T1 A.T2 ]
bundle3 = [ B.T2 C.T1 ]

On A.T1 revert, initially two transactions are popped (`pop(txs, 2)`), followed by recursively popping dependents, which eventually results in: txs = [ ]
```

### Delaying Timeout

EVM cancel due to timeout must be delayed if the transaction currently being executed is in a bundle.

## JSON-RPC API

### `kaia_sendRawTransactions`

The following JSON-RPC method for the Kaia node should be added.

- Description: Sends multiple transactions at once.
- Parameters:
  - `txList` - a list of RLP-encoded transactions (e.g., `[RlpEncodedTx1, RlpEncodedTx2]`).
- Returns:
  - `hashList` - a list of transaction hashes.

## Rationale

### Rollback cost

State rollback costs `O(N)`, where `N` is the number of state writes.
Hence, the cost of bundle rollback is equivalent to the sum of rollback costs for each individual transaction.
As a result, this does not negatively impact the block generation performance.

### Indefinite Timeout

Due to the timeout exemption, there is a concern that a transaction in a bundle could run indefinitely, potentially causing a consensus delay.
However, transactions within a bundle are standardized, ensuring reasonably predictable operations.
Also, Kaia has computation cost limit per transaction which limit the execution time of transaction, ensuring it does not run indefinitely.
Each module must ensure that transactions included in a bundle are not resource-intensive.

### Handling Conflicts

Bundles can have conflicts when (1) a transaction belongs to multiple bundles, or (2) multiple bundles have the same `TargetTxHash`. Defining how to handle conflicts is out of scope.
There can be some options, such as disregarding all bundles if conflict is found, or introducing priority to bundles to resolve conflicts.

## Backward Compatibility

### Transaction Ordering

Generation of a bundle inevitably leads to transaction reordering.
Although transactions are initially sorted by price, nonce, and timestamp, the bundling process can alter this order.
During bundling, transactions may be reordered to meet specific bundle constraints, may be removed, or even new transactions may be injected.
It is important to note that this does not violate KIP-162. Similar to EIP-1559, the ordering of transactions based on the gas tip is more of a recommendation than a requirement.

## Test Cases

### Bundle Conflict

- Duplicate `TargetTxHash`: two bundles have the same `TargetTxHash` and `TargetRequired` is true.

```
txs = [ 1 2 3 4 ]
  module1.ExtractTxBundles() -&gt; {[ 1 2 ], target=empty, target_required=false}
txs = [ [ 1 2 ] 3 4 ]
  module2 wants to make a bundle {[ BG1 ], target=empty, target_required=true} -&gt; allowed
txs = [ [ 1 2 ] 3 4 ]
  module3 wants to make a bundle {[ BG2 ], target=empty, target_required=true} -&gt; not allowed

  after coordinating target tx of bundles

bundles = [ bundle{BundleTxs=[BG1], target=empty, target_required=true},
            bundle{BundleTxs=[1, 2], target=BG1, target_required=false},
          ]

txs after incorporating bundles:     [ [ BG1 ] [ 1 2 ] 3 4 ]
```

- Transaction Overlap Across Bundles: a transaction belongs to more than one bundle.

```
txs = [ 1 2 3 4 ]
  module1.ExtractTxBundles() -&gt; {[ 1 2 ], target=empty}
txs = [ [ 1 2 ] 3 4 ]
  module2 wants to make a bundle {[ 3 4 ], target=1} -&gt; not allowed
txs = [ [ 1 2 ] 3 4 ]
  module3 wants to make a bundle {[ 3 4 ], target=2} -&gt; allowed
txs = [ [ 1 2 ] [ 3 4 ] ]
```

## References

- [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559)

## Copyright

Copyright and related rights are waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
</description>
        <pubDate>Mon, 17 Feb 2025 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-245</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-245</guid>
      </item>
    
      <item>
        <title>Gasless Transaction</title>
        <category>Core</category>
        
          <comments>https://devforum.kaia.io/t/discussion-on-kip-247/8089</comments>
        
        <description>## Abstract

This KIP introduces Gasless Transaction (GaslessTx) where users can swap whitelisted fungible tokens into KAIA without possessing any KAIA.

## Motivation

For users who have onboarded onto KAIA from another blockchain via a bridge, it is possible that they do not possess any KAIA tokens and hold only fungible tokens from the bridged assets.
These users will be unable to perform any on-chain actions unless they acquire KAIA through alternative channels.
This limitation effectively restricts their ability to engage with decentralized applications, execute transactions, or participate in any protocol activities within the KAIA ecosystem.

To enhance the user experience for those, this KIP introduces a concept of gasless transaction (GaslessTx) where transactions with a specific pattern is recognized as the GaslessTx.
Upon detection of GaslessTx, the block proposer may lend the user KAIA to facilitate the GaslessTx execution.
A gasless swap from Token to KAIA shall pay back the lent amount.

## Specification

### Definition of GaslessTx

There are two types of GaslessTx: GaslessApproveTx, and GaslessSwapTx.

#### GaslessApproveTx

A GaslessApproveTx must meet all the following conditions to be inserted to `txpool.queue`:

- A1: `GaslessApproveTx.to` is a whitelisted ERC-20 token.
- A2: `GaslessApproveTx.data` is `approve(spender, amount)`.
- A3: `spender` is a whitelisted `GaslessSwapRouter`.
- A4: `amount` is MaxUint.
- A5: `nonce` is `getNonce(tx.from)`.

(Note that above statements can be verified solely through a static validation of a transaction.)

A GaslessApproveTx should meet all the following conditions to be promoted to `txpool.pending`:

- AP1: GaslessApproveTx is followed by a GaslessSwapTx of the same sender and the `GaslessSwapTx` can be promoted to `txpool.pending`.

#### GaslessSwapTx

A `GaslessSwapTx` must meet all the following conditions to be inserted to `txpool.queue`:

- S1: `GaslessSwapTx.to` is a whitelisted `GaslessSwapRouter`.
- S2: `GaslessSwapTx.data` is `swapForGas(token, amountIn, amountOut, amountRepay, deadline)`.
- S3. `token` is a whitelisted ERC20 token.

(Note that above statements can be verified solely through a static validation of a transaction.)

A `GaslessSwapTx` must meet all the following conditions to be promoted to `txpool.pending`:

- SP1: (If GaslessApproveTx exists in `txpool.queue`) `GaslessApproveTx.to=token`.
- SP2: (If GaslessApproveTx exists in `txpool.queue`) `GaslessApproveTx.data.amount&gt;=amountIn`.
- SP3: Nonce is the correct value.
  - (If GaslessApproveTx exists in `txpool.queue`) `GaslessApproveTx.nonce+1=tx.nonce=getNonce(tx.from)+1`.
  - (otherwise) `tx.nonce=getNonce(tx.from)`.
- SP4: `amountRepay` is the correct value.
  - (If GaslessApproveTx exists in `txpool.queue`) `amountRepay = CalcRepayAmount(GaslessApproveTx, GaslessSwapTx)`.
  - (otherwise) `amountRepay = amountRepay(GaslessSwapTx)`.
  - `amountRepay(GaslessApproveTx, GaslessSwapTx) = V1 + V2 + V3` and `amountRepay(GaslessSwapTx) = V1 + V3` where
    - `V1 = LendTx.Fee() = SwapTx.GasPrice() * 21000`
    - `V2 = ApproveTx.Fee()` (if exists)
    - `V3 = SwapTx.Fee()`

While GaslessApproveTx must be followed by with GaslessSwapTx in order to be promoted, GaslessSwapTx can exist by itself.

### Definition of LendTx

Upon detection of a GaslessTx (either GaslessApproveTx or GaslessSwapTx), the block proposer shall inject a transaction, denoted by LendTx, which transfers KAIA to the GaslessTx sender. LendTx must meet all the following requirements:

- L1: `LendTx.type` is 0x7802, a Ethereum dynamic fee transaction type.
- L2: `LendTx.from` is the proposer itself.
- L3: `LendTx.to` is `GaslessSwapTx.from`
- L4: `LendTx.value` is `amountLend`
  - `amountLend` is `V2 + V3` (from aforementioned `amountRepay` definition).

### GaslessSwapRouter

GaslessSwapRouter is a smart contract responsible for token swap and repayment. It supports only single-hop swaps through a predetermined DEX contract. It must support the following interface:

```solidity
interface IKIP247 {
    // `factory` is to check if the token-WKAIA pair has been deployed. (`factory.getPair(token1, WKAIA)`)
    // `router` is for swap (`router.swapExactTokensForETH(...)`).
    struct DEXInfo {
        address factory;
        address router;
    }

    function swapForGas(address token, uint256 amountIn, uint256 minAmountOut, uint256 amountRepay, uint256 deadline) external;
    function addToken(address token, address factory, address router) external;
    function removeToken(address token) external;

    function claimCommission() external;
    function updateCommissionRate(uint256 _commissionRate) external;

    // view functions
    function dexAddress(address token) external view returns (address dex);
    function getAmountIn(address token, uint amountOut) external view returns (uint amountIn);
    function getSupportedTokens() external view returns (address[] memory);
}
```

#### swapForGas
`swapForGas()` must ensure that all the following conditions are satisfied:

- R1: Sender has enough tokens. That is, `token.balanceOf(msg.sender) &gt;= amountIn`.
- R2: Token is whitelisted and has a corresponding DEX address other than zero.
- R3: `amountReceived &gt;= minAmountOut &gt;= amountRepay` where `amountReceived` is the swap output.

`swapForGas()` must distribute the swap output (`amountReceived`) as follows:

- The block proposer (`block.coinbase`) receives `amountRepay`.
- User receives `(amountReceived - amountRepay) * (1 - commissionRate)`.
- The commission `(amountReceived - amountRepay) * commissionRate` is stored in the contract for later claim.

#### addToken
`addToken()` allows the contract owner to whitelist a token for gasless swaps.

`addToken()` must ensure all the following conditions are satisfied:
- All addresses (token, factory, router) must be non-zero.
- The token must not already be supported.
- The factory must have a valid pair between the token and WKAIA.

#### removeToken
`removeToken()` allows the contract owner to remove a previously whitelisted token.

`removeToken()` must ensure all the following conditions are satisfied:
- The token must be currently supported.

#### claimCommission
`claimCommission()` allows the contract owner to withdraw accumulated commission fees.

`claimCommission()` must ensure all the following conditions are satisfied:
- There must be a non-zero balance in the contract.

#### updateCommissionRate
`updateCommissionRate()` allows the contract owner to set the commission rate for gasless swaps.

`updateCommissionRate()` must ensure all the following conditions are satisfied:
- The commission rate must be between 0 and 10000 (0% to 100%).

#### dexAddress
`dexAddress()` returns the factory address associated with a supported token.

`dexAddress()` must ensure all the following conditions are satisfied:
- The token must be supported.

#### getAmountIn
`getAmountIn()` calculates the required input amount of tokens to receive a specific amount of KAIA.

`getAmountIn()` must ensure all the following conditions are satisfied:
- The token must be supported.

#### getSupportedTokens
`getSupportedTokens()` returns a list of all currently supported tokens for gasless swaps.

### TxPool Policy Updates

#### GaslessTx Validation (validateTx)

To prevent GaslessTx to be discarded, the validation logic at `txpool.queue` insertion needs to detect GaslessTx and skip the account balance check.

```
// txpool.validateTx()
if ...
else: // if tx is NOT feeDelegatedTx
    if senderBalance.Cmp(tx.Cost()) &lt; 0:
        if tx is GaslessTx:
            validation ok
        else:
            insufficientFund error
```

#### Promoting GaslessTx (promoteExecutables)

GaslessApproveTx cannot be executed unless the corresponding GaslessSwapTx from the same sender is already in the transaction pool.
Conversely, if the token has not been approved, GaslessSwapTx cannot be executed before GaslessApproveTx.
In such cases, both transactions need to be promoted simultaneously.

```
// txpool.promoteExecutables()
if tx is GaslessApproveTx:
    assert tx.nonce == getNonce(tx.from)
    if GaslessSwapTx is in queue:
        assert GaslessSwapTx.nonce == getNonce(tx.from) + 1
        promote tx and GaslessSwapTx to pending
    else:
        push tx to queue

if tx is GaslessSwapTx:
    if GaslessApproveTx is in queue:
        assert tx.nonce == getNonce(tx.from) + 1
        assert GaslessApproveTx.nonce == getNonce(tx.from)
        promote tx and GaslessApproveTx to pending
    else if tx.nonce == getNonce(tx.from):
        promote tx to pending
    else:
        push tx to queue
```

#### Transaction Bundling

This KIP implements `TxBundlingModule` specified in [KIP-245](https://kips.kaia.io/KIPs/kip-245). These are the possible bundles:

- LendTx - GaslessApproveTx - GaslessSwapTx
- LendTx - GaslessSwapTx

Bundles from this module are placed at the position of each GaslessSwapTx.

```
ExtractTxBundles(txs, prevBundles):
    if GaslessTx is disabled:
        return nothing

    filter ApproveTx, SwapTx from txs
    move ApproveTx (if exists) before SwapTx
    inject LendTx before ApproveTx if exists, otherwise before SwapTx
```

### Accepted tokens

Fungible tokens compliant with ERC-20 are supported.
Non-ERC-20 compliant tokens may or may not be supported depending on the implementation.
However, Fee-on-Transfer (FoT) tokens are not supported because of the possibility of swap failure.

## Rationale

### Stateless transaction validations

The existing validation logics in transaction pool (`validateTx()`) ensures that the transaction sender has sufficient KAIA to cover the gas fee.
However, this KIP does not propose a comparable validation mechanism for GaslessTxs such as ERC-20 token balance checks.
Instead, it suggests stateless transaction validation logics, specifically A{1-4}, AP1, S{1-3}, SP{1-3}.

While the computational overhead of performing stateful validations via EVM calls may not be significant, such validations inherently lack reliability. They may become obsolete and ineffective since the state database can change between transaction validation and execution.
To address this, this KIP relies on [KIP-245](https://kips.kaia.io/KIPs/kip-245) where all transactions including LendTx will be excluded from the block when GaslessTx reverts.
Thus, this KIP proposes the most basic format checks of GaslessTx in transaction pool.

### Disabling GaslessTx

If many consensus nodes disabled GaslessTx feature, users may experience increased transaction latency.
However, as long as there are still nodes supporting GaslessTx, the transactions will eventually be executed.
Also, because this feature is enabled by default, few consensus nodes are expected to disable it.

### Standalone GaslessApproveTx

AP1 may not behave as intended under certain edge conditions.
However, even in such cases, no risk or negative consequences are introduced.
The standalone GaslessApproveTx will not be bundled as a gasless transaction, indicating that it will be treated as a regular transaction and will not have impact on system integrity.
If the sender does not have any KAIA balance, the transaction will not be executed.

## Backward Compatibility

This does not affect the backward compatibility because this does not involve hardfork.

## References

- [KIP-245](https://kips.kaia.io/KIPs/kip-245)

## Copyright

Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
</description>
        <pubDate>Tue, 18 Feb 2025 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-247</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-247</guid>
      </item>
    
      <item>
        <title>Slot-Based Auction on Kaia</title>
        <category>Core</category>
        
          <comments>https://devforum.kaia.io/t/discussion-on-kip-249-slot-based-auction-on-kaia/8075</comments>
        
        <description>## Simple Summary

This KIP defines a **slot-based** mechanism in Kaia, where the **auction-winning call** is placed immediately after its **target transaction** to capture backrunning MEV. Searchers deposit funds in a vault, ensuring coverage for **bid + gas**, and an **AuctionEntryPoint contract** handles bid payment, execution of backrun logic, and gas refunds atomically. Collected fees are routed to a governance-controlled vault, providing transparency and flexibility in distributing MEV proceeds.

## Abstract

MEV (Maximal Extractable Value) commonly arises when a **backrun** exploits state changes immediately after a target transaction. This KIP enforces a **slot-based** adjacency, ensuring the winning call is placed directly after its target in a block, minimizing reorder opportunities. Searchers maintain a deposit in a vault, guaranteeing coverage for both **bid** and **gas**. They submit off-chain bids to an Auctioneer, and the final transaction is crafted and signed by the block **proposer**, enforcing adjacency. An **AuctionEntryPoint** contract handles the atomic collection of bids, execution of backrun logic, and gas reimbursement in a single transaction. Any fees collected are routed to a **governance-controlled** `AuctionFeeVault`, enabling Kaia to capture and distribute MEV proceeds transparently.

## Motivation

Currently, the MEV on Kaia network has the following issues:

1. The profit generated from MEV is centralized.
2. The spamming transactions inefficiently congest the network.

Also, during transition to the permissionless network, we’d expect these issues will have even more impact. To address these, we need a structured MEV approach so we can enhance user experience and decentralization while maintaining a sustainable and stable network.

## Specification

## Key Mechanisms

1. **Hidden Bidding**

   - Searchers privately submit bids to the off-chain Auctioneer.
   - The Auctioneer chooses the highest valid bid and sends the auction result to the validators.

2. **Slot-Based Adjacency**

   - Ensures if a target transaction is placed at index _i_, the backrun (`AuctionEntryPoint.call`) call is at _i+1_.
   - Reduces reorder exploits by preventing free-form placement of backrun transactions elsewhere in the block.
   - If no target transaction is found, the backrun transaction will be dropped.

3. **Bundle**

   - The winner’s bid will be signed by proposer and bundled based on the KIP-245.
   - Bypasses the block generation limit during the execution of BidTx.
   - Discard the bundle if it encounters revert during the execution of BidTx to prevent balance loss of proposer.

4. **Deposit &amp; Cooldown**

   - A dedicated `AuctionDepositVault` requires each searcher to lock up enough funds for their bid and gas.
   - A cooldown on withdrawals prevents immediate removal of deposits, enforcing fund coverage for the bids.

5. **Governance-Controlled Fees**
   - Proceeds from winning bids go into an `AuctionFeeVault`.
   - Kaia’s on-chain governance decides the split (e.g., treasury vs. proposer) and any further uses of these funds.

### Terminology

| Term                    | Description                                                                                             |
| :---------------------- | :------------------------------------------------------------------------------------------------------ |
| **Slot**                | The position immediately following a target transaction in a block’s transaction list.                  |
| **Target Tx**           | A user transaction that triggers a backrun opportunity.                                                 |
| **Auctioneer**          | Off-chain entity coordinating hidden bids among searchers and picking the winning call.                 |
| **Searcher**            | A participant who provides a deposit and hidden bid, including the target reference, to the Auctioneer. |
| **AuctionEntryPoint**   | A contract that handles bid payment, execution of backrun logic, and gas refunds atomically.            |
| **AuctionDepositVault** | A contract storing each searcher’s deposit, allowing a two-step withdrawal.                             |
| **AuctionFeeVault**     | A contract capturing and distributing the bid from searcher                                             |

### Parameters

| Parameter              | Description                                                                                                                                           | Value        |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ |
| `BidTxMaxDataSize`     | The maximum data size for the bid data                                                                                                                | `64KB`       |
| `BidTxMaxCallGasLimit` | The maximum gas limit for the backrun logic                                                                                                           | `10_000_000` |
| `GAS_BUFFER`           | The buffer to estimate the gas consumption. The total gas usage of bid transaction before execution is estimated as `bid.call_gas_limit + GAS_BUFFER` | `200_000`    |

---

### Auction Rules

#### Hidden Bidding

- Searchers sign their intended `bid` **offline**.
- The Auctioneer selects the **highest valid bid**, then sends the winning bid to the proposer for forming **one transaction** calling `AuctionEntryPoint` in the **final block**.

#### Searcher Bid Limit

- Each searcher may place **one winning valid bid per block**.
- Any additional bids submitted by the same searcher address for the same block will be disregarded by the Auctioneer and proposer.

#### Slot Concept

- If the **targetTx** is at index _i_, the `AuctionEntryPoint` call is placed at _i+1_.

#### Target and Backrun Adjacency

- The backrun call is at index _i+1_ if the targetTx is at _i_.
- If the proposer **does not have** the targetTx on the same block of the backrun tx, it removes the backrun Tx automatically.
- If the target transaction has been executed without revert, the bid will be paid regardless of execution result of backrun logic.

#### Early Deadline

- The **block proposer** will enforce an **early deadline** for normal user transactions to ensure the duration of an auction.
- An early deadline is the cutoff point for accepting transactions.
- After that, only the **winning** backrun call and its target can still be appended.

```py
# Filters out the late non-target transactions exceed earlyDeadline.
def filter_tx(txs, bidTargetTxs, earlyDeadline):
    txs[:] = [tx for tx in txs if not (tx.time &gt; earlyDeadline and tx not in bidTargetTxs)]
```

---

### Auction Result Validation

The auction result will be `BidDetails`.

```py
auction_type_hash = keccak256(&quot;AuctionTx(bytes32 targetTxHash,uint256 blockNumber,address sender,address to,uint256 nonce,uint256 bid,uint256 callGasLimit,bytes data)&quot;)
```

Please note that it’s not the transaction, but the bid.

```py
@dataclass
class BidDetails:
	target_tx_hash: Hash32 # Equivalanet to common.Hash in Go
	block_num: int # The target block number
	sender: str # The searcher address who has enough deposit balances
	nonce: int # The nonce of searcher in the AuctionEntryPoint, not blockchain account&apos;s nonce
	to_addr: str # The target address of backrun logic
	call_gas_limit: bytes # The gas limit of backrun logic
	call_data: bytes # The call data of backrun logic
	bid_amount: bytes # The bid amount in Kei
	from_signature: bytes # ECDSA.sign the hash typed data V4 with auction_type_hash based on EIP-712 by searcher&apos;s private key.
	auc_signature: bytes # ECDSA.sign the from_signature based on EIP-191 by auctioneer&apos;s private key
```

The EIP-712 domain and types for the `from_signature` are as follows:

```py
# Domain
domain = {
    &quot;name&quot;: &quot;KAIA_AUCTION&quot;,
    &quot;version&quot;: &quot;0.0.1&quot;,
    &quot;chainId&quot;: chain_id,
    &quot;verifyingContract&quot;: entrypoint_addr,
}

# Types
types = {
    &quot;AuctionTx&quot;: [
        {&quot;name&quot;: &quot;targetTxHash&quot;, &quot;type&quot;: &quot;bytes32&quot;},
        {&quot;name&quot;: &quot;blockNumber&quot;, &quot;type&quot;: &quot;uint256&quot;},
        {&quot;name&quot;: &quot;sender&quot;, &quot;type&quot;: &quot;address&quot;},
        {&quot;name&quot;: &quot;to&quot;, &quot;type&quot;: &quot;address&quot;},
        {&quot;name&quot;: &quot;nonce&quot;, &quot;type&quot;: &quot;uint256&quot;},
        {&quot;name&quot;: &quot;bid&quot;, &quot;type&quot;: &quot;uint256&quot;},
        {&quot;name&quot;: &quot;callGasLimit&quot;, &quot;type&quot;: &quot;uint256&quot;},
        {&quot;name&quot;: &quot;data&quot;, &quot;type&quot;: &quot;bytes&quot;},
    ]
}
```

The proposer verifies the `BidDetails` in the following order.

1. The `bid.block_num` must be in range of `[currentBlockNumber + 1, currentBlockNumber + allowFutureBlock]`.
   1. If the proposer already started mining of `bid.block_num`, it will be discarded.
2. The `bid.bid_amount` must be greater than 0.
3. The `bid.data` must be less or equal to `BidTxMaxDataSize`.
4. The `bid.call_gas_limit` must be less or equal to `BidTxMaxCallGasLimit`.
5. The `bid.sender` must not be in the winner list of the same block number if the new bid doesn&apos;t have the same target block and hash as the previous bid.
6. The `bid.from_signature` and `bid.auc_signature` must be valid.
   1. The auctioneer address will come from the AuctionEntryPoint contract.

---

### Block Building

The verified bids will wait for the next block mining. When the mining starts, the proposer will place the bid results into block following the [KIP-245](https://kips.kaia.io/KIPs/kip-245).

#### BidTx

The proposer shall inject a transaction denoted by BidTx, which calls the AuctionEntryPoint contract. The BidTx will follow the below rules:

1. BidTx.type is 0x7802, which is the Ethereum Dynamic Fee transaction type.
2. BidTx.to is an AuctionEntryPoint contract.
3. BidTx.value is 0
4. BidTx.data shall be correctly encoded for `AuctionEntyPoint.call`.
5. BidTx.maxFeePerGas, BidTx.maxPriorityFeePerGas shall be the same as those of target tx.
6. BidTx.gasLimit shall be set large enough to cover the gas fee including `Bid.call_gas_limit`.

After building the BidTx, the proposer will make a BidTx bundle based on the KIP-245.

#### BidTx Bundling

After making the BidTx, it will be bundled based on the KIP-245. The bid bundle doesn’t include target tx but single bid tx so that the target tx can’t be delayed/canceled due to bundling. If there&apos;s no target transaction or it&apos;s reverted, the bid will be discarded.

```
Original tx list: [Tx1, Tx2, Tx3, Tx4]
Target tx: Tx2
BidTx bundle: {targetTx: Tx2, bundleTxs: [B2]} // not [Tx2, B2]

Final tx list: [Tx1, Tx2, [B2], Tx3, Tx4]
```

Please note that the proposer doesn’t sign the BidTx yet and it’ll be done right before executing the BidTx. This is because we can’t finalize the nonce of the proposer before executing all previous transactions and bundles when there&apos;re more than one bundle in the same block.

---

### Smart Contract

#### AuctionEntryPoint

The AuctionEntrypoint is a singleton smart contract that implements a function that includes all logic for paying the bid, executing the backrun, and refunding gas. It also manages the address of a valid auctioneer which will be used to verify the auctioneer’s signature. The address of AuctionEntrypoint will be registered in the system registry defined in [KIP-149](https://kips.kaia.io/KIPs/kip-149).

| ![AuctionEntryPoint](/assets/kip-249/flow.png) |
| :----------------------------------------------: |
|         The execution sequence of BidTx          |

The AuctionEntryPoint will verify auctionTx and will return/revert the transaction. Since the BidTx is bundled, the propose won’t pay the gas fee if it’s reverted, otherwise it will pay the gas fee.

```solidity
interface IAuctionEntryPoint {
    /* ========== STRUCT ========== */

    struct AuctionTx {
	     bytes32 targetTxHash;
	     uint256 blockNumber;
	     address sender;
	     address to;
        uint256 nonce;
        uint256 bid;
        uint256 callGasLimit;
        bytes data;
        bytes auctioneerSig;
        bytes searcherSig;
    }

    // @dev Returns the address of current auctioneer.
    function auctioneer() external view returns (address);

    // @dev Returns the current nonce of address.
    function nonces(address) external view returns (uint256);

    // @dev Executes the all bid operations.
    function call(
        AuctionTx calldata auctionTx,
    ) external
}
```

#### AuctionDepositVault

The AuctionDepositVault is a singleton smart contract that implements a function that returns the current deposit balance of a searcher. It’s used when the proposer verifies if a searcher has enough deposit balance to pay all the bids and gasFee.

```solidity
interface IAuctionDepositVault {
    // @dev Returns the current deposit balance of address.
    function depositBalances(address) external view returns (uint256);

    // @dev Deposits KAIA into vault.
    function deposit() external payable;

    // @dev Deposits KAIA for a specific address.
    function depositFor(address, uint256) external;

    // @dev Starts cooldown period to withdraw KAIA.
    function reserveWithdraw() external;

    // @dev Withdraws KAIA after cooldown period.
    function withdraw() external;

    // @dev Takes the bid from the searcher.
    function takeBid(address searcher, uint256 bidAmount) external;

    // @dev Takes the gas fee from the searcher.
    function takeGasFee(address searcher, uint256 gasAmount) external;
}
```

#### AuctionFeeVault

The AuctionFeeVault is a governance-owned smart contract that captures and distributes the bid amounts. After executing each bid, the bidAmount will be sent to AuctionFeeVault.

During the `takeBid` function, the `AuctionFeeVault` will take the bid amount from the searcher and distribute the bid to searcher, proposer according to the each payback rate. The remaining amount will be stored in the `AuctionFeeVault` and can be withdrawn by the owner of the `AuctionFeeVault`.

```solidity
interface IAuctionFeeVault {
    // @dev The maximum payback rate.
    uint256 public constant MAX_PAYBACK_RATE = 10000; // 100%

    // @dev Takes the bid from the searcher.
    // @dev This function will be called by the AuctionDepositVault.
    function takeBid(address searcher) external payable;

    // @dev Withdraws the accumulated bid except the payback amount.
    function withdraw(address to) external;

    // @dev Registers the reward address for the node.
    // @dev One of the admin registered in the CnStaking contract can update the reward address of the validator.
    function registerRewardAddress(address nodeId, address rewardAddr) external;

    // @dev Returns the reward address for the node.
    function getRewardAddr(address nodeId) external view returns (address);

    // @dev Sets the payback rate for the searcher.
    function setPaybackRate(uint256 _paybackRate) external;

    // @dev Sets the validator payback rate for the validator.
    function setValidatorPaybackRate(uint256 _validatorPaybackRate) external;
}
```

The `registerRewardAddress` function is called by the admin registered in the CnStaking contract.

```solidity
    /// @dev Register the reward address for a node
    /// @param nodeId The CN node ID registered as a validator
    /// @param rewardAddr The reward recipient address
    function registerRewardAddress(address nodeId, address rewardAddr) external override {
       /// @dev If there&apos;s no corresponding staking contract, it will revert
       (, address staking, ) = IAddressBook(ADDRESS_BOOK).getCnInfo(nodeId);

       if (!IStaking(staking).isAdmin(msg.sender)) revert OnlyStakingAdmin();

       _nodeIdToRewardAddr[nodeId] = rewardAddr;

       emit RewardAddressRegistered(nodeId, rewardAddr);
    }
```

---

### JSON-RPC API

The following JSON-RPC methods for the Kaia node should be added to receive the auction result from the auctioneer node and share the auction-related data with the auctioneer node.

- `auction_submitBid`
  - The selected winning bid is relayed through this API from the auctioneer to the CN node. Upon receipt, the CN node will validate the bid and try to include a backrun transaction to the block when the CN is proposer.
- `auction_call`
  - Locally execute a transaction without writing to the blockchain. Same as `kaia_call`.
- `auction_subscribe`
  - Starts a subscription to chain events over websocket. Similar to `kaia_subscribe`.
  - Following event types are supported:
    - `logs` returns contract event logs
    - `newHeads` returns newly added block headers
    - `newPendingTransactions` returns new transactions that become pending state. Unlike `eth_subscribe` or `kaia_subscribe` where transaction hashes are returned, `auction_subscribe` returns the full transaction in JSON format.

---

### Auctioneer Node

The auctioneer is the independent node that relays the auction process. It validates the searcher’s bid, selects the winning bidder, and sends it to the validators.
Once the searchers deposit the KAIA into AuctionDepositVault, the auctioneer node will catch the event and recognize the searcher address.
The auctioneer node will implement the following json-rpc APIs for searchers. Please note that the detail of the API usage will be shared with the auctioneer implementation.

- `http://&lt;auctioneer-node-url&gt;/api/v1/auction/send`
  - Input: `{target_tx_hash, target_tx_raw, block_num, sender, nonce, to_addr, call_gas_limit, call_data, bid_amount, from_signature}`
  - Output: {error}

After receiving the bid, the auctioneer verifies the the `Bid` in the following order:

1. Verify if the searcher has no winning bid in the same block.
2. Verify the signature from searcher
3. Verify the `currBlockNum + 1 &lt;= Bid.blockNum &lt;= currBlockNum + 2`.
4. Verify the `Bid.bidAmount &gt;= minBidAmount`.
   - The auctioneer will check the minimum bid amount, while the proposer will check the `Bid.bidAmount &gt; 0`.
5. Verify the `Bid.nonce == AuctionEntryPoint.nonce`.
6. Verify the `Bid.data &lt;= BidTxMaxDataSize`.
7. Verify the `Bid.callGasLimit &lt;= BidTxMaxCallGasLimit`.
8. Verify if the searcher has enough deposit balance to pay the `bidAmount` and `gasFee`.
   - The `gasFee` is estimated by summing up the `Bid.callGasLimit` and `GAS_BUFFER` that covers the gas fee for the `BidTx`.
   - The `effetiveGasPrice` is estimated from the target transaction&apos;s `maxFeePerGas` and `maxPriorityFeePerGas`.

If the validation passes, the auctioneer will insert the bid into auction.

#### Early Deadline Calculation

The early deadline secures the enough auction time for all transactions, but currently it&apos;s not enforced at the consensus level. But, the auctioneer will report all activities of the validators to the public. From the reported data, the ecosystem participants (e.g., searchers, validators, users) can detect the malicious validator who doesn&apos;t follow the early deadline.

We can roughly calculate the early deadline by using the arrival time and timestamp of the parent block. This is possible thanks to Kaia&apos;s constant block producing interval; 1 second.

```
# edOffset: Early deadline offset (150ms)
# parentBlockTime: Timestamp of the parent block
# parentBlockArrivalTime: Arrival time of the parent block
# idealTime: Ideal time of the new block mining

def get_early_deadline(parentBlock):
    parentBlockTime = parentBlock.timestamp
    parentBlockArrivalTime = parentBlock.arrivalTime
    idealTime = parentBlockTime + 1s

    if idealTime &gt; parentBlockArrivalTime:
        return idealTime - edOffset
    else:
        return parentBlockArrivalTime - edOffset
```

Of course, it&apos;s affected by the network condition so any repeated, outlier, or suspicious pattern can be detected by the ecosystem participants.

---

### Governance

#### Fee Distribution

- Kaia governance configures how `bidAmount` is split (e.g., partial to the treasury or proposer).
- Withdrawals from `AuctionFeeVault` occur solely through **governance proposals** (see [KIP-81](https://kips.kaia.io/KIPs/kip-81)).

---

### Rationale

#### Hidden Auction + Slot Adjacency

- Defends against front-running by placing the backrun call directly after its target, minimizing reorder attacks.

#### BidTxMaxDataSize

- Currently, the maximum data size for rlp-encoded transaction is `128KB` and bid should also respect the limit since it will be used to build the BidTx.
- The `64KB` is enough to cover most of transaction data, while having enough margin for the auction system.

#### BidTxMaxCallGasLimit

- Based on the KIP-245, each bundle shouldn&apos;t be resource-intensive since bundle can ignore the block generation timeout.
- The `BidTxMaxCallGasLimit` limits the gas limit of the backrun logic to prevent the backrun logic from consuming too much gas and resource.
- The initial value is set to `10_000_000`.

#### Early Deadline

- The early deadline secures the enough auction time for all transactions.
- The malicious validator who doesn&apos;t follow the early deadline can be detected by the auctioneer off-chain. Please see the [Security Considerations](#security-considerations) for more details.

#### BidTx Bundling

- Thanks to bundling, the winning bid is guaranteed to be executed if it encounters the block generation limit during the execution. Also, the proposer can prevent any balance loss due to unexpected revert on `BidTx`. Since we impose a **callGasLimit** on the bid logic, the additional processing overhead will have a minimal impact compared to the **bidAmount**.

#### Single EntryPoint Call

- Bid payment, backrun logic, and gas refunds are handled **atomically** in a single `AuctionEntryPoint` transaction, preventing partial failures or inconsistencies.

#### Non-zero Bid

- The bid amount must be greater than 0 to prevent any misuse of the auction system.

#### Singleton AuctionEntryPoint and AuctionDepositVault

- The biggest concern of the auction system is that searchers can bypass the auction system to not pay any bid.
- The AuctionEntryPoint and AuctionDepositVault force searchers to submit correct auction bids by taking the deposit of the searcher once it wins the auction and is correctly ordered in a block.
- Even though the backrun logic was not executed successfully, the searcher forfeits all the bid payment.

#### Minimal On-chain Changes

- Only adjacency enforcement in block-building plus deposit logic in separate contracts. The off-chain Auctioneer coordinates hidden bids and determines the winning searcher.

---

### Security considerations

1. **Front-Running / “Sandwich” Attack**

   - **Feasibility**: Since each searcher is limited to one valid bid per block, a single searcher cannot both front-run and back-run the same transaction in a single block. However, an entity could attempt to coordinate a “sandwich” by using two separate searcher addresses, for example:

   ```
   (target-1)  (bid from searcher A)  (target)  (bid from searcher B)
   ```

   - **Deposit Risk**: Each searcher address must lock up enough deposit to cover its own **bid + gas**. If the target transaction reverts, the deposit for the **front-run slot** (Searcher A) is forfeited.

2. **Malicious Auctioneer**

   - **Trusted Auctioneer**: In this initial design, the Auctioneer is trusted, substantially reducing malicious risk.
   - Future iterations could allow more permissionless auctioneers. In such scenarios, an auctioneer could theoretically attempt to cheat. However, the proposer checks the auction results, generating the real transaction. If the auctioneer produced a fraudulent result, it would fail the verification.

3. **Malicious Validator**

   - The proposer is responsible for creating the final transaction (BidTx) after receiving the highest valid bid. While one might suspect a proposer could copy or imitate the backrun logic, the AuctionEntryPoint contract requires a valid auctioneer signature. Any attempt to replay a backrun call without that signature will not be accepted.
   - If the validator doesn&apos;t follow the early deadline, it can be detected by the auctioneer off-chain as mentioned in the [Early Deadline Calculation](#early-deadline-calculation).

### Backward Compatibility

- **No fundamental consensus** changes, as adjacency remains a block-building policy.
- It is strongly recommended to upgrade the client to apply this auction protocol, so that the validators can benefit from the auction.

---

## References

- [MEV Concepts](https://github.com/flashbots/pm)
- [RFC 2119 Terminology](https://datatracker.ietf.org/doc/html/rfc2119)
- [KIP-82 (Fee Mechanism for Kaia)](https://kips.kaia.io/KIPs/kip-82)

---

## Copyright

Copyright and related
rights waived via [CC0 1.0 Universal](https://creativecommons.org/publicdomain/zero/1.0/).
</description>
        <pubDate>Mon, 24 Feb 2025 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-249</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-249</guid>
      </item>
    
      <item>
        <title>eth_config JSON-RPC Method</title>
        <category>Core</category>
        
          <comments>https://devforum.kaia.io/t/discussion-on-kip-276/8822</comments>
        
        <description>## Abstract

Add a new api endpoint to fetch the configuration for each fork. This KIP focuses on describing the difference from [EIP-7910](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7910.md).

## Motivation

Increases transparency of configuration values ​​for each fork by adding an `eth_config` api endpoint.
This API will allow users to check that their node are ready for the next fork and that the current fork configuration values ​​are correct.

## Specification

The key words &quot;MUST&quot;, &quot;MUST NOT&quot;, &quot;REQUIRED&quot;, &quot;SHALL&quot;, &quot;SHALL NOT&quot;, &quot;SHOULD&quot;, &quot;SHOULD NOT&quot;, &quot;RECOMMENDED&quot;, &quot;NOT RECOMMENDED&quot;, &quot;MAY&quot;, and &quot;OPTIONAL&quot; in this document are to be interpreted as described in RFC 2119 and RFC 8174.

All clients constraints directly under the specification follow [EIP-7910](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7910.md).

### Configuration RPC

A new JSON-RPC API, `eth_config`, is introduced. It takes no parameters and returns the result object specified in the next section.

### Result Object Structure

The RPC result object structure follows [EIP-7910](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7910.md).

### Members of the Configuration Object

The members of the configuration object follow that of [EIP-7910](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7910.md) except for the following fields.

#### `activationTime`

The result MUST NOT have any `activationTime` field.

#### `blobSchedule`

The blob configuration parameters for the specific fork will be represented as specified in [EIP-7910](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7910.md). If [EIP-4844](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4844.md) isn&apos;t implemented in the fork, the `blobSchedule` field MUST be `null`.

#### `forkId`

The `forkId` follows that of [EIP-7910](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7910.md), i.e. the `FORK_HASH` format as specified in [EIP-6122](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-6122.md).

#### `precompiles`

The precompiles follows that of [EIP-7910](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7910.md).

On top of Ethereum precompiles, some Kaia&apos;s precompiles may appear in this field.
The contract names are (in order): `VMLOG`, `FEE_PAYER`, `VALIDATE_SENDER`

`FEE_PAYER` and `VALIDATE_SENDER` values ​​of `0xa` and `0xb` deployed in Byzantine are ignored.

If any new precompiles are added, the EIP or KIP about the precompiles SHOULD state the names.

#### `systemContracts`

A JSON object representing the system-level contracts defined by EIPs and KIPs.
These have keys that are alphabetically sorted contract names (e.g., ADDRESS_BOOK) and values ​​that are 20-byte addresses.

System contracts whose address is hardcoded MUST appear in the result. Conversly, if a system contract address is stored in other contract&apos;s variables (e.g. [KIP-247 GaslessSwapRouter](https://github.com/kaiachain/kips/blob/main/KIPs/kip-247.md) is referenced from [KIP-149 Registry](https://github.com/kaiachain/kips/blob/main/KIPs/kip-149.md)), it SHALL NOT appear in the result.

The system contracts from genesis are (in order) `MAINNET_CREDIT`, `ADDRESS_BOOK`.
Note that `MAINNET_CREDIT` is only included if the genesis hash matches mainnet&apos;s one.

For Kip103, the added system contract is `KIP103`

For Kip160, the added system contract is `KIP160`

For Randao, the added system contract is `REGISTRY`.

For Prague, the added system contract is `HISTORY_STORAGE_ADDRESS`.

If new system contracts are added at hardcoded addresses, the EIP or KIP about the system contracts SHOULD state the names.

### Blob Parameter Only Forks

Blob Parameter Only Forks do not exist in kaia, so this is ignored.

## Rationale

### activationTime field

Because Kaia uses block numbers, not timestamps to specify fork activation, so there is no suitable value to return for this field. If the block number is returned, it can be interpreted as a timestamp in the long past. If zero is returned, it can be interpreted as genesis activation.

However, if the field is absent, the SDK will explicitly know that the activationTime is absent. For instance, in Javascript, it will be `result.current.activationTime === undefined`, and `undefined` cannot be converted to numbers.

### `FEE_PAYER` and `VALIDATE_SENDER` before and since Istanbul

`FEE_PAYER` and `VALIDATE_SENDER`, which were deployed in byzantim, are now also deployed to addresses `0x3fe` and `0x3ff`.

This means that `FEE_PAYER` and `VALIDATE_SENDER` are deployed to two addresses. However, for JSON structure compatibility with Ethereum, from istanbul onwards, only addresses `0x3fe` and `0x3ff` will be returned for these addresses, and `0xa` and `0xb` will be ignored. Therefore, `eth_config` defined in KIP276 returns the following.

Smart contracts deployed BEFORE Istanbul can access precompiles with:
| address | precompile |
| ------- | ---------- |
| 0x09 | `VMLOG` |
| 0xa | `FEE_PAYER` |
| 0xb | `VALIDATE_SENDER` |
| 0x3fe | N.A. |
| 0x3ff | N.A. |

Smart contracts deployed SINCE Istanbul can access precompiles with:
| address | precompile |
| ------- | ---------- |
| 0x09 | `BLAKE2F` |
| 0xa | N.A. -&gt; `KZG_POINT_EVALUATION` since Cancun |
| 0xb | N.A. -&gt; `BLS12_G1ADD` since Prague |
| 0x3fd | `VMLOG` |
| 0x3fe | `FEE_PAYER` |
| 0x3ff | `VALIDATE_SENDER` |

*Note: N.A. means not included in the JSON result.*

The relevant source code of Kaia is [here](https://github.com/kaiachain/kaia/blob/v2.0.3/blockchain/vm/contracts.go).

### System contracts are returned or not

Only a subset of Kaia&apos;s system contracts are returned from this API.
- If its address is hardcoded, it can be returned because the address never changes. e.g. AddressBook at 0x400, Registry at 0x401.
- If its address is stored as a variable in other contracts, it must not be returned. The address can change at any time regardless of hardfork. e.g. contracts like GaslessSwapRouter and CLRegistry are referenced from Registy and can be changed at any time by transaction.

## Backwards Compatibility

This does not affect the backward compatibility because this does not involve hardfork.

## Test Cases

### Sample Configs

Mainnet Prague Config

```JSON
{
  &quot;blobSchedule&quot;: null,
  &quot;chainId&quot;: &quot;0x2019&quot;,
  &quot;forkId&quot;: &quot;0xc00bab0e&quot;,
  &quot;precompiles&quot;: {
    &quot;BLAKE2F&quot;: &quot;0x0000000000000000000000000000000000000009&quot;,
    &quot;BLS12_G1ADD&quot;: &quot;0x000000000000000000000000000000000000000b&quot;,
    &quot;BLS12_G1MSM&quot;: &quot;0x000000000000000000000000000000000000000c&quot;,
    &quot;BLS12_G2ADD&quot;: &quot;0x000000000000000000000000000000000000000d&quot;,
    &quot;BLS12_G2MSM&quot;: &quot;0x000000000000000000000000000000000000000e&quot;,
    &quot;BLS12_MAP_FP2_TO_G2&quot;: &quot;0x0000000000000000000000000000000000000011&quot;,
    &quot;BLS12_MAP_FP_TO_G1&quot;: &quot;0x0000000000000000000000000000000000000010&quot;,
    &quot;BLS12_PAIRING_CHECK&quot;: &quot;0x000000000000000000000000000000000000000f&quot;,
    &quot;BN254_ADD&quot;: &quot;0x0000000000000000000000000000000000000006&quot;,
    &quot;BN254_MUL&quot;: &quot;0x0000000000000000000000000000000000000007&quot;,
    &quot;BN254_PAIRING&quot;: &quot;0x0000000000000000000000000000000000000008&quot;,
    &quot;ECREC&quot;: &quot;0x0000000000000000000000000000000000000001&quot;,
    &quot;FEE_PAYER&quot;: &quot;0x00000000000000000000000000000000000003fe&quot;,
    &quot;ID&quot;: &quot;0x0000000000000000000000000000000000000004&quot;,
    &quot;KZG_POINT_EVALUATION&quot;: &quot;0x000000000000000000000000000000000000000a&quot;,
    &quot;MODEXP&quot;: &quot;0x0000000000000000000000000000000000000005&quot;,
    &quot;RIPEMD160&quot;: &quot;0x0000000000000000000000000000000000000003&quot;,
    &quot;SHA256&quot;: &quot;0x0000000000000000000000000000000000000002&quot;,
    &quot;VALIDATE_SENDER&quot;: &quot;0x00000000000000000000000000000000000003ff&quot;,
    &quot;VMLOG&quot;: &quot;0x00000000000000000000000000000000000003fd&quot;
  },
  &quot;systemContracts&quot;: {
    &quot;ADDRESS_BOOK&quot;: &quot;0x0000000000000000000000000000000000000400&quot;,
    &quot;HISTORY_STORAGE_ADDRESS&quot;: &quot;0x0000f90827f1c53a10cb7a02335b175320002935&quot;,
    &quot;KIP103&quot;: &quot;0xd5ad6d61dd87edabe2332607c328f5cc96aecb95&quot;,
    &quot;KIP160&quot;: &quot;0xa4df15717da40077c0ad528296adbbd046579ee9&quot;,
    &quot;MAINNET_CREDIT&quot;: &quot;0x0000000000000000000000000000000000000000&quot;,
    &quot;REGISTRY&quot;: &quot;0x0000000000000000000000000000000000000401&quot;
  }
}
```

Kairos Cancun Config

```JSON
{
  &quot;blobSchedule&quot;: null,
  &quot;chainId&quot;: &quot;0x3E9&quot;,
  &quot;forkId&quot;: &quot;0x897592ea&quot;,
  &quot;precompiles&quot;: {
    &quot;BLAKE2F&quot;: &quot;0x0000000000000000000000000000000000000009&quot;,
    &quot;BN254_ADD&quot;: &quot;0x0000000000000000000000000000000000000006&quot;,
    &quot;BN254_MUL&quot;: &quot;0x0000000000000000000000000000000000000007&quot;,
    &quot;BN254_PAIRING&quot;: &quot;0x0000000000000000000000000000000000000008&quot;,
    &quot;ECREC&quot;: &quot;0x0000000000000000000000000000000000000001&quot;,
    &quot;FEE_PAYER&quot;: &quot;0x00000000000000000000000000000000000003fe&quot;,
    &quot;ID&quot;: &quot;0x0000000000000000000000000000000000000004&quot;,
    &quot;KZG_POINT_EVALUATION&quot;: &quot;0x000000000000000000000000000000000000000a&quot;,
    &quot;MODEXP&quot;: &quot;0x0000000000000000000000000000000000000005&quot;,
    &quot;RIPEMD160&quot;: &quot;0x0000000000000000000000000000000000000003&quot;,
    &quot;SHA256&quot;: &quot;0x0000000000000000000000000000000000000002&quot;,
    &quot;VALIDATE_SENDER&quot;: &quot;0x00000000000000000000000000000000000003ff&quot;,
    &quot;VMLOG&quot;: &quot;0x00000000000000000000000000000000000003fd&quot;
  },
  &quot;systemContracts&quot;: {
    &quot;ADDRESS_BOOK&quot;: &quot;0x0000000000000000000000000000000000000400&quot;,
    &quot;KIP103&quot;: &quot;0xd5ad6d61dd87edabe2332607c328f5cc96aecb95&quot;,
    &quot;KIP160&quot;: &quot;0x3d478e73c9dbebb72332712d7265961b1868d193&quot;,
    &quot;REGISTRY&quot;: &quot;0x0000000000000000000000000000000000000401&quot;
  }
}
```

#### Sample JSON-RPC

##### With Future Fork Scheduled

The following RPC command, issued on Kairos when Prague was scheduled but not activated:

```bash
curl -X POST -H &quot;Content-Type: application/json&quot; --data &apos;{&quot;jsonrpc&quot;:&quot;2.0&quot;,&quot;method&quot;:&quot;eth_config&quot;,&quot;id&quot;:1}&apos; http://localhost:8545
```

would return (after formatting):

```JSON
{
  &quot;jsonrpc&quot;: &quot;2.0&quot;,
  &quot;id&quot;: 1,
  &quot;result&quot;: {
    &quot;current&quot;: {
      &quot;blobSchedule&quot;: null,
      &quot;chainId&quot;: &quot;0x3E9&quot;,
      &quot;forkId&quot;: &quot;0xf00fbab3&quot;,
      &quot;precompiles&quot;: {
        &quot;BLAKE2F&quot;: &quot;0x0000000000000000000000000000000000000009&quot;,
        &quot;BN254_ADD&quot;: &quot;0x0000000000000000000000000000000000000006&quot;,
        &quot;BN254_MUL&quot;: &quot;0x0000000000000000000000000000000000000007&quot;,
        &quot;BN254_PAIRING&quot;: &quot;0x0000000000000000000000000000000000000008&quot;,
        &quot;ECREC&quot;: &quot;0x0000000000000000000000000000000000000001&quot;,
        &quot;FEE_PAYER&quot;: &quot;0x00000000000000000000000000000000000003fe&quot;,
        &quot;ID&quot;: &quot;0x0000000000000000000000000000000000000004&quot;,
        &quot;KZG_POINT_EVALUATION&quot;: &quot;0x000000000000000000000000000000000000000a&quot;,
        &quot;MODEXP&quot;: &quot;0x0000000000000000000000000000000000000005&quot;,
        &quot;RIPEMD160&quot;: &quot;0x0000000000000000000000000000000000000003&quot;,
        &quot;SHA256&quot;: &quot;0x0000000000000000000000000000000000000002&quot;,
        &quot;VALIDATE_SENDER&quot;: &quot;0x00000000000000000000000000000000000003ff&quot;,
        &quot;VMLOG&quot;: &quot;0x00000000000000000000000000000000000003fd&quot;
      },
      &quot;systemContracts&quot;: {
        &quot;ADDRESS_BOOK&quot;: &quot;0x0000000000000000000000000000000000000400&quot;,
        &quot;KIP103&quot;: &quot;0xd5ad6d61dd87edabe2332607c328f5cc96aecb95&quot;,
        &quot;KIP160&quot;: &quot;0x3d478e73c9dbebb72332712d7265961b1868d193&quot;,
        &quot;REGISTRY&quot;: &quot;0x0000000000000000000000000000000000000401&quot;
      }
    },
    &quot;next&quot;: {
      &quot;blobSchedule&quot;: null,
      &quot;chainId&quot;: &quot;0x3E9&quot;,
      &quot;forkId&quot;: &quot;0x0d29cd98&quot;,
      &quot;precompiles&quot;: {
        &quot;BLAKE2F&quot;: &quot;0x0000000000000000000000000000000000000009&quot;,
        &quot;BLS12_G1ADD&quot;: &quot;0x000000000000000000000000000000000000000b&quot;,
        &quot;BLS12_G1MSM&quot;: &quot;0x000000000000000000000000000000000000000c&quot;,
        &quot;BLS12_G2ADD&quot;: &quot;0x000000000000000000000000000000000000000d&quot;,
        &quot;BLS12_G2MSM&quot;: &quot;0x000000000000000000000000000000000000000e&quot;,
        &quot;BLS12_MAP_FP2_TO_G2&quot;: &quot;0x0000000000000000000000000000000000000011&quot;,
        &quot;BLS12_MAP_FP_TO_G1&quot;: &quot;0x0000000000000000000000000000000000000010&quot;,
        &quot;BLS12_PAIRING_CHECK&quot;: &quot;0x000000000000000000000000000000000000000f&quot;,
        &quot;BN254_ADD&quot;: &quot;0x0000000000000000000000000000000000000006&quot;,
        &quot;BN254_MUL&quot;: &quot;0x0000000000000000000000000000000000000007&quot;,
        &quot;BN254_PAIRING&quot;: &quot;0x0000000000000000000000000000000000000008&quot;,
        &quot;ECREC&quot;: &quot;0x0000000000000000000000000000000000000001&quot;,
        &quot;FEE_PAYER&quot;: &quot;0x00000000000000000000000000000000000003fe&quot;,
        &quot;ID&quot;: &quot;0x0000000000000000000000000000000000000004&quot;,
        &quot;KZG_POINT_EVALUATION&quot;: &quot;0x000000000000000000000000000000000000000a&quot;,
        &quot;MODEXP&quot;: &quot;0x0000000000000000000000000000000000000005&quot;,
        &quot;RIPEMD160&quot;: &quot;0x0000000000000000000000000000000000000003&quot;,
        &quot;SHA256&quot;: &quot;0x0000000000000000000000000000000000000002&quot;,
        &quot;VALIDATE_SENDER&quot;: &quot;0x00000000000000000000000000000000000003ff&quot;,
        &quot;VMLOG&quot;: &quot;0x00000000000000000000000000000000000003fd&quot;
      },
      &quot;systemContracts&quot;: {
        &quot;ADDRESS_BOOK&quot;: &quot;0x0000000000000000000000000000000000000400&quot;,
        &quot;HISTORY_STORAGE_ADDRESS&quot;: &quot;0x0000f90827f1c53a10cb7a02335b175320002935&quot;,
        &quot;KIP103&quot;: &quot;0xd5ad6d61dd87edabe2332607c328f5cc96aecb95&quot;,
        &quot;KIP160&quot;: &quot;0x3d478e73c9dbebb72332712d7265961b1868d193&quot;,
        &quot;REGISTRY&quot;: &quot;0x0000000000000000000000000000000000000401&quot;
      }
    },
    &quot;last&quot;: {
      &quot;blobSchedule&quot;: null,
      &quot;chainId&quot;: &quot;0x3E9&quot;,
      &quot;forkId&quot;: &quot;0x0d29cd98&quot;,
      &quot;precompiles&quot;: {
        &quot;BLAKE2F&quot;: &quot;0x0000000000000000000000000000000000000009&quot;,
        &quot;BLS12_G1ADD&quot;: &quot;0x000000000000000000000000000000000000000b&quot;,
        &quot;BLS12_G1MSM&quot;: &quot;0x000000000000000000000000000000000000000c&quot;,
        &quot;BLS12_G2ADD&quot;: &quot;0x000000000000000000000000000000000000000d&quot;,
        &quot;BLS12_G2MSM&quot;: &quot;0x000000000000000000000000000000000000000e&quot;,
        &quot;BLS12_MAP_FP2_TO_G2&quot;: &quot;0x0000000000000000000000000000000000000011&quot;,
        &quot;BLS12_MAP_FP_TO_G1&quot;: &quot;0x0000000000000000000000000000000000000010&quot;,
        &quot;BLS12_PAIRING_CHECK&quot;: &quot;0x000000000000000000000000000000000000000f&quot;,
        &quot;BN254_ADD&quot;: &quot;0x0000000000000000000000000000000000000006&quot;,
        &quot;BN254_MUL&quot;: &quot;0x0000000000000000000000000000000000000007&quot;,
        &quot;BN254_PAIRING&quot;: &quot;0x0000000000000000000000000000000000000008&quot;,
        &quot;ECREC&quot;: &quot;0x0000000000000000000000000000000000000001&quot;,
        &quot;FEE_PAYER&quot;: &quot;0x00000000000000000000000000000000000003fe&quot;,
        &quot;ID&quot;: &quot;0x0000000000000000000000000000000000000004&quot;,
        &quot;KZG_POINT_EVALUATION&quot;: &quot;0x000000000000000000000000000000000000000a&quot;,
        &quot;MODEXP&quot;: &quot;0x0000000000000000000000000000000000000005&quot;,
        &quot;RIPEMD160&quot;: &quot;0x0000000000000000000000000000000000000003&quot;,
        &quot;SHA256&quot;: &quot;0x0000000000000000000000000000000000000002&quot;,
        &quot;VALIDATE_SENDER&quot;: &quot;0x00000000000000000000000000000000000003ff&quot;,
        &quot;VMLOG&quot;: &quot;0x00000000000000000000000000000000000003fd&quot;
      },
      &quot;systemContracts&quot;: {
        &quot;ADDRESS_BOOK&quot;: &quot;0x0000000000000000000000000000000000000400&quot;,
        &quot;HISTORY_STORAGE_ADDRESS&quot;: &quot;0x0000f90827f1c53a10cb7a02335b175320002935&quot;,
        &quot;KIP103&quot;: &quot;0xd5ad6d61dd87edabe2332607c328f5cc96aecb95&quot;,
        &quot;KIP160&quot;: &quot;0x3d478e73c9dbebb72332712d7265961b1868d193&quot;,
        &quot;REGISTRY&quot;: &quot;0x0000000000000000000000000000000000000401&quot;
      }
    }
  }
}
```

##### Without Future Fork Scheduled

When no future forks are configured, the same RPC command would return:

```JSON
{
  &quot;jsonrpc&quot;: &quot;2.0&quot;,
  &quot;id&quot;: 1,
  &quot;result&quot;: {
    &quot;current&quot;: {
      &quot;blobSchedule&quot;: null,
      &quot;chainId&quot;: &quot;0x3E9&quot;,
      &quot;forkId&quot;: &quot;0xf00fbab3&quot;,
      &quot;precompiles&quot;: {
        &quot;BLAKE2F&quot;: &quot;0x0000000000000000000000000000000000000009&quot;,
        &quot;BN254_ADD&quot;: &quot;0x0000000000000000000000000000000000000006&quot;,
        &quot;BN254_MUL&quot;: &quot;0x0000000000000000000000000000000000000007&quot;,
        &quot;BN254_PAIRING&quot;: &quot;0x0000000000000000000000000000000000000008&quot;,
        &quot;ECREC&quot;: &quot;0x0000000000000000000000000000000000000001&quot;,
        &quot;FEE_PAYER&quot;: &quot;0x00000000000000000000000000000000000003fe&quot;,
        &quot;ID&quot;: &quot;0x0000000000000000000000000000000000000004&quot;,
        &quot;KZG_POINT_EVALUATION&quot;: &quot;0x000000000000000000000000000000000000000a&quot;,
        &quot;MODEXP&quot;: &quot;0x0000000000000000000000000000000000000005&quot;,
        &quot;RIPEMD160&quot;: &quot;0x0000000000000000000000000000000000000003&quot;,
        &quot;SHA256&quot;: &quot;0x0000000000000000000000000000000000000002&quot;,
        &quot;VALIDATE_SENDER&quot;: &quot;0x00000000000000000000000000000000000003ff&quot;,
        &quot;VMLOG&quot;: &quot;0x00000000000000000000000000000000000003fd&quot;
      },
      &quot;systemContracts&quot;: {
        &quot;ADDRESS_BOOK&quot;: &quot;0x0000000000000000000000000000000000000400&quot;,
        &quot;KIP103&quot;: &quot;0xd5ad6d61dd87edabe2332607c328f5cc96aecb95&quot;,
        &quot;KIP160&quot;: &quot;0x3d478e73c9dbebb72332712d7265961b1868d193&quot;,
        &quot;REGISTRY&quot;: &quot;0x0000000000000000000000000000000000000401&quot;
      }
    },
    &quot;next&quot;: null,
    &quot;last&quot;: null
  }
}
```

## References

- [EIP-7910](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7910.md)

## Copyright

Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
</description>
        <pubDate>Wed, 15 Oct 2025 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-276</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-276</guid>
      </item>
    
      <item>
        <title>Self Validator Registration</title>
        <category>Core</category>
        
          <comments>https://github.com/kaiachain/kips/issues/77</comments>
        
        <description>## Abstract

Introduce a self validator registration process that accepts validator-initiated requests (onboarding, offboarding, add/remove staking contracts) and validates request data under strict invariants.
Upon explicitly approved by the Kaia team, it&apos;ll register the validator information to corresponding smart contracts. This KIP won&apos;t bring any changes to the existing smart contracts, such as `AddressBook` and `SimpleBlsRegistry`.

## Specification

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.

### Terminology

| Term                  | Description                                                                                                                                                      |
| :-------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **AddressBook**       | The smart contract that manages the validator information, such as node ID, staking contract address, and reward address.                                        |
| **SimpleBlsRegistry** | The smart contract that manages the BLS public key information, introduced by [KIP-113](https://kips.kaia.io/KIPs/kip-113).                                      |
| **Consensus Node ID** | The node address participating in the consensus (i.e., address in council). It uniquely identifies a validator. A validator can have only one consensus node ID. |
| **Node ID**           | The dummy node address assigned for each staking contract registered in the AddressBook. A validator can have multiple node IDs.                                 |
| **Manager**           | An account set by the validator to manage its registration lifecycle.                                                                                            |

Adding a new staking contract requires a unique Node ID to be assigned, so a validator needs to assign multiple node IDs to the multiple staking contracts. But among all node IDs, only one node ID will be participating in the consensus and other node IDs will be dummy addresses. In this KIP, we call the node ID that participates in the consensus as **Consensus Node ID** and the other node IDs as **Node IDs**.

### ValidatorManager

A new `ValidatorManager` smart contract (`VMC` for short) is introduced, which implements the self registration processes for validators. The possible requests are:

1. **Onboarding**: Request to register a new validator to `AddressBook` and `SimpleBlsRegistry`.
2. **Offboarding**: Request to unregister a validator from `AddressBook` and `SimpleBlsRegistry`.
3. **AddStakingContract**: Request to add a new staking contract to a `AddressBook`.
4. **RemoveStakingContract**: Request to remove a staking contract from a `AddressBook`.

When onboarding/adding a new staking contract, the `StakingContract` MUST be `CnStakingV3` introduced by [KIP-163](https://kips.kaia.io/KIPs/kip-163).

The `VMC` must implement the IValidatorManager interface.

```solidity
interface IValidatorManager {
    enum RequestType {
        NoRequest,
        Onboarding,
        Offboarding,
        AddStakingContract,
        RemoveStakingContract
    }

    struct OnboardingRequest {
        address manager;
        address consensusNodeId;
        address stakingContract;
        address rewardAddress;
        IKIP113.BlsPublicKeyInfo blsPublicKeyInfo;
    }

    struct Request {
        /// @dev the manager that submitted the request
        address manager;
        /// @dev requestIndex is used to prevent request-replacement attack when approving or rejecting a pending request
        uint256 requestIndex;
        /// @dev the type of request
        RequestType requestType;
        /// @dev the ABI-encoded request data (abi.encode)
        ///  - Onboarding: struct OnboardingRequest
        ///  - Offboarding: N.A. (no request data required)
        ///  - AddStakingContract: (address nodeId, address stakingContract, address rewardAddress)
        ///  - RemoveStakingContract: (address nodeId)
        bytes requestData;
    }

    /// @notice Request a new validator onboarding, offboarding, adding a new staking contract, or removing a staking contract
    /// @param consensusNodeId The consensus node id that the request is for
    /// @param requestType The type of request
    /// @param requestData The encoded request data
    /// @dev Callable by validator manager
    /// @dev Note that this only allows to submit one request at a time
    function request(address consensusNodeId, RequestType requestType, bytes memory requestData) external;

    /// @notice Cancel existing request for the given consensus node id
    /// @param consensusNodeId The consensus node id that the request is for
    /// @dev Callable by validator manager
    function cancelRequest(address consensusNodeId) external;

    /// @notice Transfer the validator manager to a new address
    /// @dev Callable by validator manager
    ///      Can&apos;t transfer manager if there is a pending request
    function transferManager(address consensusNodeId, address newManager) external;

    /// @notice Approve a request
    /// @dev Callable by owner
    /// @param consensusNodeId The consensus node id that has a request to be approved
    function approveRequest(address consensusNodeId, uint256 requestIndex) external;

    /// @notice Reject request of a validator manager
    /// @dev Callable by owner
    /// @param consensusNodeId The consensus node id that has a request to be rejected
    function rejectRequest(address consensusNodeId, uint256 requestIndex) external;
}
```

After the activation of `VMC`, all validators must manage their own registration process through `VMC`. For onboarding of existing validators, the Kaia team will initially backfill the validator information at the deployment of `VMC`.

### Prerequisites

Since the `VMC` will execute calls to the `AddressBook` and `SimpleBlsRegistry`, the following prerequisites should be satisfied to ensure the execution of requests:

1. `VMC` is registered as an admin of `AddressBook`. It can include more than one admin for backup purposes.
2. `AddressBook` has `requirement` set to 1.
   - This is required since `AddressBook` internally implements a multisig mechanism for the admin list.
3. `VMC` is the owner of `SimpleBlsRegistry`.

If any of the prerequisites are not met, the `VMC` MUST revert the request approval. (not prevent request submission)

### Self Registration Process

#### Onboarding

1. Prepare a manager account.
2. Manager deploys a new consensus staking contract (`CnStakingV3MultiSig`) through the staking factory in [Registry](https://kips.kaia.io/KIPs/kip-149).
3. Candidate finishes the initialization of staking contract.
4. Candidate submits an onboarding request to `VMC`.
5. Kaia team reviews the request.
6. Kaia team approves the request.
   - a. `VMC` MUST execute `AddressBook.submitRegisterCnStakingContract` to register the validator information.
   - b. `VMC` MUST execute `SimpleBlsRegistry.register` to register the BLS public key information.

#### Offboarding

1. Manager submits an offboarding request to `VMC`.
2. Kaia team reviews the request.
3. Kaia team approves the request.
   - a. `VMC` MUST execute `AddressBook.submitUnregisterCnStakingContract` to unregister the validator information of staking contracts.
   - b. `VMC` MUST execute `SimpleBlsRegistry.unregister` to unregister the BLS public key information.

#### AddStakingContract

1. Manager deploys a new staking contract through the staking factory in [Registry](https://kips.kaia.io/KIPs/kip-149).
2. Validator finishes the initialization of staking contract.
3. Validator submits an add staking contract request to `VMC`.
4. Kaia team reviews the request.
5. Kaia team approves the request.
   - a. `VMC` MUST execute `AddressBook.submitRegisterCnStakingContract` to register a new staking contract.

#### RemoveStakingContract

1. Manager submits a remove staking contract request to `VMC`.
2. Kaia team reviews the request.
3. Kaia team approves the request.
   - a. `VMC` MUST execute `AddressBook.submitUnregisterCnStakingContract` to unregister the staking contract.

### Request validation

The `VMC` MUST implement the following validation rules for each request type and MUST revert for both request submission and approval if any of the rules are not met:

#### Common Validation Rules

1. Request for `Validator (ConsensusNodeId)` MUST be submitted by the corresponding `Manager`.
   - When onboarding, the `Request.manager` MUST be `msg.sender`.
2. Request for `Validator (ConsensusNodeId)` can be cancelled by only the `Manager` that submitted the request.
3. `Validator (ConsensusNodeId)` can have only one pending request at a time.
4. `Request.requestData` MUST be encoded correctly.

#### Onboarding Request Validation

1. `ConsensusNodeId` MUST NOT exist in `VMC`.
2. `BlsPublicKeyInfo` MUST be valid.
3. `ConsensusNodeId` MUST have a balance of at least `MIN_CONSENSUS_NODE_BALANCE`.
4. Node entry MUST be valid.
   - a. `ConsensusNodeId` MUST NOT exist in `AddressBook`.
   - b. `StakingContract` MUST be deployed by the `Manager` via the registered staking factory.
   - c. `StakingContract` MUST be initialized.
   - d. `StakingContract` MUST have `nodeId()` equal to `RequestData.consensusNodeId`
   - e. `StakingContract` MUST have `rewardAddress()` equal to `RequestData.rewardAddress`.
   - f. If `PublicDelegation` is enabled, `PublicDelegation` MUST be deployed by the `StakingContract` via the registered public delegation factory.

#### Offboarding Request Validation

1. `ConsensusNodeId` MUST exist in `VMC`.

#### AddStakingContract Request Validation

1. `ConsensusNodeId` MUST exist in `VMC`.
2. `NodeId` MUST NOT be managed by current `Manager`.
3. Node entry MUST be valid.
   - a. `NodeId` MUST NOT exist in `AddressBook`.
   - b. `StakingContract` MUST be deployed by the `Manager` via the registered staking factory.
   - c. `StakingContract` MUST be initialized.
   - d. `StakingContract` MUST have `nodeId()` equal to `RequestData.consensusNodeId`
   - e. `StakingContract` MUST have `rewardAddress()` equal to `RequestData.rewardAddress`.
4. New staking contract MUST be compatible with the consensus node&apos;s staking contract.
   - a. `PublicDelegation` MUST NOT be enabled.
   - b. `gcId` MUST be the same.
   - c. `rewardAddress` MUST be the same.

#### RemoveStakingContract Request Validation

1.  `ConsensusNodeId` MUST exist in `VMC`.
2.  `NodeId` MUST NOT be current `ConsensusNodeId`.
3.  `NodeId` MUST be managed by current `Manager`.

## Rationale

### Maintain existing smart contracts

The `AddressBook` provides a trusted source of validator information, deployed at `0x0000000000000000000000000000000000000400` since the genesis block. Migrating the existing `AddressBook` will essentially require a major effort and cost to entire ecosystem. The Kaia team decided to implement `VMC`, which doesn&apos;t require any changes to the existing smart contracts but can meet all the requirements for the self validator registration process.

### Request-Approval flow

Both validators and the Kaia team need time to be aware of the new validator registration process. This Request-Approval flow prevents any accidental or malicious requests from being executed directly without proper review process.

### One request at a time

The validator registration process is critical for the network and should be done in a secure and reliable manner. Allowing multiple requests at a time would introduce unnecessary complexity for both validators and network.

### Consensus Node ID as a request argument

The `request` function requires `ConsensusNodeId` as an argument to explicitly use `ConsensusNodeId` as a unique identifier of a validator. The `Manager` is only responsible for sending the request transaction on its `ConsensusNodeId`, and should not play as a unique identifier for a validator.

### Lack of staking amount requirement for onboarding

Currently there&apos;re validators operating multiple staking contracts with different staking amounts. In this case, it&apos;s possible that the staking contract has less than 5M KAIA staked. If a validator has less than 5M KAIA staked in total, the Kaia network automatically demotes the validator and it doesn&apos;t affect to network stability. But in the future permissionless system, it&apos;ll be necessary to introduce strict staking amount requirement for onboarding to prevent any spamming.

### No Public Delegation for an additional staking contract

Since `AddStakingContract` request doesn&apos;t allow `PublicDelegation` to be enabled, the validator should offboard and onboard again to migrate staking contract to enable `PublicDelegation`. This is intended to prevent any unintentional reward distribution. We can consider the following scenario:

1. Validator A first has a staking contract with `PublicDelegation` disabled.
2. Validator A deploys a new staking contract with `PublicDelegation` enabled.
3. Validator A updates the reward address of existing staking contract to the `PublicDelegation` address.

Right after (3), all rewards will go to `PublicDelegation` address, which only tracks the staking amount of new staking contract. If there&apos;s not enough staking amount, than small shareholders will receive massive rewards, which is not intentional.

## Backwards Compatibility

### CnStakingContract

The `VMC` contract will be backward compatible with the existing `CnStakingContractV2` and `CnStakingContractV3`, but not compatible with the previous deprecated `CnStakingContractV1`. Please note that the `CnStakingContractV2` compatibility is required for the existing validators (when backfilling the validator information).

## Forward Compatibility

Forward compatibility might be considered since self registration process is essential for a permissionless network. During the future progress, the Kaia team is expecting fundamental changes to the existing smart contracts, which means `VMC` can be deprecated once the new smart contracts are deployed. The Kaia team should provide a proper migration plan for the existing validators and keep the interface compatible with the new smart contracts to ensure seamless transition.

## Security Considerations

In an emergency situation which requires instant recovery through direct access to the `AddressBook` or `SimpleBlsRegistry`, it can cause state inconsistency between `VMC` and the `AddressBook` or `SimpleBlsRegistry`. In this case, the Kaia team should perform a state recovery process through manual intervention and implement a proper recovery function to ensure the network can recover from the emergency situation.

## Reference

- [KIP-163](https://kips.kaia.io/KIPs/kip-163)
- [KIP-113](https://kips.kaia.io/KIPs/kip-113)

## Copyright

Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
</description>
        <pubDate>Mon, 10 Nov 2025 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-277</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-277</guid>
      </item>
    
      <item>
        <title>BlobTx for Kaia</title>
        <category>Core</category>
        
          <comments>https://devforum.kaia.io/t/discussion-on-kip-279/8897</comments>
        
        <description>## Abstract

Introduce the Blob transaction type that represents the existence of data blobs, corresponding KZG commitments and proofs. This KIP follows the latest Ethereum Fusaka specs and will be included in Kaia&apos;s Osaka Hardfork. Therefore, [EIP-4844](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4844.md) is the baseline, but also includes the [EIP-7516](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7516.md) BLOBBASEFEE opcode, [EIP-7594](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7594.md) cell proof format, and [EIP-7918](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7918.md) blob base fee calculation. The blob per block limit was lowered to fit in Kaia&apos;s short 1-second block time. Since the limit is low, the target gas mechanism was removed for simplicity.

## Motivation

Kaia wants to accommodate rollups for scalability and specialized features in L2 chains. Rollups typically need to upload their summary data onto L1 which incurs a high data load. For instance, if an L2 submits a 128 KB blob every 10 seconds, the chain receives 33 GB a month and pays 33,000 KAIA. With this KIP, the L2 can post its data through [EIP-4844 compatible blob transactions to provide cost-effective and scalable](https://docs.arbitrum.io/how-arbitrum-works/inside-arbitrum-nitro) method. As the blockchain can be free from the burden of persisting the calldata, the chain can offer a lower pricing.

## Specification

### Parameter

**Type parameters**

| Parameter | Value | Note |
|-|-|-|
| `BLOB_TX_TYPE` | `0x03` | For Ethereum compatible RLP |
| `TxTypeEthereumBlob` | `0x7803` | For Kaia consensus RLP |
| `VERSIONED_HASH_VERSION_KZG` | `0x01` | Prefixed to `BlobVersionedHash` |
| `BlobSidecarVersionV1` | `0x01` | The sidecar_version of `BlobTxWithBlobsV1` |

**Blob size parameters**

| Parameter | Value | Note |
|-|-|-|
| `BYTES_PER_FIELD_ELEMENT` | 32 | (byte/elem) |
| `FIELD_ELEMENTS_PER_BLOB` | 4096 | (elem/blob) |
| `BYTES_PER_BLOB` | `BYTES_PER_FIELD_ELEMENT * FIELD_ELEMENTS_PER_BLOB` = 131072 | 128 KB
| `GAS_PER_BLOB` | 131072 | (blobgas/blob). 1 blobgas/byte |
| `MAX_BLOB_GAS_PER_BLOCK` | 131072 | max 1 blob/block |
| `BLOB_BASE_FEE_MULTIPLIER` | 8 | baseFeePerBlobGas = 8*baseFeePerGas |
| `BLOB_SIDECARS_RETENTION` | 1814400 | 21 days in blocks |

**Cell proof parameters**

| Parameter | Value | Note |
|-|-|-|
| `FIELD_ELEMENTS_PER_EXT_BLOB` | `2 * FIELD_ELEMENTS_PER_BLOB` = 8192 | (elem/extblob) |
| `FIELD_ELEMENTS_PER_CELL` | 64 | (elem/cell) |
| `BYTES_PER_CELL` | `FIELD_ELEMENTS_PER_CELL * BYTES_PER_FIELD_ELEMENT` = 2048 | (byte/cell) |
| `CELLS_PER_EXT_BLOB` | `FIELD_ELEMENTS_PER_EXT_BLOB // FIELD_ELEMENTS_PER_CELL` = 128 | (cell/extblob) |

**Data types**

| Type | Definition |
|-|-|
| `kzg4844.Blob` | `[BYTES_PER_BLOB]byte` = `[131072]byte` |
| `kzg4844.Commitment` | `[48]byte` |
| `kzg4844.Proof` | `[48]byte` |
| `BlobVersionedHash` | `[32]byte` = `0x01 + hash(commitment)[1:]` |

![blobtx_structure](/assets/kip-279/blobtx_structure.png)

### Blob transaction

The transaction fields are identical to the EIP-4844 definition.

```js
TransactionPayloadBody = [chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, to, value, data, access_list, max_fee_per_blob_gas, blob_versioned_hashes, y_parity, r, s]
```

All fields follow the same semantics as EIP-4844. Note that the `to` field cannot be `nil` and always be an address.

### Block header

The block header is extended with two new fields blobGasUsed and excessBlobGas.

```py
class Header:
	parentHash:   hash
	rewardbase:   address
	root:         hash
	txHash:       hash
	receiptHash:  hash
	bloom:        bloom
	blockScore:   bigint
	number:       bigint
	gasUsed:      uint64
	time:         bigint
	timeFoS:      uint8
	extra:        bytes
	governance:   bytes
	vote:         bytes

	baseFee:      int   # since Magma, as per KIP-71

	randomReveal: bytes # since Randao, as per KIP-114
	mixHash:      bytes # since Randao, as per KIP-114

	blobGasUsed:   uint64 # since Osaka, as per KIP-279
	excessBlobGas: uint64 # since Osaka, as per KIP-279
```

### Networking

There are multiple network representations. BlobTx and BlobSidecar travel through the network of nodes in various forms.

```js
BlobTxRLP = 0x7803 || rlp(TransactionPayloadBody)
EthBlobTxRLP = 0x03 || rlp(TransactionPayloadBody)
BlobTxSidecar = rlp([sidecar_version, blobs, commitments, cellProofs])
BlobTxWithBlobs = rlp([TransactionPayloadBody, sidecar_version, blobs, commitments, proofs]
```

The Kaia chain internally processes the RLP encoding prefixed with `TxTypeEthereumBlob (0x7803)`. But for Ethereum compatibility, a Kaia node MAY return an RLP encoding prefixed with `BLOB_TX_TYPE (0x03)`.

**Sidecar format**

- Both BlobTxSidecar and BlobTxWithBlobs MUST include a `sidecar_version` field before the blobs field. The version field is also referred to as `wrapper_version`.
- The sidecar version MUST be `BlobSidecarVersionV1 (0x01)` as per EIP-7594.
- There MUST be `CELLS_PER_EXT_BLOB (128)` proofs (i.e. cell proofs) for each blob as per EIP-7594.

**Propagate BlobTxWithBlobs**

- An RPC endpoint node, MAY accept a `BlobTxWithBlobs` via its `eth_sendRawTransaction` and `kaia_sendRawTransaction` JSON-RPC API.
- A node SHOULD accept a `BlobTxWithBlobs` via its p2p `TxMsg (0x06)`.
- When a node receive a `BlobTxWithBlobs` via RPC or p2p, it MUST be validated, then propagated to its peers like any other transactions in the txpool.

**Block consensus**

- When a block proposer decides to include a `BlobTx`, the proposer MUST assure that the originating `BlobTxWithBlobs` is verified.
- Block proposals in `IstanbulMsg (0x11)` SHOULD include the `BlobTxWithBlobs` so validators can verify it.
- Validators MUST assure that the `BlobTxWithBlobs` is verified *before committing to the block*. Note that verifying a `BlobTxWithBlobs` does not require block execution.
- As a result, whenever you see a `BlobTx` in a finalized block, you are sure that there exists a corresponding `BlobTxSidecar` because you trust the CNs&apos; consensus.

**Persist BlobTxSidecar**

- When a node receive a finalized block with `BlobTx` inside, the node SHOULD persist the corresponding `BlobTxSidecar`. The node can obtain the `BlobTxSidecar` from following sources:
  - From the `BlobTxWithBlobs` in the node&apos;s txpool memory
  - From a peer over p2p `NewBlockMsg (0x0b)`
  - From a peer over p2p `BlobSidecarsMsg (0x16)`
- Nodes MAY request peers for `BlobTxSidecars` via `BlobSidecarsRequestMsg (0x15)` p2p message. In turn, nodes SHOULD respond peers with `BlobTxSidecars` in `BlobSidecarsMsg (0x16)` p2p message, if available.
- Nodes SHOULD NOT request peers for `BlobTxSidecars` whose block number is older than `BLOB_SIDECARS_RETENTION` before the head block.
- Nodes MAY delete the `BlobTxSidecar` whose block number is older than `BLOB_SIDECARS_RETENTION` before the head block.

![blobtx_networking](/assets/kip-279/blobtx_networking.png)

### BlobTxWithBlobs validation

Upon receiving a `BlobTxWithBlobs` via RPC or p2p, it is verified as follows.

1. The `BlobTxWithBlobs` format complies with EIP-7594.
  ```py
  tx, sidecar_version, blobs, commitments, proofs = blobTxWithBlobs
  assert sidecar_version == 1  # BlobSidecarVersionV1
  assert len(tx.blobVersionedHashes) == len(blobs) == len(commitments)  # 1 commitment per blob
  assert len(blobs) * CELLS_PER_EXT_BLOB == len(proofs)  # 128 proofs per blob
  ```
2. The `BlobTxWithBlobs` contains the correct proofs
  ```py
  for i in range(len(tx.blobVersionedHashes)):
    assert CalcBlobHashV1(commitments[i]) == tx.blobVersionedHashes[i]
  assert VerifyCellProofs(blobs, commitments, cellProofs)
  ```

A `BlobTxWithBlobs` verification happens in these situations:
- Receive via `eth_sendRawTransaction` RPC
- Receive via [`TxMsg` p2p message](https://github.com/kaiachain/kaia/blob/v2.1.0/node/cn/protocol.go#L61)
- Receive via [`NewBlockMsg` p2p message](https://github.com/kaiachain/kaia/blob/v2.2.0-rc.1/node/cn/protocol.go#L89)
- Receive via [`IstanbulMsg` p2p message](https://github.com/kaiachain/kaia/blob/v2.1.0/consensus/istanbul/backend/handler.go)&apos;s [Proposal payload](https://github.com/kaiachain/kaia/blob/v2.1.0/consensus/istanbul/types.go). Note that the `type Proposal interface` is instantiated as `*types.Block` which includes the transactions inside. Since a `BlobTxWithBlobs` is treated as a transaction, the Proposal should naturally include Sidecars.

### Block validation

A valid block must satisfy the following conditions. Note that the authenticity of the `tx.blobVersionedHashes` is not verified here because we assume that consensus nodes validated them against `BlobTxSidecar`.

Rules below are similar to the EIP-4844 &apos;Execution layer validation&apos; specification, except the `baseFeePerBlobGas` calculation.

1. `header.excessBlobGas` equals the value calculated from the parent header.
  ```py
  def calcExcessBlobGas(parent: Header) -&gt; int:
    return max(0, parent.excessBlobGas + parent.blobGasUsed - TARGET_BLOB_GAS_PER_BLOCK)
  assert block.header.excessBlobGas == calcExcessBlobGas(block.parent.header)
  ```
2. For each BlobTx, the sender has enough balance to fund both execution gas and blob gas.
  ```py
  for tx in block.transactions:
    ...
    max_execution_fee = tx.gas * tx.maxFeePerGas
    max_blob_fee = len(tx.blobVersionedHashes) * GAS_PER_BLOB * tx.maxFeePerBlobGas
    assert signer(tx).balance &gt;= max_execution_fee + max_blob_fee
    ...
  ```
3. For each BlobTx, there is at least one blob versioned hash with the correct version.
  ```py
  for tx in block.transactions:
    ...
    if tx.type == BLOB_TX_TYPE:
      assert len(tx.blobVersionedHashes) &gt; 0
      for h in tx.blobVersionedHashes:
        assert h[0] == VERSIONED_HASH_VERSION_KZG
    ...
  ```
4. For each BlobTx, `tx.maxFeePerBlobGas` is at least the calculated `baseFeePerBlobGas`.
  ```py
  baseFeePerBlobGas = BLOB_BASE_FEE_MULTIPLIER * block.header.baseFeePerGas
  for tx in block.transactions:
    if tx.type == BLOB_TX_TYPE:
      assert tx.maxFeePerBlobGas &gt;= baseFeePerBlobGas
  ```
5. `header.blobGasUsed` is correctly calculated from the transactions and is below the limit.
  ```py
  blobGasUsed = 0
  for tx in block.transactions:
    if tx.type == BLOB_TX_TYPE:
      blobGasUsed += len(tx.blobVersionedHashes) * GAS_PER_BLOB
  assert block.header.blobGasUsed == blobGasUsed
  assert block.header.blobGasUsed &lt;= MAX_BLOB_GAS_PER_BLOCK
  ```

### Opcodes

The `BLOBHASH (0x49)` opcode was introduced to the Kaia chain with the Cancun hardfork, but it has been returning a zero hash. Its behavior MUST change to take in one integer argument index and return `tx.blob_versioned_hashes[index]`. Its gas cost `3` stay the same.

The `BLOBBASEFEE (0x4a)` opcode was introduced to the Kaia chain with the Cancun hardfork, but it has been returning 0. Its behavior MUST change to return the blob base fee of the current block it is executing in. Its gas cost `2` MUST stay the same.

### Precompile

The `POINT_EVALUATION_PRECOMPILE (0x0a)` was introduced to the Kaia chain with the Cancun hardfork, and it does not change.

## API

#### eth_sendRawTransaction, kaia_sendRawTransaction

eth_sendRawTransaction and kaia_sendRawTransaction accepts a `BlobTxWithBlobs`. It MUST NOT accept `BlobTx` without the sidecar.

#### eth_getRawTransaction, kaia_getRawTransaction

eth_getRawTransaction returns the raw transaction RLP prefixed with `BLOB_TX_TYPE (0x03)`. kaia_getRawTransaction returns the raw transaction RLP prefixed with `TxTypeEthereumBlob (0x7803)`. Both APIs never return `BlobTxWithBlobs`.

#### eth_getBlock, eth_getTransaction, eth_getTransactionReceipt

These eth namespace APIs that return transaction fields MUST show the `maxFeePerBlobGas` and `blobVersionedHashes` fields, and its `type: &quot;0x03&quot;`.
The `eth_getTransactionReceipt` MUST also show `blobGasUsed` and `blobGasPrice` fileds.

#### kaia_getBlock, kaia_getTransaction, kaia_getTransactionReceipt

These kaia namespace APIs that return transaction fields MUST show the `maxFeePerBlobGas` and `blobVersionedHashes` fields, and its `typeInt: 30723`, and `type: &quot;TxTypeEthereumBlob&quot;`.
The `eth_getTransactionReceipt` MUST also show `blobGasUsed` and `blobGasPrice` fileds.

#### eth_getBlobSidecars

A new JSON-RPC API `eth_getBlobSidecars` returns the stored `BlobTxSidecars` associated with the `BlobTx`s in the specified block. An empty array is returned if the block does not contain any `BlobTx`. An error is returned if the block has some `BlobTx` but any of the associated `BlobTxSidecars` are unavailable in the RPC node, e.g. deleted by expiration or didn&apos;t receive from peers.

- Parameters:
  - `number` - integer or hexadecimal block number, or the string tags such as &quot;pending&quot;, &quot;latest&quot;.
  - `fullBlob` - If true, returns the full blob data. Otherwise, return up to the first 32 bytes of each blob. False by default.
- Returns:
  - An array of:
    - `blobSidecar` - A sidecar object
      - `version` - The `sidecar_version` in integer
      - `blobs` - An array of blobs in hex strings
      - `commitments` - An array of commitments in hex strings
      - `proofs` - An array of proofs in hex strings
    - `blockHash` - The hash of the block the blob is included
    - `blockNumber` - The number of the block the blob is included
    - `txHash` - The hash of the transaction the blob is included
    - `txIndex` - The index of the transaction the blob is included
- Example
  ```sh
  curl http://localhost:8551 -X POST -H &quot;Content-Type: application/json&quot; \
    --data &apos;{&quot;jsonrpc&quot;:&quot;2.0&quot;, &quot;id&quot;:1, &quot;method&quot;:&quot;eth_getBlobSidecars&quot;, &quot;params&quot;:[&quot;0x1234&quot;,false]}&apos; \
  ```
  ```json
  {
    &quot;jsonrpc&quot;: &quot;2.0&quot;,
    &quot;id&quot;: 1,
    &quot;result&quot;: [
      {
        &quot;blobSidecar&quot;: {
          &quot;version&quot;: 1,
          &quot;blobs&quot;: [
            &quot;0x61c47a49eb50be125fa6c05e1bc9f3eb1c7c555bddd93d8fc1be4d0bff6cae32&quot;
          ],
          &quot;commitments&quot;: [
            &quot;0x90b049b9255bf13b96c226a1ec1a3dac0deac6533b55378c846e91839000ddc31a662afdfc489694add406846cfeefbf&quot;
          ],
          &quot;proofs&quot;: [
            &quot;0xb4104a0b89b4a302c44c753560fae156107746edc98ce81c91d7045490eebee984bd9d96648bad6ff1454643ad00d2e1&quot;
          ]
        },
        &quot;blockHash&quot;: &quot;0x1d67a5039edec9296a3f5935111da5b712df541905ff6ce9f3581d3bc7a1afbd&quot;,
        &quot;blockNumber&quot;: &quot;0xc0f6b6b&quot;,
        &quot;txHash&quot;: &quot;0xbb7376baf28c7a1698729ce91266ac82652281704fe217e0b5a5ef968e62b169&quot;,
        &quot;txIndex&quot;: &quot;0x1&quot;,
      }
    ]
  }
  ```

#### eth_getBlobSidecarByTxHash

A new JSON-RPC API `eth_getBlobSidecarByTxHash` returns the stored `BlobTxSidecars` associated with the specified `BlobTx`. An error is returned if the transaction does not exist or is not a BlobTx type. An error is returned if the transaction is a `BlobTx` but the associated `BlobTxSidecars` are unavailable in the RPC node, e.g. deleted by expiration or didn&apos;t receive from peers.


- Parameters:
  - `txHash` - The hash of the blob transaction.
  - `fullBlob` - If true, returns the full blob data. Otherwise, return up to the first 32 bytes of each blob. False by default.
- Returns:
  - `blobSidecar` - A sidecar object
    - `version` - The `sidecar_version` in integer
    - `blobs` - An array of blobs in hex strings
    - `commitments` - An array of commitments in hex strings
    - `proofs` - An array of proofs in hex strings
  - `blockHash` - The hash of the block the blob is included
  - `blockNumber` - The number of the block the blob is included
  - `txHash` - The hash of the transaction the blob is included
  - `txIndex` - The index of the transaction the blob is included
  - Example
    ```sh
    curl http://localhost:8551 -X POST -H &quot;Content-Type: application/json&quot; \
      --data &apos;{&quot;jsonrpc&quot;:&quot;2.0&quot;, &quot;id&quot;:1, &quot;method&quot;:&quot;eth_getBlobSidecarByTxHash&quot;, &quot;params&quot;:[&quot;0xbb7376baf28c7a1698729ce91266ac82652281704fe217e0b5a5ef968e62b169&quot;,false]}&apos; \
    ```
    ```json
    {
      &quot;jsonrpc&quot;: &quot;2.0&quot;,
      &quot;id&quot;: 1,
      &quot;result&quot;: {
        &quot;blobSidecar&quot;: {
          &quot;version&quot;: 1,
          &quot;blobs&quot;: [
            &quot;0x61c47a49eb50be125fa6c05e1bc9f3eb1c7c555bddd93d8fc1be4d0bff6cae32&quot;
          ],
          &quot;commitments&quot;: [
            &quot;0x90b049b9255bf13b96c226a1ec1a3dac0deac6533b55378c846e91839000ddc31a662afdfc489694add406846cfeefbf&quot;
          ],
          &quot;proofs&quot;: [
            &quot;0xb4104a0b89b4a302c44c753560fae156107746edc98ce81c91d7045490eebee984bd9d96648bad6ff1454643ad00d2e1&quot;
          ]
        },
        &quot;blockHash&quot;: &quot;0x1d67a5039edec9296a3f5935111da5b712df541905ff6ce9f3581d3bc7a1afbd&quot;,
        &quot;blockNumber&quot;: &quot;0xc0f6b6b&quot;,
        &quot;txHash&quot;: &quot;0xbb7376baf28c7a1698729ce91266ac82652281704fe217e0b5a5ef968e62b169&quot;,
        &quot;txIndex&quot;: &quot;0x1&quot;,
      }
    }
    ```

#### eth_blobBaseFee

A new JSON-RPC API `eth_blobBaseFee` returns the expected next block&apos;s `baseFeePerBlobGas`. Clients may use this value to choose an appropriate `tx.maxFeePerBlobGas`. The expected value is `BLOB_BASE_FEE_MULTIPLIER * next baseFeePerGas`.

- Parameters: none
- Returns:
  - `result`: The expected blob base fee in kei in hex string.
- Example
  ```sh
  curl http://localhost:8551 -X POST -H &quot;Content-Type: application/json&quot; \
    --data &apos;{&quot;jsonrpc&quot;:&quot;2.0&quot;, &quot;id&quot;:1, &quot;method&quot;:&quot;eth_blobBaseFee&quot;, &quot;params&quot;:[]}&apos; \
  ```
  ```json
  {
    &quot;jsonrpc&quot;: &quot;2.0&quot;,
    &quot;id&quot;: 1,
    &quot;result&quot;: &quot;0x2e90edd000&quot;
  }
  ```

#### eth_feeHistory, kaia_feeHistory

The `eth_feeHistory` and `kaia_feeHistory` APIs MAY return an additional array field `baseFeePerBlobGas` that contains the historic blob base fee over the requested block range.

## Rationale

### Blob capacity

Ethereum blobspace supply is 0.5 blobs/sec since Pectra hardfork&apos;s [EIP-7691](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7691.md) (target 6 blobs per block, 12 second block time). [Ethereum&apos;s blobspace utilization is currently at around 60%](https://www.binance.com/en/square/post/24317625438034). Ethereum is planning to reach 1.2 blobs/sec in the future after [EIP-7892 BPO2](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7892.md) hardfork.

This KIP proposes higher or similar capacity of 1 blobs/sec. This will be sufficient enough to accommodate several L2s.

The network should easily handle 1 blob per block because one blob (134 KB including cell proofs) is much smaller than the [theoretical block size limit of 10 MB](https://github.com/kaiachain/kaia/blob/f882207448f239d2d148e057ca1b950b031d5a46/params/protocol_params.go#L220-L222).

### Blob base fee as a multiple of base fee

This KIP proposes the max 1 blobs per block (`MAX_BLOB_GAS_PER_BLOCK = TARGET_BLOB_GAS_PER_BLOCK = 1 * GAS_PER_BLOB`) parameter. Under this configuration, excessBlobGas is always zero.

EIP-4844 adjusts the blob base fee based on previous block&apos;s excessBlobGas. This mechanism cannot work in this KIP because MAX equals the TARGET.

EIP-7918 imposes an implicit lower bound (&quot;reserve price&quot;) to the blob base fee. It is implicit in a way that excessBlobGas does not drop when the blob base fee is lower than the reserve price. Again, this mechanism cannot be applied as-is in this KIP because excessBlobGas does not rise at the first place. The reserve price here is a certain multiple of base fee.

This KIP directly defines the blob base fee as a multiple of base fee, skipping the excessBlobGas logic. It inherits the the rationale of EIP-7918 to make BlobTxs pay for the computing cost it incurs.

### Blob base fee multiplier

The blob fee is priced relative to a large-calldata transaction price.

- Storing in calldata: Send a regular transaction with a 128 KB calldata.
- Sending BlobTx: Send a BlobTxWithBlobs. Its size is 134 KB including the 128 cell proofs.

#### Computing cost

Broadcasting a BlobTx incurs KZG proof verification to every node in the network. According to the estimation in [EIP-7918](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7918.md), batch-verifying `CELLS_PER_EXT_BLOB (128)` proofs costs roughly 15 times the `POINT_EVALUATION_PRECOMPILE (50000 gas)`. Therefore, we can estimate that verifying a blob costs about 750,000 gas.

#### Storage cost

A significant storage burden is saved with BlobTx because sidecars are only persisted for a short period of time. In contrast, transaction calldata are stored indefinitely. For comparion, let us assume that [EIP-4444](https://eips.ethereum.org/EIPS/eip-4444) is activated in the future so that transaction calldata are stored for a year.

Storing a 128 KB calldata indefinitely (but 1 year in this calculation) is priced at 5,242,880 gas. With [KIP-223](https://github.com/kaiachain/kips/blob/main/KIPs/kip-223.md) and [EIP-7623](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7623.md), nonzero byte costs 40 gas per byte. Usually L2 rollup data are compressed blobs, so they are mostly nonzero random bytes. Therefore, `40 * 128 * 1024 = 5,242,880 gas`.

Proportionally, storing a 134 KB blob for 21 days should be priced at `(21day * 134KB) / (1year * 128KB) * 5,242,880 = 315,785 gas`

#### Blob base fee

Based on the computing and storage costs, a blob fee should be equivalent to paying an execution gas fee of `750,000 + 315,785 = 1,065,785 gas`. This is around 5 times cheaper than sending a large calldata. Now translate the gas cost to the unit of blob base fee.

```
(1,065,785 gas) * (baseFee kei/gas) ~ (1 blob) * (131072 blobgas/blob) * (blobBaseFee kei/blobgas)
blobBaseFee ~= (8.13 gas/blobgas) * (baseFee kei/gas)
```

Therefore, `baseFeePerBlobGas = BLOB_BASE_FEE_MULTIPLIER (8) * baseFeePerGas`. For instance:
- When baseFeePerGas = 25 gkei,
- Calldata gas fee = `5,242,880 * 25gkei = 0.131 KAIA`
- BlobTx blob fee = `131072 * 8 * 25gkei = 0.026 KAIA` is 5x cheaper.

### Reject sidecar V0

The EIP-4844 defined the blobTxWithBlobs without the version field (V0) that had been used since Dencun Hardfork. Since Fusaka Hardfork, only the new format - with the version field (V1) - will be accepted ([EIP-7607](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7607.md), [EIP-7594](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7594.md), [go-ethereum blobpool](https://github.com/ethereum/go-ethereum/blob/2a4847a7d1462cdda375429b447e7162514721e0/core/txpool/blobpool/blobpool.go#L1669)). Since we haven&apos;t supported V0, we start with V1 at the beginning of BlobTx in Kaia.

```js
BlobTxWithBlobsV0 = rlp([TransactionPayloadBody, blobs, commitments, proofs]
BlobTxWithBlobsV1 = rlp([TransactionPayloadBody, sidecar_version, blobs, commitments, proofs]
```

### Custom RPC for sidecar retrieval

Ethereum provides the sidecars via its [Beacon API `/eth/v1/beacon/blobs/{block_id}`](https://ethereum.github.io/beacon-APIs/#/Beacon/getBlobs). Since Kaia does not have Beacon API framework, new JSON-RPC APIs `eth_getBlobSidecars` and `eth_getBlobSidecarByTxHash` were added.

## References

- [EIP-4844: Shard Blob Transactions](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4844.md)
- [EIP-7516: BLOBBASEFEE instruction](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7516.md)
- [EIP-7594: PeerDAS - Peer Data Availability Sampling](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7594.md)
- [EIP-7918: Blob base fee bounded by execution cost](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7918.md)
- [Ethereum Deneb consensus specs](https://github.com/ethereum/consensus-specs/tree/master/specs/deneb)
- [Ethereum Fulu consensus specs](https://github.com/ethereum/consensus-specs/tree/master/specs/fulu)
- [Kaia BLOBHASH, BLOBBASEFEE, KZG precompile implementation](https://github.com/klaytn/klaytn/pull/2032)
- [Kaia RLP block size limit implemenation](https://github.com/kaiachain/kaia/pull/575)
- [AWS pricing calculator](https://calculator.aws/)
</description>
        <pubDate>Mon, 24 Nov 2025 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-279</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-279</guid>
      </item>
    
      <item>
        <title>Permissionless Validator Lifecycle</title>
        <category>Core</category>
        
          <comments>https://github.com/kaiachain/kips/issues/86</comments>
        
        <description>## Abstract

This KIP introduces an automatic state transition framework for candidates and validators in the permissionless Kaia network. It defines 9 states (4 candidate states, 5 validator states) and their allowed transitions, enabling decentralized validator lifecycle management without manual intervention from the Kaia team.

## Motivation

The current Kaia network operates in a permissioned manner where the Kaia team manually manages validator states. As the network transitions to permissionless operation, an automatic state management system becomes necessary.

With KIP-227 introducing VRank for quantitative evaluation of validators, this KIP establishes the state machine that governs:

- How candidates join the network through a VRank testing
- How validators participate in consensus based on stake ranking
- How validators are paused or removed upon VRank violations
- How validators can voluntarily exit or perform maintenance

This framework enables trustless validator lifecycle management while maintaining network stability through structured state transitions.

## Specification

### Parameters

The following parameters are used in this KIP:

| Parameter                | Description                                                                                                                    | Sample Value                                                  |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------- |
| `MaxValidatorCount`      | Maximum number of validators across all validator states (ValActive, ValReady, ValInactive, ValPaused, ValExiting)             | 100                                                           |
| `ActiveValidatorCount`   | Number of top-staked validators that is in `ValActive`, `ValPaused` when epoch starts                                          | 50                                                            |
| `MaxReadyCandidateCount` | Maximum number of candidates that can be in `CandReady`                                                                        | 3                                                             |
| `MinStake`               | Minimum staking amount required to enter the consensus                                                                         | 5,000,000 KAIA                                                |
| `ValPausedTimeout`       | Maximum duration a validator can remain in `ValPaused` before transitioning to `ValInactive`                                   | 8 hours                                                       |
| `ValIdleTimeout`         | Maximum duration a validator can remain in `ValInactive` or `ValReady` before transitioning to `CandInactive`                  | 30 days                                                       |
| `ValPausedSlotLimit`     | Maximum number of validators that can be in `ValPaused`                                                                        | 1\*`F` / 2, where `F` is the byzantine fault tolerance factor |
| `ValExitingSlotLimit`    | Maximum number of validators that can be in `ValExiting`                                                                       | 1\*`F` / 2, where `F` is the byzantine fault tolerance factor |
| `VRankEpoch (Epoch)`     | Epoch interval for vrank                                                                                                       | 86400                                                         |
| `PFS_THRESHOLD`          | Threshold for proposal failure score (PFS) per epoch. PFS ≥ PFS_THRESHOLD indicates severe violation (defined in [KIP-227](/KIPs/kip-227)) | TBD                                                           |
| `CFS_THRESHOLD`          | Threshold for candidate failure score during VRank testing. CFS ≥ CFS_THRESHOLD indicates failing the testing (defined in [KIP-227](/KIPs/kip-227))   | TBD                                                           |

### Overview

The framework defines 9 states divided into two categories:

- **Candidate States**: Unknown (placeholder), CandInactive, CandReady, CandTesting
- **Validator States**: ValActive, ValReady, ValInactive, ValPaused, ValExiting

State transitions may occur either **at epoch interval** or **at arbitrary blocks**. Any event that occurred at block `N` **must take effect starting from block `N+1`**. For example, if a validator in `ValActive` state requests a transition to `ValPaused` at block `N`, the validator remains in `ValActive` for block `N`, and transitions to `ValPaused` starting from block `N+1`.

Only validators in **ValActive** state participate in consensus and receive rewards. The active validator set consists of the top 50 validators by staking amount at the epoch interval block.

**Idle Timer**: Validators in `ValInactive` or `ValReady` states are subject to `ValIdleTimeout`. The idle timer accumulates across both states and **only resets when the validator transitions to `CandInactive` or `ValActive`**. Transitions between `ValInactive` and `ValReady` do not reset the timer. This mechanism prevents a validator from remaining indefinitely in the ValInactive/ValReady states.

### Candidate States

| State        | Description                                                                         |
| ------------ | ----------------------------------------------------------------------------------- |
| Unknown      | Conceptual state representing entities not registered in the system.                |
| CandInactive | Candidate not ready to participate in VRank testing at the next epoch.              |
| CandReady    | Candidate has signaled readiness to participate in VRank testing at the next epoch. |
| CandTesting  | Candidate undergoing VRank testing to prove infrastructure reliability.             |

### Validator States

| State       | Description                                                                                                         |
| ----------- | ------------------------------------------------------------------------------------------------------------------- |
| ValActive   | Active validator participating in consensus and earning rewards (= committee). Must be in top 50 by staking amount. |
| ValReady    | Validator has signaled readiness to become `ValActive`. Waiting for top 50 position.                                |
| ValInactive | Inactive validator not participating in consensus. Need to signal readiness or exit to avoid timeout.               |
| ValPaused   | Validator in maintenance/recovery mode. May be voluntary or forced by VRank violation.                              |
| ValExiting  | Transitional state for current epoch. Becomes `ValInactive` at next epoch.                                          |

### State Transitions

State transitions are categorized by timing:

- **Epoch Interval**: Evaluated and executed when the new epoch starts only
- **Anytime**: Can occur immediately upon transaction during any block

### Transition Conditions

The `User tx` is a transaction initiated by the user, while `System tx` is a system operation initiated by the core client, which follows the same convention as [EIP-4788](https://eips.ethereum.org/EIPS/eip-4788).

#### Registration &amp; Deregistration

| From         | To           | Timing  | Condition | Trigger |
| ------------ | ------------ | ------- | --------- | ------- |
| Unknown      | CandInactive | Anytime | -         | User tx |
| CandInactive | Unknown      | Anytime | -         | User tx |

#### Candidate Lifecycle

| From         | To           | Timing  | Condition                                                                                                                                 | Trigger   |
| ------------ | ------------ | ------- | ----------------------------------------------------------------------------------------------------------------------------------------- | --------- |
| CandInactive | CandReady    | Anytime | Over `MinStake` AND `CandReady count &lt; MaxReadyCandidateCount` AND `Val* count + CandReady count + CandTesting count &lt; MaxValidatorCount` | User tx   |
| CandReady    | CandInactive | Anytime | -                                                                                                                                         | User tx   |
| CandReady    | CandInactive | Epoch   | Below `MinStake`                                                                                                                          | System tx |
| CandReady    | CandTesting  | Epoch   | Over `MinStake`                                                                                                                           | System tx |
| CandTesting  | CandInactive | Epoch   | VRank score exceeds `CFS_THRESHOLD`                                                                                                       | System tx |
| CandTesting  | ValActive    | Epoch   | Pass VRank AND Top 50 by stake                                                                                                            | System tx |
| CandTesting  | ValInactive  | Epoch   | Pass VRank AND Below Top 50                                                                                                               | System tx |

#### Validator Active Set

| From        | To          | Timing  | Condition             | Trigger   |
| ----------- | ----------- | ------- | --------------------- | --------- |
| ValActive   | ValInactive | Epoch   | Below Top 50 by stake | System tx |
| ValInactive | ValReady    | Anytime | Over `MinStake`       | User tx   |
| ValReady    | ValInactive | Anytime | -                     | User tx   |
| ValReady    | ValInactive | Epoch   | Below Top 50 by stake | System tx |
| ValReady    | ValActive   | Epoch   | Top 50 by stake       | System tx |

#### Maintenance &amp; Recovery

| From      | To          | Timing  | Condition                                                                                     | Trigger   |
| --------- | ----------- | ------- | --------------------------------------------------------------------------------------------- | --------- |
| ValActive | ValPaused   | Anytime | `ValPaused count &lt; ValPausedSlotLimit` AND request by self for maintenance                    | User tx   |
| ValActive | ValPaused   | Anytime | `ValPaused count &lt; ValPausedSlotLimit` AND PFS is less than `PFS_THRESHOLD` (minor violation) | System tx |
| ValPaused | ValActive   | Anytime | -                                                                                             | User tx   |
| ValPaused | ValInactive | Anytime | `paused duration &gt;= ValPausedTimeout`                                                         | System tx |
| ValPaused | ValInactive | Epoch   | Below Top 50 by stake                                                                         | System tx |

#### Exit &amp; Offboarding

| From        | To           | Timing  | Condition                                                                                       | Trigger   |
| ----------- | ------------ | ------- | ----------------------------------------------------------------------------------------------- | --------- |
| ValActive   | ValExiting   | Anytime | `ValExiting count &lt; ValExitingSlotLimit` AND request by self for offboarding                    | User tx   |
| ValActive   | ValExiting   | Anytime | `ValExiting count &lt; ValExitingSlotLimit` AND PFS exceeds `PFS_THRESHOLD` (severe violation)     | System tx |
| ValActive   | ValExiting   | Anytime | `ValExiting count &lt; ValExitingSlotLimit` AND staking amount below `MinStake` (severe violation) | System tx |
| ValPaused   | ValExiting   | Anytime | `ValExiting count &lt; ValExitingSlotLimit` AND request by self for offboarding                    | User tx   |
| ValExiting  | ValInactive  | Epoch   | -                                                                                               | System tx |
| ValReady    | CandInactive | Anytime | `idle duration &gt;= ValIdleTimeout`                                                               | System tx |
| ValInactive | CandInactive | Anytime | `idle duration &gt;= ValIdleTimeout`                                                               | System tx |
| ValInactive | CandInactive | Anytime | -                                                                                               | User tx   |

### Epoch Transition

Epoch transitions occur at the start of the first block of the next epoch (`block.number % VRankEpoch == 0`), as part of the block processing logic. It means the epoch transition for `[N, N+VRankEpoch-1]` epoch is executed at the start of `N+VRankEpoch` block. The following pseudo code defines the transition ordering:

```python
def process_epoch_transition():
    # T1: Clear transitional states
    for validator in get_validators_by_state(ValExiting):
        transition(validator, ValInactive)

    # T2: Evaluate VRank for candidates in testing
    # Failed candidates return to CandInactive, passed candidates are marked for promotion
    passed_candidates = []
    for candidate in get_candidates_by_state(CandTesting):
        if not passed_vrank(candidate):
            transition(candidate, CandInactive)
        else:
            passed_candidates.append(candidate)

    # Build eligible validator pool for top 50 calculation
    # Pool includes: current active, ready, paused validators, and passed candidates
    # We can assume validators in ValActive have MinStake always since if not, it&apos;ll be ValExiting state due to staking violation
    # Exclude any entity with stake below MinStake
    eligible_pool = [
        *get_validators_by_state(ValActive),
        *get_validators_by_state(ValReady),
        *get_validators_by_state(ValPaused),
        *passed_candidates
    ]

    # Determine top 50 by stake (tie-break by address for determinism just in case)
    eligible_pool.sort(key=lambda v: (v.stake, v.address), reverse=True)
    top50 = set(eligible_pool[:ActiveValidatorCount])

    # T3a &amp; T3b: Promote or demote based on top 50
    for entity in eligible_pool:
        if is_top50(entity, top50):
            # T3a: Promote to ValActive (entities in top 50)
            if entity.state in (ValReady, CandTesting):
                transition(entity, ValActive)
            # ValActive stays ValActive
            # ValPaused stays ValPaused (requires voluntary recovery)
        else:
            # T3b: Demote to ValInactive (entities below top 50)
            if entity.state in (ValActive, ValPaused, CandTesting, ValReady):
                transition(entity, ValInactive)

    for candidate in get_candidates_by_state(CandReady):
        # T4a: Start new testing period for ready candidates
        if candidate.stake &gt;= MinStake:
            transition(candidate, CandTesting)
        # T4b: Demote to CandInactive if below MinStake
        else:
            transition(candidate, CandInactive)

def is_top50(entity, top50):
    return entity in top50 and entity.stake &gt;= MinStake
```

**Ordering Rationale:**

1. **T1 (ValExiting → ValInactive)**: Clear exiting validators first to free up slots and ensure they don&apos;t affect top 50 calculation.

2. **T2 (CandTesting evaluation)**: Evaluate VRank before calculating top 50 since passed candidates become eligible for the active set.

3. **T3a (Promote to ValActive)**: Entities in top 50 are promoted. `ValReady` and passed `CandTesting` transition to `ValActive`. `ValPaused` stays paused (recovery is voluntary).

4. **T3b (Demote to ValInactive)**: Entities below top 50 are demoted. `ValActive`, `ValPaused`, `ValReady`, and passed `CandTesting` transition to `ValInactive`.

5. **T4a (CandReady → CandTesting)**: Start testing last, after all other transitions are complete, so new testers don&apos;t affect the current epoch&apos;s calculations.

6. **T4b (CandReady → CandInactive)**: Demote to CandInactive if below MinStake.

The following diagram illustrates all valid state transition paths:

```mermaid
flowchart LR
    subgraph Candidates[&quot;Candidate States&quot;]
        Unknown
        CandInactive
        CandReady
        CandTesting
    end

    subgraph Validators[&quot;Validator States&quot;]
        ValInactive
        ValReady
        ValActive
        ValPaused
        ValExiting
    end

    %% Registration &amp; Deregistration
    Unknown --&gt;|&quot;register&quot;| CandInactive
    CandInactive --&gt;|&quot;deregister&quot;| Unknown

    %% Candidate Lifecycle
    CandInactive --&gt;|&quot;signal ready&quot;| CandReady
    CandReady --&gt;|&quot;cancel ready&quot;| CandInactive
    CandReady -.-&gt;|&quot;T4a: start testing&quot;| CandTesting
    CandReady -.-&gt;|&quot;T4b: below MinStake&quot;| CandInactive
    CandTesting -.-&gt;|&quot;T2: failed VRank&quot;| CandInactive
    CandTesting -.-&gt;|&quot;T3a: pass &amp; top 50&quot;| ValActive
    CandTesting -.-&gt;|&quot;T3b: pass &amp; below top 50&quot;| ValInactive

    %% Validator Active Set
    ValActive -.-&gt;|&quot;T3b: below top 50&quot;| ValInactive
    ValInactive --&gt;|&quot;signal ready&quot;| ValReady
    ValReady --&gt;|&quot;cancel ready&quot;| ValInactive
    ValReady -.-&gt;|&quot;T3b: below top 50&quot;| ValInactive
    ValReady -.-&gt;|&quot;T3a: top 50&quot;| ValActive

    %% Maintenance &amp; Recovery
    ValActive --&gt;|&quot;self maintenance&quot;| ValPaused
    ValActive --&gt;|&quot;minor VRank violation&quot;| ValPaused
    ValPaused --&gt;|&quot;recovered&quot;| ValActive
    ValPaused --&gt;|&quot;paused timeout&quot;| ValInactive
    ValPaused -.-&gt;|&quot;T3b: below top 50&quot;| ValInactive

    %% Exit &amp; Offboarding
    ValPaused --&gt;|&quot;self offboarding&quot;| ValExiting
    ValActive --&gt;|&quot;self offboarding&quot;| ValExiting
    ValActive --&gt;|&quot;severe VRank violation&quot;| ValExiting
    ValExiting -.-&gt;|&quot;T1: next epoch&quot;| ValInactive
    ValReady --&gt;|&quot;idle timeout&quot;| CandInactive
    ValInactive --&gt;|&quot;idle timeout&quot;| CandInactive
    ValInactive --&gt;|&quot;self offboarding&quot;| CandInactive
```

- **Solid arrows**: Anytime transitions
- **Dotted arrows**: Epoch interval transitions

&gt; **Note**: Any state transition not specified above is ILLEGAL and must be rejected by the protocol.

## Rationale

### Penalty for VRank violation

Currently, the penalty for VRank violation is temporary (`ValPaused`) or permanent (`ValExiting`) based on the severity as defined in [KIP-227](/KIPs/kip-227). A severe violation occurs when PFS exceeds or equals `PFS_THRESHOLD` (i.e., excessive round changes due to proposal failures), while a minor violation occurs when less than `PFS_THRESHOLD` yet. Additionally, a staking violation—where a validator in `ValActive` unstakes below `MinStake`—is also treated as a severe violation, triggering a transition to `ValExiting`. This will affect to reward suspension but not slashing or extending the lockup period, which is more direct penalty. During the permissionless transition, we&apos;d expect many validators requires enough onboarding period to operate validator node smoothly. If we enforce strict penalty from the early stage, it can lead to many validators being offboarded, which is not desirable and eventually lead to network instability. To prevent this and ensure enough transition period, we came up with current transition conditions. After the permissionless transition, if it turns out that current penalty is not sufficient, we can introduce more strict penalty such as slashing.

### `ValInactive` state

With nature of BFT-based consensus (we have room for upgrade), we need appropriate number of validators to operate the network. Without competition model, we can&apos;t expect any new validators to join the network when the `ValActive` slot is full. To activate the staking competition while keeping the network stable, we introduced the intermediate state `ValInactive` to allow validators under the top 50 by stake to have enough time to stake more KAIA and join the network.

### `ValExiting` state

If a validator wants to exit the network, it can voluntarily submit a request to make itself `ValExiting` state, and stop participating in the consensus. After the next epoch, it&apos;ll be automatically transitioned to `ValInactive` state, which can be offboarded freely. This is to ensure not rapidly offboarding validators, which can lead to network instability. If we directly offboard the validator, it&apos;s hard to restrict the number of offboarded (= `ValExiting` state) validators.

### Timeout and Slot Limit

Each validator state has specific timeout and/or slot limit constraints to prevent permanent slot occupation and ensure network stability:

- **`ValInactive` and `ValReady` (Idle Timeout)**: Without idle timeout, it&apos;s possible that some validators to remain in the `ValInactive` or `ValReady` state indefinitely, which effectively take up the slot and prevent new validators from joining the network.

- **`ValPaused` (Timeout and Slot Limit)**: The timeout prevents extended maintenance periods, while the slot limit ensures enough validators remain active for consensus. Additionally, the total pausable time per epoch is limited to prevent abuse—validators who exceed the maximum allowed paused time within an epoch will be penalized through VRank violation as defined in KIP-227. This applies regardless of whether the pause was voluntary or involuntary.

- **`ValExiting` (Slot Limit)**: This prevents too many validators from exiting simultaneously, which could destabilize the network. The slot limit ensures gradual offboarding.

## Backwards Compatibility

This KIP introduces a new framework for candidate and validator state and all participants must follow the rules defined in this KIP.

## Security Considerations

Query the node state at on-chain cannot be trusted since the on-chain state is not block-level atomic. For example, given the following transaction ordering:

```plaintext
block N:
  tx0: read  validator.state (returns ValActive)
  tx1: write validator.state = ValPaused (transition to ValPaused)
  tx2: read  validator.state (returns ValPaused)
```

`tx2` will read the state as `ValPaused`, while the validator is still treated as `ValActive` in the block N. For smart contract developers, it&apos;s highly discouraged to rely on the on-chain node state for critical decisions.

## References

- [KIP-227: Candidate and Validator Evaluation](https://github.com/kaiachain/kips/pull/27) - Defines VRank criteria and evaluation rules
- [EIP-4788: System Transactions](https://eips.ethereum.org/EIPS/eip-4788) - Defines system transaction convention

## Copyright

Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
</description>
        <pubDate>Mon, 19 Jan 2026 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-286</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-286</guid>
      </item>
    
      <item>
        <title>Permissionless Staking Policy</title>
        <category>Core</category>
        
          <comments>https://github.com/kaiachain/kips/issues/87</comments>
        
        <description>## Abstract

This KIP introduces a permissionless staking policy for the Kaia network, removing potential attack vectors and simplifying the protocol for open validator participation.

The current staking system has two design aspects that become problematic in a permissionless environment:

1. **Unstaking amounts are counted as effective stake**: The effective staking amount includes KAIA pending withdrawal, allowing validators to earn rewards on assets no longer committed to network security.

2. **Multiple staking contracts per validator**: Validators can register multiple `CnStaking` contracts, adding unnecessary complexity to node consolidation and creating potential attack surfaces through excessive contract registration.

This KIP addresses both issues by: (1) calculating effective staking amount as `staking() - unstaking()`, excluding pending withdrawals from reward distribution, and (2) restricting each validator to a single staking contract.

These changes align the consensus reward calculation with the existing governance voting power calculation, which already excludes unstaking amounts. The minimum 5M KAIA requirement in `CnStaking` for validator eligibility remains unchanged. Consensus Liquidity (KIP-226) is unaffected as it uses an upfront cooldown mechanism.

## Motivation

The current staking system was designed for a permissioned environment with trusted validators. Two aspects of this design become problematic in a permissionless context:

**Unstaking Counted as Effective Stake**

When a staker requests a withdrawal, the amount enters a 7-day lockup period. During this period, the unstaking amount remains included in the validator&apos;s effective staking amount for block reward calculation. This creates:

- **Misaligned Incentives**: Unstaking assets earn rewards despite no longer being committed to network security.
- **Logical Inconsistency**: Governance voting power already excludes unstaking amounts, creating a gap between consensus rewards and governance participation.
- **Economic Attack Surface**: Attackers could cycle stake through withdrawal requests, maintaining reward eligibility while minimizing actual economic commitment.

**Multiple Staking Contracts per Validator**

Validators can currently register multiple `CnStaking` contracts under the same consensus node. This was not an intended design but emerged from implementation flexibility. This creates:

- **Protocol Complexity**: Node consolidation logic must aggregate staking amounts across multiple contracts, increasing implementation and maintenance burden.
- **Attack Surface**: Malicious actors could register excessive staking contracts, exploiting the consolidation overhead.

This KIP addresses these issues by excluding unstaking amounts from effective stake and restricting validators to a single staking contract.

## Specification

The key words &quot;MUST&quot;, &quot;MUST NOT&quot;, &quot;REQUIRED&quot;, &quot;SHALL&quot;, &quot;SHALL NOT&quot;, &quot;SHOULD&quot;, &quot;SHOULD NOT&quot;, &quot;RECOMMENDED&quot;, &quot;MAY&quot;, and &quot;OPTIONAL&quot; in this document are to be interpreted as described in RFC 2119.

### Configuration

This KIP SHALL be activated at `PERMISSIONLESS_FORK_BLOCK_NUMBER`.

| Config                             | Value |
| ---------------------------------- | ----- |
| `PERMISSIONLESS_FORK_BLOCK_NUMBER` | TBD   |

### Single Staking Contract per Validator

After activation, each validator MUST have exactly one `CnStaking` contract registered. Validators with multiple staking contracts MUST consolidate to a single contract before activation.

### Effective Staking Amount Calculation

Before this KIP, the effective staking amount for a validator is derived from the contract&apos;s native KAIA balance (`balance`), which includes both actively staked and unstaking amounts. After activation, the effective staking amount MUST exclude unstaking amounts.

#### CnStaking Contract Interface

The `CnStaking` contract provides the following relevant methods:

| Method        | Description                                                                       |
| ------------- | --------------------------------------------------------------------------------- |
| `balance`     | Total KAIA balance held by the contract                                           |
| `staking()`   | Staked amount including unstaking (i.e., `staking() = activeStake + unstaking()`) |
| `unstaking()` | Total amount pending withdrawal                                                   |

Please note that this KIP only applies to the `CnStaking` contract registered in the `AddressBookV2` defined by [KIP-290](https://kips.kaia.io/KIPs/kip-290).

#### Before (Current Behavior)

```python
def get_effective_staking_amount(consolidated_staking_contracts: List[CnStaking]) -&gt; int:
    &quot;&quot;&quot;
    Current implementation: uses contract balance which includes unstaking.
    Allows multiple staking contracts per validator.
    &quot;&quot;&quot;
    total = 0
    for contract in consolidated_staking_contracts:
        # balance includes both active stake and unstaking amount
        total += contract.balance
    return total
```

#### After (This KIP)

```python
def get_effective_staking_amount(staking_contract: CnStaking, block_number: int) -&gt; int:
    &quot;&quot;&quot;
    After PERMISSIONLESS_FORK_BLOCK_NUMBER:
    - Single staking contract per validator
    - Excludes unstaking amounts
    &quot;&quot;&quot;
    if block_number &gt;= PERMISSIONLESS_FORK_BLOCK_NUMBER:
        # Exclude unstaking: staking() includes unstaking, so subtract it
        return staking_contract.staking() - staking_contract.unstaking()
    else:
        # Legacy behavior: use balance (includes unstaking)
        return staking_contract.balance
```

### Impact on Reward Distribution

The reward distribution mechanism (KIP-82) uses each validator&apos;s staking amount to calculate their share of block rewards. After this KIP, the staking amount MUST reflect only the actively staked amount from a single staking contract:

```
effectiveStake = cnStaking.staking() - cnStaking.unstaking() + clStaking
```

Where `clStaking` is the staked KAIA in Consensus Liquidity pools, which remains unaffected by this KIP.

### Consensus Liquidity Compatibility

This KIP does not affect Consensus Liquidity (KIP-226). CLDEX uses an upfront cooldown mechanism where the 7-day minimum staking period begins upon liquidity provision. Therefore, all KAIA in CLDEX pools is already considered actively staked.

### Validator Eligibility

The minimum 5M KAIA requirement in `CnStaking` for validator eligibility remains unchanged. This requirement is evaluated against only the actively staked amount (excluding unstaking) from the single staking contract after this KIP.

## Rationale

### Single Staking Contract per Validator

The current AddressBook doesn&apos;t check the duplication of reward address for new staking entry. This unintentionally enables the current multiple staking contract registration. It requires additional complexity to consolidate the validator information and operation cost. Currently, there&apos;re 3 main reasons for multiple staking contracts:

1. Legacy purpose (can be simply removed)
2. Delegation purpose (can be handled by public delegation)
3. Internal product purpose

For 2, we can handle it by explicit delegation contract. We&apos;re already utilizing delegation contract for public delegation that separates the principal and reward. It can be also used for CnStaking that doesn&apos;t use public delegation. For 3, it&apos;s a valid use case but not a common one (only 1 validator), but with enough transition period, it&apos;s not critical to migrate to single staking contract.

### No Changes to Consensus Liquidity

Consensus Liquidity (KIP-226) uses an upfront cooldown mechanism where the 7-day minimum staking period begins upon liquidity provision. This design inherently ensures all KAIA in CLDEX is actively committed, eliminating the need for unstaking exclusion logic.

## Backwards Compatibility

This KIP introduces breaking changes that require hardfork. Validators must consolidate their staking contracts before activation. The core client and on-chain components (e.g., on-chain governance) must follow the new logic after the hardfork.

## References

- [KIP-82: A new GC reward structure](https://kips.kaia.io/KIPs/kip-82)
- [KIP-163: Public Delegation](https://kips.kaia.io/KIPs/kip-163)
- [KIP-226: Consensus Liquidity](https://kips.kaia.io/KIPs/kip-226)
- [KIP-277: Self Validator Registration](https://kips.kaia.io/KIPs/kip-277)

## Copyright

Copyright and related rights waived via [CC0](/LICENSE).
</description>
        <pubDate>Mon, 19 Jan 2026 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-287</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-287</guid>
      </item>
    
      <item>
        <title>Permissionless Smart Contracts</title>
        <category>Core</category>
        
          <comments>https://github.com/kaiachain/kips/issues/90</comments>
        
        <description>## Abstract

This standard defines the spec for core smart contracts required to support permissionless validator operations.
The contracts with major changes include AddressBookV2, CnStakingV4, PublicDelegationV2, and VRank.
Other system contracts may have minor changes to accommodate these updates.
These contracts replace or extend existing contracts while maintaining backward compatibility where possible.

## Motivation

The updated contracts introduce the following improvements:

- **Flexible key management**: Validators can use external multisig solutions (e.g., KaiaSafe) or EOAs, instead of relying on embedded multisig functionality.
- **Simplified data model**: Each validator is represented by a single `(nodeId, stakingContract, rewardAddress)` entry, eliminating the complexity of consolidated staking.
- **Unified validator registry**: BLS public keys are stored directly in AddressBookV2, providing _a single source of truth_ for all validator information.
- **Upgradeability**: Some contracts are deployed as upgradeable proxies, enabling protocol improvements without requiring validator migration.

## Specification

The key words &quot;MUST&quot;, &quot;MUST NOT&quot;, &quot;REQUIRED&quot;, &quot;SHALL&quot;, &quot;SHALL NOT&quot;, &quot;SHOULD&quot;, &quot;SHOULD NOT&quot;, &quot;RECOMMENDED&quot;, &quot;MAY&quot;, and &quot;OPTIONAL&quot; in this document are to be interpreted as described in RFC 2119.

### Parameters

| Constant     | Value |
| ------------ | ----- |
| `FORK_BLOCK` | TBD   |

### Overview

The following contracts are introduced or updated:

| Contract           | Description                                                   |
| ------------------ | ------------------------------------------------------------- |
| AddressBookV2      | Unified validator registry with BLS keys and state management |
| CnStakingV4        | Staking contract for validators                               |
| PublicDelegationV2 | Instant delegation with transferable tokens                   |
| VRank              | Validator ranking system                                      |

SimpleBlsRegistry (KIP-113) is obsoleted by AddressBookV2.

Some contracts (e.g., VotingV2, StakingTrackerV3, CLRegistryV2, AuctionFeeVault) might have minor changes to accommodate the above updates.

### AddressBookV2

AddressBookV2 serves as the unified registry for all validator and candidate information.
It consolidates functionality previously spread across AddressBook, CnStaking, and SimpleBlsRegistry (KIP-113).

#### Key Changes from AddressBookV1

- **Integrated information**: On-chain information such as BLS key or gcId shall be stored directly in AddressBookV2.
- **Unique reward address**: Each validator entry must be a unique combination of `(nodeId, stakingContract, rewardAddress)`. Stake consolidation by reward address shall no longer be supported, as mentioned in KIP-287.
- **Removed embedded multisig**: Administrative operations shall not use built-in multisig.
- **State management**: Validator states as defined in KIP-286 shall be stored and managed within AddressBookV2.
- **Backward compatible interface**: The contract shall implement the existing AddressBook interface to maintain compatibility with nodes and other contracts.
- **Only CnStakingV4**: The new address book must only accept CnStakingV4 deployed properly by the factory.
- **Upgradeable**: The contract must be deployed as an upgradeable proxy to enable future improvements.

#### Data Structures

```solidity
struct NodeInfo {
    /// @dev Manager address for updating modifiable fields
    address manager;
    /// @dev Node identifier used in consensus (immutable)
    address nodeId;
    /// @dev Address to receive block rewards (immutable when PublicDelegation is enabled)
    address rewardAddress;
    /// @dev Associated staking contract address (immutable)
    address stakingContract;
    /// @dev Address for on-chain governance voting (mutable)
    address voterAddress;
    /// @dev BLS public key and proof-of-possession (immutable)
    BlsPublicKeyInfo blsInfo;
    /// @dev Governance Council ID, only for GCs (immutable)
    uint256 gcId;
    /// @dev Validator metadata in JSON format (mutable)
    string metadata;
    /// @dev Current state of the node
    NodeState state;
}

enum NodeState {
  Unknown,
  CandInactive,
  CandReady,
  CandTesting,
  ValInactive,
  ValReady,
  ValActive,
  ValPaused,
  ValExiting
}
```

#### Interface

TBU

### CnStakingV4

CnStakingV4 is the staking contract used by candidates and validators to stake KAIA and manage their node operations.

#### Key Changes from CnStakingV3

- **Removed embedded multisig**: The contract does not include built-in multisig functionality. Validators MAY deploy the contract with a multisig wallet (e.g., KaiaSafe) as the owner, or use an EOA.
- **No initial lockup**: The initial lockup mechanism, originally designed for token lockup of initial investors before network launch, shall be removed.
- **Removed redundant information**: Information stored in CnStakingV3 (such as rewardAddress, stakingTracker, voterAddress, gcId) shall be managed by AddressBookV2.
- **Upgradeable**: The contract must be deployed as a beacon proxy, allowing the protocol to upgrade all CnStakingV4 instances simultaneously.
- **Factory deployment**: A dedicated factory contract must handle CnStakingV4 deployment, ensuring consistent initialization and registration with AddressBookV2.

### PublicDelegationV2

PublicDelegationV2 enables KAIA holders to delegate their stake to validators and receive liquid staking tokens in return.

#### Key Changes from PublicDelegationV1

- **Transferable token**: pdKAIA token must be transferable, unlike PublicDelegationV1 where transfers were prohibited.
- **Instant redelegation**: Delegators must be able to redelegate their stake to a different validator instantly, without waiting for the standard 7-day withdrawal period.
- **Upgradeable**: The contract must be deployed as an upgradeable proxy to enable future improvements.

### VRank

VRank is a newly introduced contract that manages validator and candidate scoring based on performance metrics.

TBU

### Other contracts

- **Upgradeable**: VotingV2, StakingTrackerV3, CLRegistryV2, and other contracts must be deployed as an upgradeable proxy to enable future improvements.

## Rationale

### Removing Embedded Multisig

The original contracts include built-in multisig functionality to provide security for validator operations.
However, this approach has several drawbacks:

- Limits flexibility in key management strategies
- Increases contract complexity and gas costs
- Duplicates functionality available in mature external solutions (e.g., KaiaSafe)

By removing embedded multisig, validators gain flexibility to choose their preferred security model while reducing contract complexity.

### One-to-One Mapping in AddressBookV2

The previous design allows multiple staking contracts to share a reward address, with stakes consolidated for reward distribution, voting power calculation, etc:

```
[NodeId1, StakingContract1, RewardAddress1]
[NodeId2, StakingContract2, RewardAddress1]  // same reward address
[NodeId3, StakingContract3, RewardAddress1]  // same reward address
=&gt; Voting power = StakingContract1 + StakingContract2 + StakingContract3
```

This design allows validators to manage multiple staking contracts as a single validator entity.
However, the consolidation adds unnecessary complexity.
In AddressBookV2, each validator entry represents a single staking contract with a unique reward address, simplifying the data model.
See KIP-287 for details.

### Integrating information to AddressBookV2

Storing BLS public keys and GC IDs in a separate contract was inevitable, as the existing AddressBook could not be upgraded.
With AddressBookV2, they can be stored directly in a single contract, reducing the number of contract calls and providing a single source of truth for validator data.

### Upgradeability

There have been several contract upgrades in the past (e.g., CnStakingV1 -&gt; V2 -&gt; V3, StakingTrackerV1 -&gt; V2).
Migrations often involved significant overhead; especially, CnStaking migration takes over 7 days for withdrawal.
With PublicDelegation, migrations become even more complex as they require coordination with individual delegators.
This degraded user experience and delayed protocol improvements.

Deploying contracts as upgradeable proxies eliminates this migration overhead, allowing protocol improvements to apply immediately.
Upgrades are authorized through on-chain governance, ensuring transparent and decentralized control over contract changes.

## Backwards Compatibility

Parties depending on the system contracts MUST review this document and update their implementations accordingly.

### AddressBookV2

AddressBookV2 is designed to be compatible with AddressBook interface and existing consolidation logic, in order to reduce the friction with existing services.
Despite this compatibility, services MUST adopt AddressBookV2 interfaces to take full advantage of the new features.
Existing consolidation logic that groups stakes by reward address continues to work correctly; each group simply contains exactly one entry.

AddressBookV2 replaces the existing AddressBook at `0x0000000000000000000000000000000000000400`.
The address will contain proxy code starting from `FORK_BLOCK`.

### CnStakingV4

All validators MUST migrate to CnStakingV4 before `FORK_BLOCK`, as AddressBookV2 only accepts staking contracts deployed by the CnStakingV4 factory.
Since withdrawals require a 7-day waiting period, validators SHOULD begin their migration well in advance of `FORK_BLOCK`.

Each validator MUST use a single staking contract; validators with multiple staking contracts MUST consolidate before migration.

Services reading old CnStaking (V1-V3) state (such as rewardAddress or voterAddress) MUST update to read from AddressBookV2.

### PublicDelegationV2

Users and contracts MUST NOT rely on the non-transferable property of delegation tokens, since pdKAIA tokens become transferable in PublicDelegationV2.

### On-chain information

Users MUST read on-chain information from AddressBookV2 after `FORK_BLOCK`.

## References

- [KIP-81: Implementing the on-chain governance voting method](https://kips.kaia.io/KIPs/kip-81)
- [KIP-113: BLS public key registry](https://kips.kaia.io/KIPs/kip-113)
- [KIP-226: Consensus liquidity for Kaia](https://kips.kaia.io/KIPs/kip-226)
- [KIP-227: Candidate and Validator Evaluation](https://kips.kaia.io/KIPs/kip-227)
- [KIP-277: Self Validator Registration](https://kips.kaia.io/KIPs/kip-277)
- [KIP-286: Permissionless Validator Lifecycle](https://kips.kaia.io/KIPs/kip-286)
- [KIP-287: Permissionless Staking Policy](https://kips.kaia.io/KIPs/kip-287)

## Copyright

Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
</description>
        <pubDate>Fri, 23 Jan 2026 00:00:00 +0000</pubDate>
        <link>https://kips.kaia.io/KIPs/kip-290</link>
        <guid isPermaLink="true">https://kips.kaia.io/KIPs/kip-290</guid>
      </item>
    
  </channel>
</rss>
