<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>dmitrybase</title>
        <link>https://paragraph.com/@dmitrybase</link>
        <description>Shrek enjoyer</description>
        <lastBuildDate>Sat, 09 May 2026 09:39:37 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>dmitrybase</title>
            <url>https://storage.googleapis.com/papyrus_images/c8ab59646b71b0eb01827e4a5146949c5f62525f9bce21b991c2f317db30de04.jpg</url>
            <link>https://paragraph.com/@dmitrybase</link>
        </image>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[Using Ethereum wallets on TON 💎]]></title>
            <link>https://paragraph.com/@dmitrybase/using-ethereum-wallets-on-ton</link>
            <guid>yl26ICZkzzhP1aLeWcnA</guid>
            <pubDate>Mon, 28 Oct 2024 21:41:45 GMT</pubDate>
            <description><![CDATA[Or How to handle ECDSA signatures in TONTLDRThis post explains how to sign and verify data and transactions in TON using ECDSA (though TON primarily uses EDDSA). It assumes familiarity with EVM and basic TON concepts)Keep in mind, this is my very first post. Cheers.Hey, it&apos;s @swift1337! I do protocol @ ZetaChain 🐲 Being a protocol engineer is fun. It involves solving specific, niche challenges every day. At ZetaChain we&apos;re are working hard to bring omnichain capabilities to The Ope...]]></description>
            <content:encoded><![CDATA[<p><strong><em>Or How to handle ECDSA signatures in TON</em></strong></p><h1 id="h-tldr" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>TLDR</strong></h1><blockquote><p>This post explains how to sign and verify data and transactions in TON using <strong>ECDSA</strong> (though TON primarily uses EDDSA). It assumes familiarity with EVM and basic TON concepts)</p></blockquote><p>Keep in mind, this is my very first post. Cheers.</p><hr><p>Hey, it&apos;s <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/swift1337">@swift1337</a>! I do protocol @ <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.zetachain.com/">ZetaChain</a> 🐲</p><p>Being a protocol engineer is fun. It involves solving specific, niche challenges every day. At ZetaChain we&apos;re are working hard to bring omnichain capabilities to The Open Network (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://ton.org/">TON</a>) 💎</p><p>During this journey, one of the interesting technical challenges was signing TON transactions using ZetaChain&apos;s Threshold Signature Schemes (<strong>TSS</strong>) protocol, which relies on cryptography that differs from what is used in TON. The main use case is to bridge and withdraw locked TON during cross-chain transactions, with support of calling arbitrary smart contracts on connected chains.</p><p>In this post, I want to elaborate on how we solved ECDSA on TON and enabled support for Multi-Party Computation (MPC).</p><p>Other use cases may include using <strong>Metamask for signing ton transactions</strong>, as well as cross-chain governance.</p><p>All of the sources are available in our repo:</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/zeta-chain/protocol-contracts-ton">https://github.com/zeta-chain/protocol-contracts-ton</a></p><h2 id="h-on-curves-and-signatures" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">On Curves and Signatures</h2><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/62b61ce55049f6c2f7fc8dba09c1e2a461103a8561136279b19b2d6b4fcce147.png" alt="Facts" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Facts</figcaption></figure><p>Bitcoin, Ethereum, and many other blockchains use the Elliptic Curve Digital Signature Algorithm (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm">ECDSA</a>) and specifically the <code>secp256k1</code> curve to sign and verify data. TON, on the other hand, uses the Edwards-curve Digital Signature Algorithm (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://en.wikipedia.org/wiki/EdDSA">EdDSA</a>) with  <code>ed25519</code> curve.</p><h3 id="h-what-is-a-cryptographic-signature" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">What is a cryptographic signature?</h3><p>Basically, it&apos;s a mathematical way of proving that a message came from a specific signer.</p><p>For example, we can make a signature of &quot;Alice says <code>Hello, Bob!</code>&quot;. Technically, it would work like this:</p><pre data-type="codeBlock" text="Signer: Alice (0x0fA03A88042647025f8524b12899c6A3f1781A2e)
Input: &quot;Hello, Bob!&quot;

Signature:
0x07965b9c69767e2b45fe086aebc4fc103baf26f27f6176e9eb6a3458743ceef877d4e04987619321c7bcf5a2311d25fa61e48ac68c388b4d45bf42f098de831c1b
"><code><span class="hljs-symbol">Signer:</span> Alice (<span class="hljs-number">0</span>x0fA03A88042647025f8524b12899c6A3f1781A2e)
<span class="hljs-symbol">Input:</span> <span class="hljs-string">"Hello, Bob!"</span>

<span class="hljs-symbol">Signature:</span>
<span class="hljs-number">0</span>x07965b9c69767e2b45fe086aebc4fc103baf26f27f6176e9eb6a3458743ceef877d4e04987619321c7bcf5a2311d25fa61e48ac68c388b4d45bf42f098de831c1b
</code></pre><p><strong>Magic 🧙🏻‍♂️ This is literally the backbone of any blockchain</strong></p><p>The big brains of cryptography (not meme coins) 🧠 realized that it&apos;s inconvenient to operate with raw messages of arbitrary size (imagine signing a large book). To keep signatures consistent, we sign the hash of the message (usually SHA-256) instead of the entire message.</p><p>A process of getting a public key from a signature is called <strong>signature recovery</strong>. Technically, it looks like this:</p><pre data-type="codeBlock" text="// pseudo code
alicePubKey = ecdsa_recover(sha256(&quot;Hello, Bob!&quot;), signature)

alice = pubKeyToAddress(alicePubKey) // 0x0fA03A88042647025f85....
"><code><span class="hljs-comment">// pseudo code</span>
alicePubKey = <span class="hljs-built_in">ecdsa_recover</span>(sha256("Hello, Bob!"), signature)

alice = <span class="hljs-built_in">pubKeyToAddress</span>(alicePubKey) <span class="hljs-comment">// 0x0fA03A88042647025f85....</span>
</code></pre><p>Regarding the structure of this signature, it has <strong>65 bytes</strong>:</p><ul><li><p>32 bytes for <strong>R</strong>: One of the elliptic curve point coordinates, linking the message to the signature.</p></li><li><p>32 bytes for <strong>S</strong>: Derived from the message, private key, and r value. Both r and s are needed to verify the signature.</p></li><li><p>1 byte for <strong>V</strong>: A recovery identifier (0 or 1) used to determine the correct public key for signature verification.</p></li></ul><p>If you want to have an overview the topic, I suggest these introductory sources:</p><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://learnmeabitcoin.com/technical/cryptography/elliptic-curve/ecdsa/">https://learnmeabitcoin.com/technical/cryptography/elliptic-curve/ecdsa/</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://habr.com/ru/articles/692072/">https://habr.com/ru/articles/692072/</a></p></li><li><br></li><li><br></li></ul><h2 id="h-ton-of-quirks-and-superiority" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">TON of Quirks and Superiority</h2><p>TON is <em>far</em> superior to ETH in technical terms (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://ton.org/comparison_of_blockchains.pdf">link</a>). Because <code>async</code> and <code>sharding</code> are badass words for a reason! Just acknowledge it, comrade. <code>/s</code></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.quora.com/What-does-it-mean-when-the-British-say-very-interesting">Very Interesting</a>, but a regular wallet is also a smart contract. Due to this, even a simple coin transfer is actually two transactions (&quot;Alice sends a message with coins to Bob&quot; + &quot;Bob receives a message with coins from Alice&quot;).</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/95d1a5b86a8fa5135c4f3ef3d79bffc8245cac8ed6b9f88c58a2c42874b2a8fa.png" alt="avg TON wallet transaction" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">avg TON wallet transaction</figcaption></figure><p>When Alice sends 1 TON to Bob, Alice creates and signs a message off-chain and then broadcasts it to Alice&apos;s smart contract on TON. As of Q4 2024, the latest wallet implementation is <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/ton-blockchain/wallet-contract-v5">WalletV5</a>. It uses the following message structure for handling an external message (<code>recv_external</code>):</p><pre data-type="codeBlock" text="// pseudocode
cell msg_body: $someData + $signature
"><code><span class="hljs-comment">// pseudocode</span>
cell msg_body: <span class="hljs-variable">$someData</span> + <span class="hljs-variable">$signature</span>
</code></pre><p>Okay, forget pseudo code, we&apos;re not pseudo nerds! Warm up your fingers, let&apos;s see some <em>superiority</em> in practice:</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/ton-blockchain/wallet-contract-v5/blob/main/contracts/wallet_v5.fc">https://github.com/ton-blockchain/wallet-contract-v5/blob/main/contracts/wallet_v5.fc</a></p><h3 id="h-how-wallet-v5-checks-eddsa-signatures" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">How Wallet V5 checks EDDSA signatures</h3><pre data-type="codeBlock" text="() process_signed_request(slice in_msg_body, int is_external) impure inline {
  slice signature = in_msg_body.get_last_bits(size::signature);
  slice signed_slice = in_msg_body.remove_last_bits(size::signature);

  ;; ...
  
  slice data_slice = get_data().begin_parse();
  
  ;; The wallet fetches Alice&apos;s public key from its state
  ;; It&apos;s stored when Alice deploys her wallet
  ;; This is usually done automatically by most wallets (e.g. TonKeeper)
  ;; during the first transaction
  int public_key = data_slice~load_uint(size::public_key);

  ;; ...

  int is_signature_valid = check_signature(slice_hash(signed_slice), signature, public_key);

  ;; further wallet&apos;s logic ......
}
"><code>() process_signed_request(slice in_msg_body, int is_external) impure inline {
  slice <span class="hljs-attr">signature</span> = in_msg_body.get_last_bits(size::signature)<span class="hljs-comment">;</span>
  slice <span class="hljs-attr">signed_slice</span> = in_msg_body.remove_last_bits(size::signature)<span class="hljs-comment">;</span>

  <span class="hljs-comment">;; ...</span>
  
  slice <span class="hljs-attr">data_slice</span> = get_data().begin_parse()<span class="hljs-comment">;</span>
  
  <span class="hljs-comment">;; The wallet fetches Alice's public key from its state</span>
  <span class="hljs-comment">;; It's stored when Alice deploys her wallet</span>
  <span class="hljs-comment">;; This is usually done automatically by most wallets (e.g. TonKeeper)</span>
  <span class="hljs-comment">;; during the first transaction</span>
  int <span class="hljs-attr">public_key</span> = data_slice~load_uint(size::public_key)<span class="hljs-comment">;</span>

  <span class="hljs-comment">;; ...</span>

  int <span class="hljs-attr">is_signature_valid</span> = check_signature(slice_hash(signed_slice), signature, public_key)<span class="hljs-comment">;</span>

  <span class="hljs-comment">;; further wallet's logic ......</span>
}
</code></pre><p>As you can see, the logic is pretty straightforward:</p><ul><li><p>Split the message into <code>signed_slice</code> (payload) and <code>signature</code></p></li><li><p>Hash the payload: <code>slice_hash(signed_slice)</code></p></li><li><p>Take the owner&apos;s public key from the contract&apos;s state</p></li><li><p>Call <code>check_signature(hash, signature, public_key)</code></p></li></ul><p>And <code>check_signature</code> is actually an alias from <code>stdlib.fc</code>:</p><pre data-type="codeBlock" text=";; EDDSA signature check
int check_signature(int hash, slice signature, int public_key) asm &quot;CHKSIGNU&quot;;
"><code>;; EDDSA signature check
<span class="hljs-built_in">int</span> check_signature(<span class="hljs-built_in">int</span> <span class="hljs-built_in">hash</span>, <span class="hljs-built_in">slice</span> signature, <span class="hljs-built_in">int</span> public_key) asm <span class="hljs-string">"CHKSIGNU"</span>;
</code></pre><p>You can read more about TVM instructions here</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.ton.org/learn/tvm-instructions/instructions">https://docs.ton.org/learn/tvm-instructions/instructions</a></p><h2 id="h-the-key-point" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The Key Point</h2><p>To implement a contract or a wallet in TON that can send assets and invoke operations based on an &quot;EVM signer&quot;, we need to have a way to recover an ECDSA signature. Actually, there is a dedicated TVM instruction for that! Meet <code>ECRECOVER</code>:</p><blockquote><p>Recovers public key from signature, identical to Bitcoin/Ethereum operations. Takes 32-byte hash as uint256 <code>hash</code>; 65-byte signature as uint8 <code>v</code> and uint256 <code>r</code>, <code>s</code>. Returns <code>0</code> on failure, public key and <code>-1</code> on success. The 65-byte public key is returned as uint8 <code>h</code>, uint256 <code>x1</code>, <code>x2</code>.</p></blockquote><h2 id="h-show-me-the-code" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Show Me the Code</h2><p>Here&apos;s the flow that we&apos;ll use:</p><ol><li><p>Encode TON external message as bytes</p></li><li><p>Sign it using ECDSA</p></li><li><p>Prepend/Append it to the message</p></li><li><p>Check the signature using <code>ECRECOVER</code></p></li><li><p>If the signature matches the expected sender, invoke the operation (e.g., send TON coins)</p></li></ol><p>Let&apos;s say we want to implement a custody contract that allows us to send TON based on an ECDSA signature signed from Metamask. Then we can use the following external message body:</p><pre data-type="codeBlock" text="cell
  &gt; signature: 65 bytes -&gt; slice of 520 bits
  &gt; cell_ref:
    &gt; cell: arbitrary payload
"><code>cell
  <span class="hljs-operator">></span> signature: <span class="hljs-number">65</span> <span class="hljs-keyword">bytes</span> <span class="hljs-operator">-</span><span class="hljs-operator">></span> slice of <span class="hljs-number">520</span> bits
  <span class="hljs-operator">></span> cell_ref:
    <span class="hljs-operator">></span> cell: arbitrary payload
</code></pre><p>If you think “wtf is a cell?!”, you can read <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.ton.org/learn/overviews/cells">this</a>.</p><p>As an example, I&apos;ll use ZetaChain&apos;s cross-chain Gateway implementation.</p><pre data-type="codeBlock" text="cell auth::ecdsa::external(slice message, slice expected_evm_address) inline {
  ;; 1. Get signature
  slice signature = message~load_bits(size::signature_size);

  ;; 2. Get payload cell
  throw_if(error::no_signed_payload, message.slice_refs_empty?());
  cell payload = message~load_ref();

  ;; 3. Calculate payload hash
  int payload_hash = cell_hash(payload);

  ;; 4. Check signature
  int sig_check = check_ecdsa_signature(payload_hash, signature, expected_evm_address);

  if (sig_check != true) {
      ~strdump(&quot;check_ecdsa_signature&quot;);
      sig_check~dump();
      throw(error::invalid_signature);
  }

  return payload;
}
"><code>cell auth::ecdsa::external(slice message, slice expected_evm_address) inline {
  <span class="hljs-comment">;; 1. Get signature</span>
  slice <span class="hljs-attr">signature</span> = message~load_bits(size::signature_size)<span class="hljs-comment">;</span>

  <span class="hljs-comment">;; 2. Get payload cell</span>
  throw_if(error::no_signed_payload, message.slice_refs_empty?())<span class="hljs-comment">;</span>
  cell <span class="hljs-attr">payload</span> = message~load_ref()<span class="hljs-comment">;</span>

  <span class="hljs-comment">;; 3. Calculate payload hash</span>
  int <span class="hljs-attr">payload_hash</span> = cell_hash(payload)<span class="hljs-comment">;</span>

  <span class="hljs-comment">;; 4. Check signature</span>
  int <span class="hljs-attr">sig_check</span> = check_ecdsa_signature(payload_hash, signature, expected_evm_address)<span class="hljs-comment">;</span>

  if (sig_check != true) {
      ~strdump("check_ecdsa_signature")<span class="hljs-comment">;</span>
      sig_check~dump()<span class="hljs-comment">;</span>
      throw(error::invalid_signature)<span class="hljs-comment">;</span>
  }

  return payload<span class="hljs-comment">;</span>
}
</code></pre><p><strong>crypto.fc</strong></p><pre data-type="codeBlock" text=";; Returns keccak256 hash of the data as uint256
int hash_keccak256(builder b) asm &quot;1 INT HASHEXT_KECCAK256&quot;;

;; ECRECOVER FunC wrapper
(int, int, int, int) ecdsa_recover(int hash, int v, int r, int s) asm &quot;ECRECOVER&quot;;

;; TVM uses `v` ONLY as `0` or `1`. In ETH/BTC, a prefix is used. See RFC6979
;; See https://bitcoin.stackexchange.com/questions/38351/ecdsa-v-r-s-what-is-v
int normalize_ecdsa_recovery_id(int v) inline {
  ;; &quot;compressed recovery_id for pub key&quot;
  if v &gt;= 31 {
      return v - 31;
  }

  ;; &quot;uncompressed recovery_id for pub key&quot;
  if v &gt;= 27 {
      return v - 27;
  }

  return v;
}

;; Checks ECDSA signature. Returns int as an outcome:
;; 1: unable to recover public key
;; 2: recovered key is not uncompressed
;; 3: recovered address does not match the expected address
;; -1 (true): signature is valid
(int) check_ecdsa_signature(int hash, slice signature, slice expected_evm_address) impure inline_ref {
  ;; 1 Parse (v, r, s)
  int v = signature~load_uint(8).normalize_ecdsa_recovery_id();
  int r = signature~load_uint(256);
  int s = signature~load_uint(256);

  ;; 2. Recover public key
  (int h, int x1, int x2, int flag) = ecdsa_recover(hash, v, r, s);
  if flag != true {
      return 1;
  }

  ;; Deny compressed public keys (0x04 prefix means uncompressed)
  if h != 4 {
      return 2;
  }

  ;; 3. Derive 20 bytes evm address from the public key
  int pub_key_hash = begin_cell()
      .store_uint(x1, 256)
      .store_uint(x2, 256)
      .hash_keccak256();

  slice actual_evm_address = begin_cell()
      .store_uint(pub_key_hash, 256)
      .end_cell()
      .begin_parse()
      .slice_last(20 * 8);

  ;; 4. Compare with the expected address
  if equal_slices(expected_evm_address, actual_evm_address) == false {
      return 3;
  }

  return true;
}
"><code>;; Returns <span class="hljs-built_in">keccak256</span> hash of the data <span class="hljs-keyword">as</span> <span class="hljs-keyword">uint256</span>
<span class="hljs-keyword">int</span> hash_keccak256(builder b) asm <span class="hljs-string">"1 INT HASHEXT_KECCAK256"</span>;

;; ECRECOVER FunC wrapper
(<span class="hljs-keyword">int</span>, <span class="hljs-keyword">int</span>, <span class="hljs-keyword">int</span>, <span class="hljs-keyword">int</span>) ecdsa_recover(<span class="hljs-keyword">int</span> hash, <span class="hljs-keyword">int</span> v, <span class="hljs-keyword">int</span> r, <span class="hljs-keyword">int</span> s) asm <span class="hljs-string">"ECRECOVER"</span>;

;; TVM uses `v` ONLY <span class="hljs-keyword">as</span> `<span class="hljs-number">0</span>` or `<span class="hljs-number">1</span>`. In ETH<span class="hljs-operator">/</span>BTC, a prefix <span class="hljs-keyword">is</span> used. See RFC6979
;; See https:<span class="hljs-comment">//bitcoin.stackexchange.com/questions/38351/ecdsa-v-r-s-what-is-v</span>
<span class="hljs-keyword">int</span> normalize_ecdsa_recovery_id(<span class="hljs-keyword">int</span> v) inline {
  ;; <span class="hljs-string">"compressed recovery_id for pub key"</span>
  <span class="hljs-keyword">if</span> v <span class="hljs-operator">></span><span class="hljs-operator">=</span> <span class="hljs-number">31</span> {
      <span class="hljs-keyword">return</span> v <span class="hljs-operator">-</span> <span class="hljs-number">31</span>;
  }

  ;; <span class="hljs-string">"uncompressed recovery_id for pub key"</span>
  <span class="hljs-keyword">if</span> v <span class="hljs-operator">></span><span class="hljs-operator">=</span> <span class="hljs-number">27</span> {
      <span class="hljs-keyword">return</span> v <span class="hljs-operator">-</span> <span class="hljs-number">27</span>;
  }

  <span class="hljs-keyword">return</span> v;
}

;; Checks ECDSA signature. Returns <span class="hljs-keyword">int</span> <span class="hljs-keyword">as</span> an outcome:
;; <span class="hljs-number">1</span>: unable to recover <span class="hljs-keyword">public</span> key
;; <span class="hljs-number">2</span>: recovered key <span class="hljs-keyword">is</span> not uncompressed
;; <span class="hljs-number">3</span>: recovered <span class="hljs-keyword">address</span> does not match the expected <span class="hljs-keyword">address</span>
;; <span class="hljs-number">-1</span> (<span class="hljs-literal">true</span>): signature <span class="hljs-keyword">is</span> valid
(<span class="hljs-keyword">int</span>) check_ecdsa_signature(<span class="hljs-keyword">int</span> hash, slice signature, slice expected_evm_address) impure inline_ref {
  ;; <span class="hljs-number">1</span> Parse (v, r, s)
  <span class="hljs-keyword">int</span> v <span class="hljs-operator">=</span> signature<span class="hljs-operator">~</span>load_uint(<span class="hljs-number">8</span>).normalize_ecdsa_recovery_id();
  <span class="hljs-keyword">int</span> r <span class="hljs-operator">=</span> signature<span class="hljs-operator">~</span>load_uint(<span class="hljs-number">256</span>);
  <span class="hljs-keyword">int</span> s <span class="hljs-operator">=</span> signature<span class="hljs-operator">~</span>load_uint(<span class="hljs-number">256</span>);

  ;; <span class="hljs-number">2.</span> Recover <span class="hljs-keyword">public</span> key
  (<span class="hljs-keyword">int</span> h, <span class="hljs-keyword">int</span> x1, <span class="hljs-keyword">int</span> x2, <span class="hljs-keyword">int</span> flag) <span class="hljs-operator">=</span> ecdsa_recover(hash, v, r, s);
  <span class="hljs-keyword">if</span> flag <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-literal">true</span> {
      <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;
  }

  ;; Deny compressed <span class="hljs-keyword">public</span> keys (<span class="hljs-number">0x04</span> prefix means uncompressed)
  <span class="hljs-keyword">if</span> h <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-number">4</span> {
      <span class="hljs-keyword">return</span> <span class="hljs-number">2</span>;
  }

  ;; <span class="hljs-number">3.</span> Derive <span class="hljs-number">20</span> <span class="hljs-keyword">bytes</span> evm <span class="hljs-keyword">address</span> <span class="hljs-keyword">from</span> the <span class="hljs-keyword">public</span> key
  <span class="hljs-keyword">int</span> pub_key_hash <span class="hljs-operator">=</span> begin_cell()
      .store_uint(x1, <span class="hljs-number">256</span>)
      .store_uint(x2, <span class="hljs-number">256</span>)
      .hash_keccak256();

  slice actual_evm_address <span class="hljs-operator">=</span> begin_cell()
      .store_uint(pub_key_hash, <span class="hljs-number">256</span>)
      .end_cell()
      .begin_parse()
      .slice_last(<span class="hljs-number">20</span> <span class="hljs-operator">*</span> <span class="hljs-number">8</span>);

  ;; <span class="hljs-number">4.</span> Compare with the expected <span class="hljs-keyword">address</span>
  <span class="hljs-keyword">if</span> equal_slices(expected_evm_address, actual_evm_address) <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-literal">false</span> {
      <span class="hljs-keyword">return</span> <span class="hljs-number">3</span>;
  }

  <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
}
</code></pre><p>And that’s a wrap. Congrats for reading this. ECDSA on TON — because why settle for one chain when you can bridge them all?</p>]]></content:encoded>
            <author>dmitrybase@newsletter.paragraph.com (dmitrybase)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/6eed9e230beec8b6fc66917254dd5220205dd494aa3e7976cce8f4836b3a817a.png" length="0" type="image/png"/>
        </item>
    </channel>
</rss>