<?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>Chakra</title>
        <link>https://paragraph.com/@chakra-papers</link>
        <description>Everything about my project Chakra</description>
        <lastBuildDate>Wed, 01 Jul 2026 18:14:25 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[The Ear That Never Sleeps]]></title>
            <link>https://paragraph.com/@chakra-papers/the-ear-that-never-sleeps</link>
            <guid>S3vrrgDhN0OgbpgdGpJF</guid>
            <pubDate>Tue, 30 Jun 2026 13:13:02 GMT</pubDate>
            <description><![CDATA[How The Sentinel Network Hears Solana Without Being Asked]]></description>
            <content:encoded><![CDATA[<p><strong>CHAKRA SERIES - 11</strong><br>Inside <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://listener.rs">listener.rs</a>: How The Sentinel Network Hears Solana Without Being Asked</p><p>Welcome to Day 11.</p><p>Day 10 closed out the entire on chain side of CHAKRA. Everything from the 64 byte tss_pubkey in <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://state.rs">state.rs</a> to the admin keypair I was honest about on Day 10, all of it done.</p><p>Today we cross over. We are no longer inside the Solana program. We are inside chakra-sentinel, the separate Rust application running on actual servers, the thing that listens, signs, and executes. And the first question to answer is the most basic one.</p><p>How does the Sentinel Network even know something happened on Solana.</p><hr><p><strong>Part 1: The Problem With Polling</strong></p><p>The naive approach would be polling. Every few seconds, ask Solana, did anything happen. Did our program emit any events. Fetch the latest transactions. Parse them. Look for something we care about.</p><p>This works but it is ugly. You miss events if your poll happens between two transactions. You add unnecessary load to the RPC. You introduce latency that grows with your poll interval. And you are paying for compute and bandwidth on every single poll even when absolutely nothing happened.</p><hr><p>CHAKRA does not poll. It subscribes.</p><p><strong>Part 2: WebSocket Subscription, The Whole Idea</strong></p><p>rust</p><pre data-type="codeBlock" text="let (_subscription, receiver) = PubsubClient::logs_subscribe(
    DEVNET_WSS,
    RpcTransactionLogsFilter::Mentions(vec![PROGRAM_ID.to_string()]),
    RpcTransactionLogsConfig {
        commitment: Some(CommitmentConfig::confirmed()),
    },
)?;"><code>let (_subscription, receiver) = PubsubClient::<span class="hljs-built_in">logs_subscribe</span>(
    DEVNET_WSS,
    RpcTransactionLogsFilter::<span class="hljs-built_in">Mentions</span>(vec![PROGRAM_ID.<span class="hljs-built_in">to_string</span>()]),
    RpcTransactionLogsConfig {
        commitment: <span class="hljs-built_in">Some</span>(CommitmentConfig::<span class="hljs-built_in">confirmed</span>()),
    },
)?;</code></pre><p>One function call. That is the whole setup. PubsubClient opens a persistent WebSocket connection to Solana's pubsub endpoint. The filter says only send me logs from transactions that mention this specific program ID. CommitmentConfig::confirmed means we only hear about transactions that have been confirmed by the cluster, not just seen by one node.</p><p>From this moment on, Solana pushes events to us. We do not ask. We just listen. The moment any transaction involving the CHAKRA program ID gets confirmed anywhere on devnet, our Sentinel process receives it over this open connection in real time.</p><p>No polling. No missed events. No unnecessary load. Just a permanent open ear.</p><hr><p><strong>Part 3: The Loop That Runs Forever</strong></p><p>rust</p><pre data-type="codeBlock" text="while let Ok(response) = receiver.recv() {
    let signature = response.value.signature.clone();
    
    for log in response.value.logs {
        if log.contains(&quot;Program data:&quot;) || log.contains(&quot;Instruction:&quot;) {
            if let Err(e) = crate::processor::IntentProcessor::handle_log(
                &amp;log, &amp;signature, &amp;shard_path, &amp;wallet_path
            ) {
                eprintln!(&quot;Error processing intent: {:?}&quot;, e);
            }
        }
    }
}"><code><span class="hljs-keyword">while</span> let Ok(response) <span class="hljs-operator">=</span> receiver.recv() {
    let signature <span class="hljs-operator">=</span> response.<span class="hljs-built_in">value</span>.signature.clone();
    
    <span class="hljs-keyword">for</span> log in response.<span class="hljs-built_in">value</span>.logs {
        <span class="hljs-keyword">if</span> log.contains(<span class="hljs-string">"Program data:"</span>) <span class="hljs-operator">|</span><span class="hljs-operator">|</span> log.contains(<span class="hljs-string">"Instruction:"</span>) {
            <span class="hljs-keyword">if</span> let Err(e) <span class="hljs-operator">=</span> crate::processor::IntentProcessor::handle_log(
                <span class="hljs-operator">&amp;</span>log, <span class="hljs-operator">&amp;</span>signature, <span class="hljs-operator">&amp;</span>shard_path, <span class="hljs-operator">&amp;</span>wallet_path
            ) {
                eprintln<span class="hljs-operator">!</span>(<span class="hljs-string">"Error processing intent: {:?}"</span>, e);
            }
        }
    }
}</code></pre><p>receiver.recv() blocks until a message arrives. When it does we get back a response containing the transaction signature and an array of log lines that transaction produced.</p><p>We loop through every log line looking for two things. Program data: is how Anchor encodes emitted events into the transaction logs, it is the prefix for any event struct we saw back in Day 6. Instruction: marks the actual instruction being called.</p><p>If we find either of those we hand the log line and the transaction signature to IntentProcessor::handle_log which lives in <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://processor.rs">processor.rs</a>. If something goes wrong parsing or handling it we print the error and keep going. The while loop does not break on a single bad log. The sentinel keeps listening no matter what.</p><hr><p><strong>Part 4: What CommitmentConfig::confirmed Actually Means And Why It Matters</strong></p><p>Solana has three commitment levels. processed means the transaction was seen by at least one node but might not survive. confirmed means it got voted on by a supermajority of validators, 66% of stake weight. finalized means it is permanently locked in and cannot be rolled back under any circumstances.</p><p>We use confirmed not finalized for a specific reason. If we waited for finalized on every ControlIntent event we would add extra latency to every single cross chain execution. confirmed is safe enough for our purposes because by the time our Sentinel processes the event, constructs the signature, sends it to Base Sepolia, waits for Base confirmation, and submits the proof back to Solana, the original transaction is going to be finalized anyway. We get the speed of confirmed with the effective safety of finalized just from the natural time the rest of the process takes.</p><hr><p>Part 5: The Discriminator Check Coming Next</p><p>There is one more thing worth mentioning even though it technically lives in <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://processor.rs">processor.rs</a>, because it is directly triggered by what <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://listener.rs">listener.rs</a> sends over.</p><p>When Anchor emits an event it encodes it as base64 data in the transaction logs with a specific 8 byte prefix called a discriminator. The discriminator for ControlIntent is the first 8 bytes of SHA256 of the string event:ControlIntent. When handle_log receives a Program data: line it decodes the base64, checks those first 8 bytes, and only proceeds if they match ControlIntent specifically.</p><p>This means even if someone tried to send fake logs mentioning the CHAKRA program ID, the discriminator check catches it before anything happens. The Sentinel does not just react to any log that mentions CHAKRA. It reacts specifically to logs that contain a properly encoded ControlIntent event which can only come from the actual CHAKRA program running the actual initialize_intent instruction.</p><hr><p><strong>Part 6: Why This Design Specifically</strong></p><p>The public wire design here, Solana transaction logs as the communication channel between the on chain program and the off chain Sentinel Network, has a property that is easy to miss until you think about it carefully.</p><p>The Sentinel Nodes do not need to trust me. They do not need a private API endpoint I control. They do not need a message queue I could tamper with. They read directly from Solana's public logs which are produced by the validators, not by me. If I disappeared tomorrow the Sentinel Nodes would still hear every ControlIntent that gets emitted because Solana keeps producing logs whether I am involved or not.</p><p>This is what pure event sourcing actually means in practice. The Sentinel Network's source of truth is the blockchain itself, not anything I control above it.</p><p>Soon we go into <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://signer.rs">signer.rs</a> which is where things get genuinely exciting. The Lagrange interpolation from Day 2 finally becomes actual running Rust code. The partial signatures being collected from multiple nodes over HTTP. The threshold math producing one valid Ethereum signature without any single node ever knowing the complete key.</p><p>See y'all tomorrow for Day 12 soon</p><hr><p><strong>Here are some resources</strong></p><p>logsSubscribe WebSocket<br>The Solana PubSub API for subscribing to transaction logs in real time.<br><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://solana.com/docs/rpc/websocket/logssubscribe">https://solana.com/docs/rpc/websocket/logssubscribe</a></p><p>Commitment Levels on Solana<br>What processed, confirmed, and finalized actually mean and when to use each.<br><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.solanalabs.com/consensus/commitments">https://docs.solanalabs.com/consensus/commitments</a></p><hr><br><figure float="none" data-type="figure" class="img-center"><img src="https://storage.googleapis.com/papyrus_images/b34c4310ed4a8429f54429ccfb1ffb8f03bc3657fa9f8f332bb4e2ae58b0ab84.avif" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACkQBADs=" nextheight="234" nextwidth="222" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>Written by Maha.. thankyou for reading</p><p>@solana @web3 @rust @anchor</p>]]></content:encoded>
            <author>chakra-papers@newsletter.paragraph.com (Maha)</author>
            <category>@solana</category>
            <category>@web3</category>
            <category>@anchor</category>
            <category>@rust</category>
            <enclosure url="https://storage.googleapis.com/papyrus_images/cccd2c06c699b5f30f5ae8e40ec824012b9a33857cd3d9bdd3475bfa150056b3.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Who Holds The Keys]]></title>
            <link>https://paragraph.com/@chakra-papers/who-holds-the-keys</link>
            <guid>oPSxcYPGpAGooBS997BA</guid>
            <pubDate>Tue, 30 Jun 2026 12:54:15 GMT</pubDate>
            <description><![CDATA[The absolute power it holds over our core trust layer, and the centralization I refuse to hide.]]></description>
            <content:encoded><![CDATA[<p><strong>CHAKRA SERIES - 10</strong></p><p><strong>Who Holds The Keys</strong> Inside <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://admin.rs">admin.rs</a>: The Control Panel Behind CHAKRA, And The Centralization I Am Not Hiding</p><p>Welcome to Day 10. Again continuing the docs</p><p>Day 7, 8 and 9 covered the three instructions a normal user or a Sentinel Node would ever call. initialize_intent starts an intent. cancel_intent rescues it if nothing happens. submit_proof finishes it if everything happened correctly.</p><p>There is one file left in the instructions folder. <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://admin.rs">admin.rs</a>. And I saved it for last on purpose, because while it is the file regular users will basically never touch, it is also if I am being completely honest the most powerful file in the entire program.</p><hr><p><strong>Part 1: Why This File Is Different</strong></p><p>Every file we have looked at so far does something because a user did something. Locked funds because a user called initialize_intent. Refunded because a user called cancel_intent.</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://admin.rs">admin.rs</a> is the opposite. It is the protocol talking to itself. It is how the three config accounts from Day 4, TssConfig, GlobalConfig, and SentinelAccount, actually get created and updated in the first place. Without this file running first, none of the other instructions can even start. The escrow has no treasury address. submit_proof has no tss_pubkey to check against. The sentinel registry is empty so every proof gets rejected before the body even runs.</p><p>Four instructions live here. initialize_tss_config, update_tss_config, initialize_config, and the pair add_sentinel and remove_sentinel through a shared ManageSentinel struct. Let me go through each one.</p><hr><p><strong>Part 2: initialize_tss_config, The Most Important One Time Call In The Program</strong></p><p>rust</p><pre data-type="codeBlock" text="pub fn handle_initialize_tss_config(
    ctx: Context&lt;InitializeTssConfig&gt;,
    tss_pubkey: [u8; 64],
    threshold: u8,
    total_nodes: u8,
) -&gt; Result&lt;()&gt; {
    let tss_config = &amp;mut ctx.accounts.tss_config;
    tss_config.tss_pubkey = tss_pubkey;
    tss_config.threshold = threshold;
    tss_config.total_nodes = total_nodes;
    tss_config.admin = ctx.accounts.admin.key();
    tss_config.bump = ctx.bumps.tss_config;
    Ok(())
}
"><code>pub fn handle_initialize_tss_config(
    ctx: Context<span class="hljs-operator">&lt;</span>InitializeTssConfig<span class="hljs-operator">&gt;</span>,
    tss_pubkey: [u8; <span class="hljs-number">64</span>],
    threshold: u8,
    total_nodes: u8,
) <span class="hljs-operator">-</span><span class="hljs-operator">&gt;</span> Result<span class="hljs-operator">&lt;</span>()<span class="hljs-operator">&gt;</span> {
    let tss_config <span class="hljs-operator">=</span> <span class="hljs-operator">&amp;</span>mut ctx.accounts.tss_config;
    tss_config.tss_pubkey <span class="hljs-operator">=</span> tss_pubkey;
    tss_config.threshold <span class="hljs-operator">=</span> threshold;
    tss_config.total_nodes <span class="hljs-operator">=</span> total_nodes;
    tss_config.admin <span class="hljs-operator">=</span> ctx.accounts.admin.key();
    tss_config.bump <span class="hljs-operator">=</span> ctx.bumps.tss_config;
    Ok(())
}
</code></pre><p>Remember Day 4 where I said the 64 byte tss_pubkey field is the single most important field in the entire protocol, the thing every submit_proof call gets checked against on Day 9. This is the function that writes it for the very first time.</p><p>Whoever calls this becomes tss_config.admin permanently for that account. And this tss_pubkey has to exactly match the public key whose shards are sitting across the three Sentinel Nodes from Day 2. If this value is wrong by even one byte at this moment, every single submit_proof from then on fails with InvalidProof forever, until someone with admin access fixes it with the next function.</p><hr><p><strong>Part 3: update_tss_config, The Key Rotation Door</strong></p><p>rust</p><pre data-type="codeBlock" text="#[account(
    mut,
    seeds = [b&quot;tss_config&quot;],
    bump = tss_config.bump,
    constraint = tss_config.admin == admin.key()
)]
pub tss_config: Account&lt;'info, TssConfig&gt;,
"><code>#[account(
    mut,
    seeds <span class="hljs-operator">=</span> [b<span class="hljs-string">"tss_config"</span>],
    bump <span class="hljs-operator">=</span> tss_config.bump,
    constraint <span class="hljs-operator">=</span> tss_config.admin <span class="hljs-operator">=</span><span class="hljs-operator">=</span> admin.key()
)]
pub tss_config: Account<span class="hljs-operator">&lt;</span><span class="hljs-string">'info, TssConfig&gt;,
</span></code></pre><p>One constraint. tss_config.admin == admin.key(). That is the entire gate. If you are the admin pubkey stored back when initialize_tss_config ran, you can call this and overwrite tss_pubkey, threshold, and total_nodes with new values.</p><p>Why does this exist. Now chakra uses Shamir Secret Sharing with the coordinator briefly reconstructing the key in memory. Soon upgrades to true FROST where the key never gets reconstructed anywhere ever. When that upgrade happens the way the master public key is derived changes completely. This function is the door that lets CHAKRA walk through that upgrade without redeploying the entire Anchor program. Same program, same escrow logic, same error codes, just a new tss_pubkey registered through this one call.</p><hr><p><strong>Part 4: initialize_config, And The is_initialized Guard</strong></p><p>rust</p><pre data-type="codeBlock" text="pub fn handle_initialize_config(
    ctx: Context&lt;InitializeConfig&gt;,
    treasury: Pubkey,
) -&gt; Result&lt;()&gt; {
    let config = &amp;mut ctx.accounts.config;
    config.admin = ctx.accounts.admin.key();
    config.treasury = treasury;
    config.is_initialized = true;
    config.bump = ctx.bumps.config;
    Ok(())
}
"><code>pub fn handle_initialize_config(
    ctx: Context<span class="hljs-operator">&lt;</span>InitializeConfig<span class="hljs-operator">&gt;</span>,
    treasury: Pubkey,
) <span class="hljs-operator">-</span><span class="hljs-operator">&gt;</span> Result<span class="hljs-operator">&lt;</span>()<span class="hljs-operator">&gt;</span> {
    let config <span class="hljs-operator">=</span> <span class="hljs-operator">&amp;</span>mut ctx.accounts.config;
    config.admin <span class="hljs-operator">=</span> ctx.accounts.admin.key();
    config.treasury <span class="hljs-operator">=</span> treasury;
    config.is_initialized <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
    config.bump <span class="hljs-operator">=</span> ctx.bumps.config;
    Ok(())
}
</code></pre><p>This sets config.treasury, which is exactly where the escrowed lamports go on a successful submit_proof, the thing I was honest about on Day 9 being still a work in progress.</p><p>is_initialized = true here is small but it matters. Because config lives at a fixed PDA seeded only by b"config", there is exactly one GlobalConfig account that can ever exist. The init constraint already stops a second initialize_config call from succeeding on the same address, but is_initialized as an explicit flag gives any future instruction an easy cheap way to check whether setup actually happened without relying purely on account existence. Belt and suspenders, same philosophy as the booleans in EscrowState from Day 4.</p><hr><p><strong>Part 5: ManageSentinel, Growing The Network One Node At A Time</strong></p><p>rust</p><pre data-type="codeBlock" text="pub fn handle_add_sentinel(
    ctx: Context&lt;ManageSentinel&gt;,
    sentinel_pubkey: Pubkey,
) -&gt; Result&lt;()&gt; {
    let sentinel = &amp;mut ctx.accounts.sentinel_account;
    sentinel.sentinel_pubkey = sentinel_pubkey;
    sentinel.is_active = true;
    sentinel.bump = ctx.bumps.sentinel_account;
    Ok(())
}

pub fn handle_remove_sentinel(
    ctx: Context&lt;ManageSentinel&gt;,
    _sentinel_pubkey: Pubkey,
) -&gt; Result&lt;()&gt; {
    let sentinel = &amp;mut ctx.accounts.sentinel_account;
    sentinel.is_active = false;
    Ok(())
}
"><code>pub fn handle_add_sentinel(
    ctx: Context<span class="hljs-operator">&lt;</span>ManageSentinel<span class="hljs-operator">&gt;</span>,
    sentinel_pubkey: Pubkey,
) <span class="hljs-operator">-</span><span class="hljs-operator">&gt;</span> Result<span class="hljs-operator">&lt;</span>()<span class="hljs-operator">&gt;</span> {
    let sentinel <span class="hljs-operator">=</span> <span class="hljs-operator">&amp;</span>mut ctx.accounts.sentinel_account;
    sentinel.sentinel_pubkey <span class="hljs-operator">=</span> sentinel_pubkey;
    sentinel.is_active <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
    sentinel.bump <span class="hljs-operator">=</span> ctx.bumps.sentinel_account;
    Ok(())
}

pub fn handle_remove_sentinel(
    ctx: Context<span class="hljs-operator">&lt;</span>ManageSentinel<span class="hljs-operator">&gt;</span>,
    _sentinel_pubkey: Pubkey,
) <span class="hljs-operator">-</span><span class="hljs-operator">&gt;</span> Result<span class="hljs-operator">&lt;</span>()<span class="hljs-operator">&gt;</span> {
    let sentinel <span class="hljs-operator">=</span> <span class="hljs-operator">&amp;</span>mut ctx.accounts.sentinel_account;
    sentinel.is_active <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>;
    Ok(())
}
</code></pre><p>init_if_needed in the accounts struct is doing something specific here. Normally init means create this account and it fails if the account already exists. But CHAKRA does not know in advance how many sentinel nodes will ever exist across its lifetime. init_if_needed says create this PDA if this is the first time we are seeing this sentinel_pubkey, otherwise just use the existing one. That is how add_sentinel works identically for the very first node and the fiftieth node.</p><p>And notice remove_sentinel does not delete anything. It just sets is_active = false. The account stays on chain forever. This is the soft delete pattern, same is_active check from submit_proof on Day 9. If a node ever gets compromised, flipping this one boolean instantly and permanently locks that node out of every future submit_proof, while still leaving a permanent public record that it happened.</p><hr><p><strong>Part 6: The Part I Actually Want To Talk About</strong></p><p>Here is the honest thing about every constraint in this entire file. config.admin == admin.key(). tss_config.admin == admin.key(). Right now on devnet all of those admin keys are the same keypair. My keypair. Sitting in one file on my laptop.</p><p>That means right now one private key has the power to register a completely different tss_pubkey through update_tss_config, add or deactivate any sentinel through ManageSentinel, and effectively rewrite the entire trust model from Day 9 from the outside.</p><p>I want to say this loudly instead of quietly because I think it is more important than almost anything else in this series. If this exact setup were ever deployed to mainnet as is and that one keypair got compromised, an attacker would not need to break secp256k1 or compromise two Sentinel Nodes at all. They could just call update_tss_config with their own key, then submit fake proofs that pass against their own key, and drain every escrow. Without touching any cryptography at all.</p><p>For protocol on devnet run by one person this is normal and honestly the only way I could move this fast alone. But it cannot be the end state. Before any real funds are involved this admin authority has to move off a single keypair. Things I am actively thinking about, a multisig so updates need multiple signers, or a time lock so any update_tss_config call has a mandatory delay before it takes effect giving everyone time to notice if something looks wrong.</p><p>That is <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://admin.rs">admin.rs</a>. That is the complete on chain side of CHAKRA. </p><hr><p>Tomorrow we cross over to the other side entirely. The Sentinel Network. The actual Rust processes that heard the ControlIntent event from Day 6 and now have to do something about it.</p><p>See y'all tomorrow for Day 11.</p><hr><p>Here are some resources</p><p>init_if_needed in Anchor How conditional account initialization works and when it is safe to use. <br><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.anchor-lang.com/docs/references/account-constraints#init_if_needed">https://www.anchor-lang.com/docs/references/account-constraints#init_if_needed</a></p><p>Multisig on Solana How multisig programs work, relevant to the centralization point above. <br><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.squads.so">https://docs.squads.so</a></p><br><figure float="none" data-type="figure" class="img-center"><img src="https://storage.googleapis.com/papyrus_images/b34c4310ed4a8429f54429ccfb1ffb8f03bc3657fa9f8f332bb4e2ae58b0ab84.avif" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACkQBADs=" nextheight="234" nextwidth="222" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p> Written by Maha.. thankyou for reading</p><p>@solana @web3 @anchor @rust</p><br>]]></content:encoded>
            <author>chakra-papers@newsletter.paragraph.com (Maha)</author>
            <category>@solana</category>
            <category>@web3</category>
            <category>@anchor</category>
            <category>@rust</category>
            <enclosure url="https://storage.googleapis.com/papyrus_images/282ab0b8057d2e6f17cb3d3eba1750b0504d5596dd1a1e363342f026fc32f876.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Where Math Meets Money]]></title>
            <link>https://paragraph.com/@chakra-papers/where-math-meets-money</link>
            <guid>0gEdG776604qWLwbeFdD</guid>
            <pubDate>Sun, 14 Jun 2026 07:40:03 GMT</pubDate>
            <description><![CDATA[When the threshold signatures land back on Solana, raw cryptography handles the checkout.]]></description>
            <content:encoded><![CDATA[<p><strong>CHAKRA SERIES - 9</strong></p><p><strong>Where Math Meets Money</strong><br>Inside submit_<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://proof.rs">proof.rs</a>: The On Chain Moment secp256k1_recover Decides Everything</p><p>Welcome to Day 9.</p><p>Day 7 was the start. initialize_<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://intent.rs">intent.rs</a> locks the funds and fires the signal. Day 8 was the escape hatch. cancel_<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://intent.rs">intent.rs</a> is what happens if nothing comes back in time.</p><p>Today is the third path. The happy path. The Sentinel Network heard the signal from Day 7, ran the 2 of 3 threshold signing ceremony I described back on Day 2, executed on Base Sepolia, and now comes back to Solana with proof that it actually happened.</p><p>This file is <code>submit_proof.rs</code>, and on Day 5 when I introduced InvalidProof I said this is the moment where math meets money. Today you get to see that moment in actual code.</p><hr><p><strong>Part 1: The Accounts Struct, Five New Faces</strong></p><p>rust</p><pre data-type="codeBlock" text="#[derive(Accounts)]
pub struct SubmitProof&lt;'info&gt; {
#[account(mut)]
pub sentinel: Signer&lt;'info&gt;,
#[account(
    seeds = [b&quot;sentinel&quot;, sentinel.key().as_ref()],
    bump = sentinel_auth.bump,
    constraint = sentinel_auth.is_active @ ChakraError::UnauthorizedSentinel
)]
pub sentinel_auth: Account&lt;'info, SentinelAccount&gt;,

#[account(
    seeds = [b&quot;config&quot;],
    bump = config.bump,
)]
pub config: Account&lt;'info, GlobalConfig&gt;,

#[account(
    mut,
    seeds = [
        b&quot;escrow&quot;,
        escrow_account.owner.as_ref(),
        &amp;escrow_account.target_chain_id.to_le_bytes(),
        &amp;escrow_account.nonce.to_le_bytes()
    ],
    bump = escrow_account.bump,
    close = treasury
)]
pub escrow_account: Box&lt;Account&lt;'info, EscrowState&gt;&gt;,

#[account(
    seeds = [b&quot;tss_config&quot;],
    bump = tss_config.bump,
)]
pub tss_config: Account&lt;'info, TssConfig&gt;,

/// CHECK: Treasury wallet from config
#[account(mut, address = config.treasury)]
pub treasury: AccountInfo&lt;'info&gt;,

pub system_program: Program&lt;'info, System&gt;,
}"><code><span class="hljs-comment">#[derive(Accounts)]</span>
pub struct SubmitProof&lt;'info&gt; {
<span class="hljs-comment">#[account(mut)]</span>
pub sentinel: Signer&lt;'info&gt;,
<span class="hljs-comment">#[account(</span>
    <span class="hljs-attr">seeds</span> = [b<span class="hljs-string">"sentinel"</span>, sentinel.key().as_ref()],
    <span class="hljs-attr">bump</span> = sentinel_auth.bump,
    <span class="hljs-attr">constraint</span> = sentinel_auth.is_active @ ChakraError::UnauthorizedSentinel
)]
pub sentinel_auth: Account&lt;'info, SentinelAccount&gt;,

<span class="hljs-comment">#[account(</span>
    <span class="hljs-attr">seeds</span> = [b<span class="hljs-string">"config"</span>],
    <span class="hljs-attr">bump</span> = config.bump,
)]
pub config: Account&lt;'info, GlobalConfig&gt;,

<span class="hljs-comment">#[account(</span>
    mut,
    <span class="hljs-attr">seeds</span> = [
        b<span class="hljs-string">"escrow"</span>,
        escrow_account.owner.as_ref(),
        &amp;escrow_account.target_chain_id.to_le_bytes(),
        &amp;escrow_account.nonce.to_le_bytes()
    ],
    <span class="hljs-attr">bump</span> = escrow_account.bump,
    <span class="hljs-attr">close</span> = treasury
)]
pub escrow_account: Box&lt;Account&lt;'info, EscrowState&gt;&gt;,

<span class="hljs-comment">#[account(</span>
    <span class="hljs-attr">seeds</span> = [b<span class="hljs-string">"tss_config"</span>],
    <span class="hljs-attr">bump</span> = tss_config.bump,
)]
pub tss_config: Account&lt;'info, TssConfig&gt;,

/// CHECK: Treasury wallet from config
<span class="hljs-comment">#[account(mut, address = config.treasury)]</span>
pub treasury: AccountInfo&lt;'info&gt;,

pub system_program: Program&lt;'info, System&gt;,
}</code></pre><p>sentinel is the Signer here, not the user. This whole instruction is called by a Sentinel Node, not by the person who started the intent.</p><p>sentinel_auth checks constraint = sentinel_<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://auth.is">auth.is</a>_active. This is the SentinelAccount registry from Day 4 and the UnauthorizedSentinel error from Day 5, finally being used. Even if someone has somehow produced a perfect TSS signature, if they are not a registered active sentinel, this constraint stops them right here, before the function body even runs.</p><p>escrow_account is wrapped in Box&lt;&gt;. Quick note on that, Box just moves the account data onto the heap instead of the stack. Anchor instructions have a limited stack size, and EscrowState at 211 bytes plus all the other accounts in this struct can get close to that limit. Boxing it is a common pattern to stay safely under the limit.</p><p>And look at close = treasury this time, not close = owner like yesterday. I will come back to this in Part 5.</p><hr><p><strong>Part 2: The Familiar Gatekeepers</strong></p><p>rust</p><pre data-type="codeBlock" text="require!(!escrow.is_finalized, ChakraError::AlreadyFinalized);
require!(!escrow.is_cancelled, ChakraError::AlreadyCancelled);
require!(clock.slot &lt;= escrow.timeout_slot, ChakraError::TimeoutReached);"><code><span class="hljs-built_in">require</span><span class="hljs-operator">!</span>(<span class="hljs-operator">!</span>escrow.is_finalized, ChakraError::AlreadyFinalized);
<span class="hljs-built_in">require</span><span class="hljs-operator">!</span>(<span class="hljs-operator">!</span>escrow.is_cancelled, ChakraError::AlreadyCancelled);
<span class="hljs-built_in">require</span><span class="hljs-operator">!</span>(clock.slot <span class="hljs-operator">&lt;</span><span class="hljs-operator">=</span> escrow.timeout_slot, ChakraError::TimeoutReached);</code></pre><p>Same family of checks as cancel_intent, but flipped. Cancel needed the timeout to have passed. Submit proof needs the timeout to NOT have passed yet. TimeoutReached is the mirror image of TimeoutNotReached from yesterday. Together, these two checks across two files are what guarantee an intent can only ever land in exactly one final state.</p><hr><p><strong>Part 3: Rebuilding The Message, And Why Big Endian</strong></p><p>Now here is the part I told you to remember from Day 7.</p><p>rust</p><pre data-type="codeBlock" text="let mut msg_data = Vec::with_capacity(8 + 8 + 8 + 64);
msg_data.extend_from_slice(&amp;escrow.target_chain_id.to_be_bytes());
msg_data.extend_from_slice(&amp;escrow.nonce.to_be_bytes());
msg_data.extend_from_slice(&amp;escrow.amount.to_be_bytes());
msg_data.extend_from_slice(&amp;escrow.target_address);

let msg_hash = keccak256(&amp;msg_data).to_bytes();"><code>let mut msg_data <span class="hljs-operator">=</span> Vec::with_capacity(<span class="hljs-number">8</span> <span class="hljs-operator">+</span> <span class="hljs-number">8</span> <span class="hljs-operator">+</span> <span class="hljs-number">8</span> <span class="hljs-operator">+</span> <span class="hljs-number">64</span>);
msg_data.extend_from_slice(<span class="hljs-operator">&amp;</span>escrow.target_chain_id.to_be_bytes());
msg_data.extend_from_slice(<span class="hljs-operator">&amp;</span>escrow.nonce.to_be_bytes());
msg_data.extend_from_slice(<span class="hljs-operator">&amp;</span>escrow.amount.to_be_bytes());
msg_data.extend_from_slice(<span class="hljs-operator">&amp;</span>escrow.target_address);

let msg_hash <span class="hljs-operator">=</span> <span class="hljs-built_in">keccak256</span>(<span class="hljs-operator">&amp;</span>msg_data).to_bytes();</code></pre><p>to_be_bytes(). Big endian. Back in initialize_<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://intent.rs">intent.rs</a>, the PDA seeds used to_le_bytes(), little endian, because that is just the normal Rust and Solana convention for deriving addresses, nobody outside Solana ever needs to recompute those seeds.</p><p>But this msg_data right here is different. This exact byte sequence, in this exact order, with this exact endianness, has to be reproduced perfectly by ChakraReceiver.sol on Base Sepolia, because the Sentinel Network signed a hash of this same data, and the contract on the other side needs to recompute the identical hash to verify that signature.</p><p>Ethereum and EVM chains use big endian as their convention. So this is CHAKRA speaking Ethereum's language on purpose, right here, in the middle of a Solana program. Get this wrong by even the endianness of one field, and the hash on Solana will never match the hash on Base, and every single proof will fail forever.</p><p>And keccak256, not sha256. Why. Because Ethereum's ECDSA signatures are produced over keccak256 hashes. If the Sentinel Nodes signed a keccak256 hash back during the signing ceremony from Day 2, then Solana has to also hash with keccak256 here, or the recovered key will be garbage.</p><hr><p><strong>Part 4: secp256k1_recover, The Syscall That Makes This Whole Project Possible</strong></p><p>rust</p><pre data-type="codeBlock" text="let mut sig_bytes = [0u8; 64];
sig_bytes[0..32].copy_from_slice(&amp;signature_r);
sig_bytes[32..64].copy_from_slice(&amp;signature_s);

let recovery_id = signature_v
.checked_sub(27)
.ok_or(ChakraError::InvalidProof)?;

let recovered_pubkey = secp256k1_recover(&amp;msg_hash, recovery_id, &amp;sig_bytes)
.map_err(|_| ChakraError::InvalidProof)?;

require!(
recovered_pubkey.to_bytes() == tss_config.tss_pubkey,
ChakraError::InvalidProof
);

escrow.is_finalized = true;"><code>let mut sig_bytes <span class="hljs-operator">=</span> [0u8; <span class="hljs-number">64</span>];
sig_bytes[<span class="hljs-number">0</span>..32].copy_from_slice(<span class="hljs-operator">&amp;</span>signature_r);
sig_bytes[<span class="hljs-number">32.</span><span class="hljs-number">.64</span>].copy_from_slice(<span class="hljs-operator">&amp;</span>signature_s);

let recovery_id <span class="hljs-operator">=</span> signature_v
.checked_sub(<span class="hljs-number">27</span>)
.ok_or(ChakraError::InvalidProof)?;

let recovered_pubkey <span class="hljs-operator">=</span> secp256k1_recover(<span class="hljs-operator">&amp;</span>msg_hash, recovery_id, <span class="hljs-operator">&amp;</span>sig_bytes)
.map_err(<span class="hljs-operator">|</span><span class="hljs-keyword">_</span><span class="hljs-operator">|</span> ChakraError::InvalidProof)?;

<span class="hljs-built_in">require</span><span class="hljs-operator">!</span>(
recovered_pubkey.to_bytes() <span class="hljs-operator">=</span><span class="hljs-operator">=</span> tss_config.tss_pubkey,
ChakraError::InvalidProof
);

escrow.is_finalized <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;</code></pre><p>A quick note on signature_v.checked_sub(27) first. In Ethereum, the recovery id v is encoded as either 27 or 28, an old historical convention. ECDSA recovery actually only needs a recovery id of 0 or 1. So we subtract 27 to convert back. And notice it is checked_sub, not a plain subtraction. If somehow signature_v came in less than 27, a plain subtraction would underflow a u8 and wrap around to some huge number. checked_sub catches that and returns InvalidProof instead. One more quiet little safety check, same family as the ones from Day 5.</p><p>Now, secp256k1_recover. This is the single most important function call in the entire program. Given a message hash, a recovery id, and a signature, it mathematically recovers the public key that must have produced that signature. Not "checks against" a public key. Recovers one, from nothing but the hash and the signature.</p><p>This is a native syscall on Solana, meaning it runs as actual compiled machine instructions inside the validator, not as some slow library running inside the program's own bytecode. That is a big deal. This is part of why CHAKRA can exist on Solana specifically. Verifying an Ethereum style ECDSA signature, cheaply, on chain, in a single instruction, is something most chains simply cannot do natively.</p><p>And then the line that everything in this entire series has been building toward.</p><p>recovered_<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://pubkey.to">pubkey.to</a>_bytes() == tss_config.tss_pubkey</p><p>That's it. That is the whole trust model of CHAKRA, on one line. Remember the 64 byte tss_pubkey from Day 4, the field I said everything depends on being correct. This is the line it was waiting for. If the recovered key matches, the proof is real, is_finalized flips to true, and emit!(IntentFinalized { escrow_pda, tx_hash }) fires, the same IntentFinalized event from Day 6.</p><p>If it does not match, even by one byte, InvalidProof fires, the transaction reverts, is_finalized stays false, and the escrow stays exactly where it was, still cancellable later if needed. Nothing in between. No partial trust.</p><hr><p><strong>Part 5: Where The Money Actually Goes</strong></p><p>Let's look at one final design detail honestly.</p><p>When this instruction succeeds, <code>close = treasury</code> routes the escrowed lamports straight to <code>config.treasury</code> instead of returning them to the user. This is the exact opposite of <code>cancel_intent.rs</code>, where funds go back to the original owner.</p><p>For our Milestone 1 release, these locked tokens act as the fee that compensates the Sentinel Network for doing the actual cross-chain heavy lifting on the other side. Right now, <code>treasury</code> is a placeholder destination while I map out the long-term mechanics. Once the network expands past me running all three nodes, this pool will dynamically distribute payouts to decentralized node operators based on performance. It’s a Milestone 1 framework, not the final mainnet economic model—but keeping it completely transparent here is better than pretending it's fully set in stone</p><hr><p><strong>Part 6: The Whole Series, In One Line</strong></p><p>If you asked me to reduce CHAKRA’s entire security apparatus to a single sentence, it would be this: Every moving part before this moment—the Distributed Key Generation, the Shamir Secret Sharing, and the Lagrange interpolation—exists solely to reconstruct a 64-byte public key on this exact line of code that matches <code>tss_config.tss_pubkey</code>.</p><p>An attacker who hasn't compromised a 2-of-3 quorum cannot make those bytes match. Not because a rule tells them no, but because the mathematics won't allow it.</p><hr><h3 id="h-whats-next" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">What's Next</h3><p>I am officially pausing the writing train for the next few days. I've shown you the complete architectural vision, the custom errors, the off-chain wire, and the cryptographic proof engine. Now, it's time to put down the essays and code the hell out of CHAKRA.</p><p>See you all on the other side. Thank you for reading!</p><hr><p>Here are some final resources for y'all</p><div data-type="embedly" src="https://docs.rs/solana-secp256k1-recover/latest/solana_secp256k1_recover/" data="{&quot;provider_url&quot;:&quot;https://docs.rs&quot;,&quot;description&quot;:&quot;Public key recovery from secp256k1 ECDSA signatures.&quot;,&quot;title&quot;:&quot;solana_secp256k1_recover - Rust&quot;,&quot;url&quot;:&quot;https://docs.rs/solana-secp256k1-recover/latest/solana_secp256k1_recover/&quot;,&quot;version&quot;:&quot;1.0&quot;,&quot;provider_name&quot;:&quot;Docs&quot;,&quot;type&quot;:&quot;link&quot;}" format="small"><div class="react-component embed my-5" data-drag-handle="true" data-node-view-wrapper="" style="white-space:normal"><a class="link-embed-link" href="https://docs.rs/solana-secp256k1-recover/latest/solana_secp256k1_recover/" target="_blank" rel="noreferrer"><div class="link-embed"><div class="flex-1"><div><h2>solana_secp256k1_recover - Rust</h2><p>Public key recovery from secp256k1 ECDSA signatures.</p></div><span><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-link h-3 w-3 my-auto inline mr-1"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>https://docs.rs</span></div></div></a></div></div><div data-type="embedly" src="https://ethereum.org/developers/docs/data-structures-and-encoding/" data="{&quot;provider_url&quot;:&quot;https://ethereum.org&quot;,&quot;description&quot;:&quot;An overview of the fundamental Ethereum data structures.&quot;,&quot;title&quot;:&quot;Data structures and encoding | ethereum.org&quot;,&quot;thumbnail_width&quot;:1504,&quot;url&quot;:&quot;https://ethereum.org/developers/docs/data-structures-and-encoding/&quot;,&quot;thumbnail_url&quot;:&quot;https://storage.googleapis.com/papyrus_images/a2f4796307f924bab5bfc9c7cc5d2e56c6d4cd00a64075af07b94771f8b35745.png&quot;,&quot;version&quot;:&quot;1.0&quot;,&quot;provider_name&quot;:&quot;ethereum.org&quot;,&quot;type&quot;:&quot;link&quot;,&quot;thumbnail_height&quot;:940,&quot;image&quot;:{&quot;base64&quot;:&quot;data:image/png;base64,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&quot;,&quot;img&quot;:{&quot;width&quot;:1504,&quot;height&quot;:940,&quot;src&quot;:&quot;https://storage.googleapis.com/papyrus_images/a2f4796307f924bab5bfc9c7cc5d2e56c6d4cd00a64075af07b94771f8b35745.png&quot;}}}" format="small"><link rel="preload" as="image" href="https://storage.googleapis.com/papyrus_images/a2f4796307f924bab5bfc9c7cc5d2e56c6d4cd00a64075af07b94771f8b35745.png"><div class="react-component embed my-5" data-drag-handle="true" data-node-view-wrapper="" style="white-space:normal"><a class="link-embed-link" href="https://ethereum.org/developers/docs/data-structures-and-encoding/" target="_blank" rel="noreferrer"><div class="link-embed"><div class="flex-1"><div><h2>Data structures and encoding | ethereum.org</h2><p>An overview of the fundamental Ethereum data structures.</p></div><span><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-link h-3 w-3 my-auto inline mr-1"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>https://ethereum.org</span></div><img src="https://storage.googleapis.com/papyrus_images/a2f4796307f924bab5bfc9c7cc5d2e56c6d4cd00a64075af07b94771f8b35745.png" alt="Data structures and encoding | ethereum.org"></div></a></div></div><div data-type="embedly" src="https://www.anchor-lang.com/docs/references/account-constraints" data="{&quot;provider_url&quot;:&quot;https://www.anchor-lang.com&quot;,&quot;description&quot;:&quot;Anchor Account Constraints Examples&quot;,&quot;title&quot;:&quot;Account Constraints&quot;,&quot;url&quot;:&quot;https://www.anchor-lang.com/docs/references/account-constraints&quot;,&quot;version&quot;:&quot;1.0&quot;,&quot;provider_name&quot;:&quot;Anchor-lang&quot;,&quot;type&quot;:&quot;link&quot;}" format="small"><div class="react-component embed my-5" data-drag-handle="true" data-node-view-wrapper="" style="white-space:normal"><a class="link-embed-link" href="https://www.anchor-lang.com/docs/references/account-constraints" target="_blank" rel="noreferrer"><div class="link-embed"><div class="flex-1"><div><h2>Account Constraints</h2><p>Anchor Account Constraints Examples</p></div><span><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-link h-3 w-3 my-auto inline mr-1"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>https://www.anchor-lang.com</span></div></div></a></div></div><div data-type="youtube" videoid="_vl-okF3cs8">
      <div class="youtube-player" data-id="_vl-okF3cs8" style="background-image: url('https://i.ytimg.com/vi/_vl-okF3cs8/hqdefault.jpg'); background-size: cover; background-position: center">
        <a href="https://www.youtube.com/watch?v=_vl-okF3cs8">
          <img src="https://paragraph.com/editor/youtube/play.png" class="play">
        </a>
      </div></div><hr><figure float="none" data-type="figure" class="img-center"><img src="https://storage.googleapis.com/papyrus_images/b34c4310ed4a8429f54429ccfb1ffb8f03bc3657fa9f8f332bb4e2ae58b0ab84.avif" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACkQBADs=" nextheight="234" nextwidth="222" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><br>]]></content:encoded>
            <author>chakra-papers@newsletter.paragraph.com (Maha)</author>
            <category>@solana</category>
            <category>@web3</category>
            <category>@rust</category>
            <category>@ethereum</category>
            <category>@anchor</category>
            <enclosure url="https://storage.googleapis.com/papyrus_images/1ecb522d5fede5e6fefb959f695b3fbf75f8e71bb1dbf391eacc4f1c4c02d14d.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[The Escape Hatch: Why Ten Lines of Rust Are Enough]]></title>
            <link>https://paragraph.com/@chakra-papers/the-escape-hatch-why-ten-lines-of-rust-are-enough</link>
            <guid>hzD315LCwsiV9rrSkMc2</guid>
            <pubDate>Sun, 14 Jun 2026 07:22:39 GMT</pubDate>
            <description><![CDATA[When everything goes wrong, ten lines of code are all it takes to get your money back.]]></description>
            <content:encoded><![CDATA[<h2 id="h-chakra-series-8" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">CHAKRA SERIES - 8</h2><p><strong><em>The Three Lines That Save You</em></strong><em><br>Inside</em><code> cancel_intent.rs</code><em>: The Escape Hatch Every User Hopes They Never Need</em></p><p><strong><em>Hello again Welcome to Day 8</em></strong><em>.</em></p><p><em>In part 7 we saw initialize_</em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://intent.rs"><em>intent.rs</em></a><em>. The longest instruction in CHAKRA. Validation, escrow creation, a CPI transfer, an event emission. A lot happens in that file.</em></p><p><em>Today is the opposite. Today's file is cancel_</em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://intent.rs"><em>intent.rs</em></a><em>, and I want to warn you in advance, it is going to feel almost anticlimactic. The actual handler function is ten lines long. Maybe less depending how you count.</em></p><p><em>But I think this might be my favorite file in the whole program, because of what those ten lines represent.</em></p><hr><p><strong><em>Part 1: The Whole Function, Right Here</em></strong></p><p><em>rust</em></p><pre data-type="codeBlock" text="pub fn handle_cancel_intent(ctx: Context&lt;CancelIntent&gt;) -&gt; Result&lt;()&gt; {
let escrow = &amp;ctx.accounts.escrow_account;
let clock = Clock::get()?;

require!(
    clock.slot &gt; escrow.timeout_slot,
    ChakraError::TimeoutNotReached
);

require!(!escrow.is_finalized, ChakraError::AlreadyFinalized);
require!(!escrow.is_cancelled, ChakraError::AlreadyCancelled);

Ok(())
}"><code>pub fn handle_cancel_intent(ctx: Context<span class="hljs-operator">&lt;</span>CancelIntent<span class="hljs-operator">&gt;</span>) <span class="hljs-operator">-</span><span class="hljs-operator">&gt;</span> Result<span class="hljs-operator">&lt;</span>()<span class="hljs-operator">&gt;</span> {
let escrow <span class="hljs-operator">=</span> <span class="hljs-operator">&amp;</span>ctx.accounts.escrow_account;
let clock <span class="hljs-operator">=</span> Clock::get()?;

<span class="hljs-built_in">require</span><span class="hljs-operator">!</span>(
    clock.slot <span class="hljs-operator">&gt;</span> escrow.timeout_slot,
    ChakraError::TimeoutNotReached
);

<span class="hljs-built_in">require</span><span class="hljs-operator">!</span>(<span class="hljs-operator">!</span>escrow.is_finalized, ChakraError::AlreadyFinalized);
<span class="hljs-built_in">require</span><span class="hljs-operator">!</span>(<span class="hljs-operator">!</span>escrow.is_cancelled, ChakraError::AlreadyCancelled);

Ok(())
}</code></pre><p><em>That's it. Three checks, and then Ok(()). No state gets written. No event gets emitted. No CPI happens inside this function body.</em></p><p><em>If you only looked at this function you would think, wait, where is the refund. Where does the money go back to the user. There has to be more code somewhere.</em></p><p><em>There isn't. And that is the whole point.</em></p><hr><p><strong><em>Part 2: Where The Real Work Happens</em></strong></p><p><em>The refund does not happen inside the function body at all. It happens in the accounts struct, before the function even runs.</em></p><pre data-type="codeBlock" text="#[derive(Accounts)]
pub struct CancelIntent&lt;'info&gt; {
#[account(mut)]
pub owner: Signer&lt;'info&gt;,

#[account(
    mut,
    seeds = [b&quot;escrow&quot;, owner.key().as_ref(), 
             &amp;escrow_account.target_chain_id.to_le_bytes(),
             &amp;escrow_account.nonce.to_le_bytes()],
    bump = escrow_account.bump,
    has_one = owner @ ChakraError::Unauthorized,
    close = owner
)]
pub escrow_account: Account&lt;'info, EscrowState&gt;,

pub system_program: Program&lt;'info, System&gt;,
}"><code><span class="hljs-meta">#[derive(Accounts)]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">CancelIntent</span>&lt;<span class="hljs-symbol">'info</span>&gt; {
<span class="hljs-meta">#[account(mut)]</span>
<span class="hljs-keyword">pub</span> owner: Signer&lt;<span class="hljs-symbol">'info</span>&gt;,

<span class="hljs-meta">#[account(
    mut,
    seeds = [b<span class="hljs-string">"escrow"</span>, owner.key().as_ref(), 
             &amp;escrow_account.target_chain_id.to_le_bytes(),
             &amp;escrow_account.nonce.to_le_bytes()]</span>,
    bump = escrow_account.bump,
    has_one = owner @ ChakraError::Unauthorized,
    close = owner
)]
<span class="hljs-keyword">pub</span> escrow_account: Account&lt;<span class="hljs-symbol">'info</span>, EscrowState&gt;,

<span class="hljs-keyword">pub</span> system_program: Program&lt;<span class="hljs-symbol">'info</span>, System&gt;,
}</code></pre><p><em>Two words in there are doing the entire job. has_one and close.</em></p><p><em>has_one = owner @ ChakraError::Unauthorized tells Anchor, before you even run this instruction, go check that escrow_account.owner equals the owner pubkey that signed this transaction. If they don't match, stop immediately with Unauthorized. You do not need to write an if statement for this. Anchor writes it for you, from this one line.</em></p><p><em>close = owner is the actual refund. This tells Anchor, after this instruction finishes successfully, close this account entirely. Take every single lamport sitting in it, including the amount the user originally locked plus the rent that was paid to create the account, and send all of it to owner. Then wipe the account data and mark it as closed so it can never be read or used again.</em></p><p><em>So by the time handle_cancel_intent even starts running, Anchor has already confirmed the right person is calling this, and has already queued up the full refund to happen the moment the function returns Ok. The function's only job is to decide whether that refund is allowed to go through at all.</em></p><hr><p><strong><em>Part 3: The Three Gatekeepers, One More Time</em></strong></p><p><em>clock.slot &gt; escrow.timeout_slot. This is the whole reason cancel exists. If the timeout has not passed yet, the Sentinel Network might still be working on this intent. You do not get to grab your funds back early just because you changed your mind. TimeoutNotReached stops you.</em></p><p><em>!</em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://escrow.is"><em>escrow.is</em></a><em>_finalized. If the Sentinel Network already finished and submit_proof already ran, this escrow is done. There is nothing left to cancel. AlreadyFinalized stops you.</em></p><p><em>!</em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://escrow.is"><em>escrow.is</em></a><em>_cancelled. And this one is honestly a bit funny to me now that I am looking at it carefully for this post. Let me be upfront about something.</em></p><hr><p><strong><em>Part 4: A Small Honest Thing I Noticed While Writing This</em></strong></p><p><em>Go back to Day 4 and Day 6 for a second. EscrowState has an is_cancelled boolean. </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://events.rs"><em>events.rs</em></a><em> has an IntentCancelled event with one field, escrow_pda.</em></p><p><em>Now look at this function again. Nowhere in here do we ever set </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://escrow.is"><em>escrow.is</em></a><em>_cancelled = true. Nowhere do we emit!(IntentCancelled). The check require!(!</em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://escrow.is"><em>escrow.is</em></a><em>_cancelled, ...) is checking a flag that this exact function never flips to true.</em></p><p><em>Why does it not matter functionally. Because of close = owner. The moment this function succeeds, the entire account gets deleted from existence. There is no escrow left for is_cancelled to be true on. The flag was designed as a belt and suspenders thing back when I was thinking about a version where cancel just marks the escrow instead of closing it, and the close = owner approach made that flag mostly redundant.</em></p><p><em>But the missing IntentCancelled event is a real gap. If someone is building a dashboard watching CHAKRA's logs to show "this many intents succeeded, this many got refunded", they have no clean event to catch for the refund side. They would have to watch for the account closing itself, which is a much messier thing to detect from outside.</em></p><p><em>I am noting this down as something to add. A one line emit!(IntentCancelled { escrow_pda: ... }) right before the Ok(()) would fix it completely. Small thing, but these small things are exactly why I am doing this series, writing it down in public means I cannot quietly forget about it.</em></p><hr><p><strong><em>Part 5: Why I Actually Love How Boring This File Is</em></strong></p><p><em>Think back to Day 1. Ronin. Wormhole. Nomad. Hundreds of millions of dollars stuck or stolen.</em></p><p><em>When those bridges had problems, what did recovery usually look like for a normal user. Wait for an announcement. Wait for a snapshot. Wait for a vote. Wait for a multisig of validators to agree on what happened and manually push a fix.</em></p><p><em>Here, on CHAKRA, if the Sentinel Network goes completely offline for whatever reason and never comes back, the path back to your money is this. Wait until your chosen timeout slot passes. Call one instruction. has_one checks you are the owner. close sends everything back. Done. No announcement needed. No vote needed. No one's permission needed.</em></p><p><em>The entire safety net for a user, the thing that means "worst case scenario, I still get my money back", fits in ten lines plus two words in an accounts struct. That is not me being lazy. That is the design goal. The less code there is standing between a user and their refund, the less there is that can ever go wrong on that path.</em></p><hr><p><strong><em>In document 9</em></strong></p><p><em>In part 9 we go to the file on the complete opposite end from this one. cancel_</em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://intent.rs"><em>intent.rs</em></a><em> is what happens when nothing went right. Tomorrow's file, submit_</em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://proof.rs"><em>proof.rs</em></a><em>, is what happens when everything went right, and it is where the actual cryptography, the secp256k1_recover syscall, the keccak256 hash, the whole reason this series exists, finally runs on chain for real.</em></p><p><em>See y'all for Day 9.</em></p><hr><p><strong>Here are some resourcess for u all</strong></p><div data-type="embedly" src="https://www.anchor-lang.com/docs/references/account-constraints" data="{&quot;provider_url&quot;:&quot;https://www.anchor-lang.com&quot;,&quot;description&quot;:&quot;Anchor Account Constraints Examples&quot;,&quot;title&quot;:&quot;Account Constraints&quot;,&quot;url&quot;:&quot;https://www.anchor-lang.com/docs/references/account-constraints&quot;,&quot;version&quot;:&quot;1.0&quot;,&quot;provider_name&quot;:&quot;Anchor-lang&quot;,&quot;type&quot;:&quot;link&quot;}" format="small"><div class="react-component embed my-5" data-drag-handle="true" data-node-view-wrapper="" style="white-space:normal"><a class="link-embed-link" href="https://www.anchor-lang.com/docs/references/account-constraints" target="_blank" rel="noreferrer"><div class="link-embed"><div class="flex-1"><div><h2>Account Constraints</h2><p>Anchor Account Constraints Examples</p></div><span><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-link h-3 w-3 my-auto inline mr-1"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>https://www.anchor-lang.com</span></div></div></a></div></div><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.reddit.com/r/solana/comments/w2ue3c/closing_and_reopening_an_ata_that_has_delegated/">https://www.reddit.com/r/solana/comments/w2ue3c/closing_and_reopening_an_ata_that_has_delegated/</a></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://solanacookbook.com/kr/references/accounts.html#how-to-create-a-system-account">https://solanacookbook.com/kr/references/accounts.html#how-to-create-a-system-account</a></p><div data-type="embedly" src="https://solana.com/docs/rpc/http/getsignaturesforaddress" data="{&quot;provider_url&quot;:&quot;https://solana.com&quot;,&quot;description&quot;:&quot;Returns confirmed transaction signatures for transactions that reference the supplied address in accountKeys, newest first.&quot;,&quot;title&quot;:&quot;getSignaturesForAddress&quot;,&quot;mean_alpha&quot;:82.421031746,&quot;thumbnail_width&quot;:1200,&quot;url&quot;:&quot;https://solana.com/docs/rpc/http/getsignaturesforaddress&quot;,&quot;thumbnail_url&quot;:&quot;https://storage.googleapis.com/papyrus_images/f5b2ef9b963d3124e5efc6c186c7d1608793ae868551c12527854aad33d7af6b.png&quot;,&quot;version&quot;:&quot;1.0&quot;,&quot;provider_name&quot;:&quot;Solana&quot;,&quot;type&quot;:&quot;link&quot;,&quot;thumbnail_height&quot;:630,&quot;image&quot;:{&quot;base64&quot;:&quot;data:image/png;base64,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&quot;,&quot;img&quot;:{&quot;width&quot;:1200,&quot;height&quot;:630,&quot;src&quot;:&quot;https://storage.googleapis.com/papyrus_images/f5b2ef9b963d3124e5efc6c186c7d1608793ae868551c12527854aad33d7af6b.png&quot;}}}" format="small"><link rel="preload" as="image" href="https://storage.googleapis.com/papyrus_images/f5b2ef9b963d3124e5efc6c186c7d1608793ae868551c12527854aad33d7af6b.png"><div class="react-component embed my-5" data-drag-handle="true" data-node-view-wrapper="" style="white-space:normal"><a class="link-embed-link" href="https://solana.com/docs/rpc/http/getsignaturesforaddress" target="_blank" rel="noreferrer"><div class="link-embed"><div class="flex-1"><div><h2>getSignaturesForAddress</h2><p>Returns confirmed transaction signatures for transactions that reference the supplied address in accountKeys, newest first.</p></div><span><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-link h-3 w-3 my-auto inline mr-1"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>https://solana.com</span></div><img src="https://storage.googleapis.com/papyrus_images/f5b2ef9b963d3124e5efc6c186c7d1608793ae868551c12527854aad33d7af6b.png" alt="getSignaturesForAddress"></div></a></div></div><hr><figure float="none" data-type="figure" class="img-center"><img src="https://storage.googleapis.com/papyrus_images/b34c4310ed4a8429f54429ccfb1ffb8f03bc3657fa9f8f332bb4e2ae58b0ab84.avif" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACkQBADs=" nextheight="234" nextwidth="222" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>written by maha thankyou</p>]]></content:encoded>
            <author>chakra-papers@newsletter.paragraph.com (Maha)</author>
            <category>@solana</category>
            <category>@web3</category>
            <category>@rust</category>
            <category>@anchor</category>
            <enclosure url="https://storage.googleapis.com/papyrus_images/1fed94ae458b90860e52f6aafe241654539fba10dc33be9f73f2cb587f6ef93a.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Pulling The Trigger: The Start of a Cross-Chain Intent]]></title>
            <link>https://paragraph.com/@chakra-papers/the-start-of-cross-chain-intent</link>
            <guid>xCTGxrrJp5mANxru0yWF</guid>
            <pubDate>Sun, 14 Jun 2026 07:13:36 GMT</pubDate>
            <description><![CDATA[The moment code becomes action we pull the trigger on initialize_intent.rs]]></description>
            <content:encoded><![CDATA[<h2 id="h-chakra-series-7" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><em>CHAKRA SERIES - 7</em></h2><p><strong><em>Pulling The Trigger</em></strong><em><br>Inside initialize_</em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://intent.rs"><em>intent.rs</em></a><em>: How CHAKRA Locks Funds And Starts The Clock</em></p><p><em>Welcome to Day 7.</em></p><p><em>Day 4 was </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://state.rs"><em>state.rs</em></a><em>, the containers. Day 5 was </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://errors.rs"><em>errors.rs</em></a><em>, the ten promises. Day 6 was </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://events.rs"><em>events.rs</em></a><em>, the voice that talks to the Sentinel Network without any middleman.</em></p><p><em>All three of those files have one thing in common. They cannot do anything by themselves. A struct cannot run. An error cannot fire on its own. An event cannot emit itself.</em></p><p><em>Something has to actually call them. Something has to be the first thing that runs when a real user does a real thing.</em></p><p><em>That something is</em><code> initialize_intent.rs</code></p><p><em>This is the file that starts everything. Literally everything you have read about so far in this series, the escrow, the timeout, the event the Sentinel Nodes are listening for, all of it begins the moment this single instruction is called.</em></p><hr><p><strong><em>Part 1: The Accounts Struct</em></strong></p><p><em>rust</em></p><pre data-type="codeBlock" text="#[derive(Accounts)]
#[instruction(target_chain_id: u64, nonce: u64)]
pub struct InitializeIntent&lt;'info&gt; {
#[account(mut)]
pub user: Signer&lt;'info&gt;,
#[account(
    init,
    payer = user,
    space = EscrowState::LEN,
    seeds = [b&quot;escrow&quot;, user.key().as_ref(), &amp;target_chain_id.to_le_bytes(), &amp;nonce.to_le_bytes()],
    bump
)]
pub escrow_account: Account&lt;'info, EscrowState&gt;,

pub system_program: Program&lt;'info, System&gt;,
}"><code><span class="hljs-meta">#[derive(Accounts)]</span>
<span class="hljs-meta">#[instruction(target_chain_id: u64, nonce: u64)]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">InitializeIntent</span>&lt;<span class="hljs-symbol">'info</span>&gt; {
<span class="hljs-meta">#[account(mut)]</span>
<span class="hljs-keyword">pub</span> user: Signer&lt;<span class="hljs-symbol">'info</span>&gt;,
<span class="hljs-meta">#[account(
    init,
    payer = user,
    space = EscrowState::LEN,
    seeds = [b<span class="hljs-string">"escrow"</span>, user.key().as_ref(), &amp;target_chain_id.to_le_bytes(), &amp;nonce.to_le_bytes()]</span>,
    bump
)]
<span class="hljs-keyword">pub</span> escrow_account: Account&lt;<span class="hljs-symbol">'info</span>, EscrowState&gt;,

<span class="hljs-keyword">pub</span> system_program: Program&lt;<span class="hljs-symbol">'info</span>, System&gt;,
}</code></pre><br><p><em>Three accounts. That is it.</em></p><p><em>user is the Signer. This is whoever is initiating the cross chain intent. They have to sign because they are about to lose lamports from their wallet, and Solana will never let anyone move your lamports without your signature.</em></p><p><em>escrow_account is the star of the show. Look at the seeds. b"escrow" plus the user's pubkey plus the target_chain_id plus the nonce, all combined. This is the exact same EscrowState struct we spent all of Day 4 designing byte by byte. space = EscrowState::LEN is literally that 211 byte calculation from Day 4 finally being put to use.</em></p><p><em>system_program is there because we are about to do a CPI, and I will explain what that means in Part 4.</em></p><p><em>One small detail before we move on. Notice target_chain_</em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://id.to"><em>id.to</em></a><em>_le_bytes() and </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://nonce.to"><em>nonce.to</em></a><em>_le_bytes(). LE means little endian. Keep that in your head. Hold onto it. When we get to submit_</em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://proof.rs"><em>proof.rs</em></a><em> later in this series you are going to see the exact same numbers encoded as BE, big endian, and the difference is not a mistake. It is on purpose. I will explain why when we get there.</em></p><hr><p><strong><em>Part 2: The Three Gatekeepers</em></strong></p><p><em>Before anything gets written, before any lamport moves, the function runs three checks.</em></p><p><em>rust</em></p><pre data-type="codeBlock" text="require!(amount &gt; 0, ChakraError::MathError);
require!(timeout_slots &gt;= 150, ChakraError::TimeoutTooShort);
require!(timeout_slots &lt;= 216000, ChakraError::TimeoutTooLong);"><code><span class="hljs-built_in">require</span><span class="hljs-operator">!</span>(amount <span class="hljs-operator">&gt;</span> <span class="hljs-number">0</span>, ChakraError::MathError);
<span class="hljs-built_in">require</span><span class="hljs-operator">!</span>(timeout_slots <span class="hljs-operator">&gt;</span><span class="hljs-operator">=</span> <span class="hljs-number">150</span>, ChakraError::TimeoutTooShort);
<span class="hljs-built_in">require</span><span class="hljs-operator">!</span>(timeout_slots <span class="hljs-operator">&lt;</span><span class="hljs-operator">=</span> <span class="hljs-number">216000</span>, ChakraError::TimeoutTooLong);</code></pre><p><em>You already met all three of these errors back on Day 5. amount &gt; 0 stops someone from creating a pointless empty intent. timeout_slots &gt;= 150 stops someone from setting a timeout so short the Sentinel Network physically cannot finish in time. timeout_slots &lt;= 216000 stops someone from locking an account open for years.</em></p><p><em>This is what I mean when I say </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://errors.rs"><em>errors.rs</em></a><em> is not a side file. It is the actual logic that runs inside the actual instruction. Day 5 was not theory. Day 5 was a preview of this exact moment.</em></p><p><em>If any of these three checks fail, the entire transaction reverts. The escrow account never even gets created. No partial state. Nothing half done. Solana either does the whole thing or none of it.</em></p><hr><p><strong><em>Part 3: Filling In The Blueprint</em></strong></p><p><em>Once the gatekeepers pass, we start writing into the escrow account that Anchor just created for us.</em></p><p><em>rust</em></p><pre data-type="codeBlock" text="escrow.owner = ctx.accounts.user.key();
escrow.target_chain_id = target_chain_id;
escrow.nonce = nonce;
escrow.amount = amount;
escrow.start_slot = clock.slot;
escrow.timeout_slot = clock.slot.checked_add(timeout_slots).ok_or(ChakraError::MathError)?;
escrow.is_finalized = false;
escrow.is_cancelled = false;
escrow.source_chain = source_chain;
escrow.target_chain = target_chain;
escrow.target_address = target_address;
escrow.bump = ctx.bumps.escrow_account;"><code>escrow.owner <span class="hljs-operator">=</span> ctx.accounts.user.key();
escrow.target_chain_id <span class="hljs-operator">=</span> target_chain_id;
escrow.nonce <span class="hljs-operator">=</span> nonce;
escrow.amount <span class="hljs-operator">=</span> amount;
escrow.start_slot <span class="hljs-operator">=</span> clock.slot;
escrow.timeout_slot <span class="hljs-operator">=</span> clock.slot.checked_add(timeout_slots).ok_or(ChakraError::MathError)?;
escrow.is_finalized <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>;
escrow.is_cancelled <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>;
escrow.source_chain <span class="hljs-operator">=</span> source_chain;
escrow.target_chain <span class="hljs-operator">=</span> target_chain;
escrow.target_address <span class="hljs-operator">=</span> target_address;
escrow.bump <span class="hljs-operator">=</span> ctx.bumps.escrow_account;</code></pre><p><em>Every single line here is one of the fields we designed in Day 4. owner, target_chain_id, nonce, amount, you have already read the reasoning for every one of these in the 60 Lines post. This is just the moment they actually get a value for the first time.</em></p><p><em>One line worth slowing down on. timeout_slot is not the timeout duration. It is current slot plus the duration. And look, it is wrapped in checked_add. If somehow adding the duration to the current slot would overflow a u64, instead of wrapping around to some tiny number in the past, the whole thing fails with MathError. This is the exact scenario I talked about on Day 5 when explaining why MathError exists. Today you are seeing the actual line of code where that protection lives.</em></p><hr><br><p><strong><em>Part 4: The Transfer, And What CPI Even Means</em></strong></p><p><em>rust</em></p><pre data-type="codeBlock" text="let cpi_context = CpiContext::new(
ctx.accounts.system_program.to_account_info(),
Transfer {
from: ctx.accounts.user.to_account_info(),
to: escrow_info,
},
);

transfer(cpi_context, amount)?;"><code>let cpi_context <span class="hljs-operator">=</span> CpiContext::<span class="hljs-keyword">new</span>(
ctx.accounts.system_program.to_account_info(),
Transfer {
<span class="hljs-keyword">from</span>: ctx.accounts.user.to_account_info(),
to: escrow_info,
},
);

transfer(cpi_context, amount)?;</code></pre><p><em>CPI stands for Cross Program Invocation. It basically means one program calling another program. Here, the CHAKRA program is calling the Solana System Program, which is the program responsible for moving lamports around.</em></p><p><em>Why can CHAKRA not just move the lamports itself directly. Because moving SOL is the System Program's job, and on Solana, programs do not bypass each other's responsibilities. CHAKRA says please, System Program, move this much from the user's account to the escrow account, and the System Program does it, because the user already signed this transaction.</em></p><p><em>The escrow account itself never signs anything here. It is on the receiving end. A PDA can receive lamports passively like this even though it has no private key, because receiving does not require a signature, only sending does.</em></p><p><em>After this line runs, the user's wallet balance just dropped by exactly amount, and the escrow PDA's balance went up by the same amount. The money is now sitting inside a Program Derived Address that only the CHAKRA program controls.</em></p><hr><p><strong><em>Part 5: The Signal</em></strong></p><p><em>rust</em></p><pre data-type="codeBlock" text="emit!(ControlIntent {
owner: ctx.accounts.user.key(),
target_chain_id,
nonce,
amount,
source_chain,
target_chain,
target_address,
escrow_pda: escrow_key,
timeout_slot: escrow.timeout_slot,
});"><code><span class="hljs-keyword">emit</span><span class="hljs-operator">!</span>(ControlIntent {
owner: ctx.accounts.user.key(),
target_chain_id,
nonce,
amount,
source_chain,
target_chain,
target_address,
escrow_pda: escrow_key,
timeout_slot: escrow.timeout_slot,
});</code></pre><p><em>This is the very last line. This is Day 6 coming full circle. The exact ControlIntent struct we walked through field by field yesterday gets emitted right here, at the very end of this function, only after everything else has already succeeded.</em></p><p><em>Order matters here. The checks ran first. The escrow got written second. The funds moved third. The signal goes out last. By the time the Sentinel Nodes hear this event, the escrow PDA on chain already has the funds sitting in it, already has all the correct fields, ready for the Sentinel to fetch and cross check.</em></p><hr><p><strong><em>Part 6: What This One Function Actually Proves</em></strong></p><p><em>Step back for a second. This single function, from top to bottom, is the entire user facing promise of CHAKRA in miniature.</em></p><p><em>You give it an amount and a destination. It checks your inputs are sane. It locks your funds in a place only the program controls. It writes down everything anyone will ever need to know about this intent. And then it shouts about it to the world, in a way that cannot be faked, because the shout only happens after the lock already happened on chain.</em></p><p><em>Nothing here asks you to trust me, Maha, or trust some company, or trust a multisig of strangers. It asks you to trust Solana's runtime, which enforces that this function either runs completely or not at all.</em></p><hr><p><strong><em>In part 8</em></strong></p><p><em>we look at the file on the opposite end of the spectrum. initialize_</em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://intent.rs"><em>intent.rs</em></a><em> is the longest instruction in the program. Next file, cancel_</em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://intent.rs"><em>intent.rs</em></a><em>, might be the shortest piece of meaningful code I have ever shipped. And I want to talk about why being short is actually the entire point of that file.</em></p><p><em>See y'all  for Part 8.</em></p><hr><p><em>Here are some resources if u want to dig deeper</em></p><div data-type="embedly" src="https://www.anchor-lang.com/docs/basics/pda" data="{&quot;provider_url&quot;:&quot;https://www.anchor-lang.com&quot;,&quot;description&quot;:&quot;Learn how to use Program Derived Addresses (PDAs) in Anchor programs to create deterministic account addresses.&quot;,&quot;title&quot;:&quot;Program Derived Address&quot;,&quot;url&quot;:&quot;https://www.anchor-lang.com/docs/basics/pda&quot;,&quot;version&quot;:&quot;1.0&quot;,&quot;provider_name&quot;:&quot;Anchor-lang&quot;,&quot;type&quot;:&quot;link&quot;}" format="small"><div class="react-component embed my-5" data-drag-handle="true" data-node-view-wrapper="" style="white-space:normal"><a class="link-embed-link" href="https://www.anchor-lang.com/docs/basics/pda" target="_blank" rel="noreferrer"><div class="link-embed"><div class="flex-1"><div><h2>Program Derived Address</h2><p>Learn how to use Program Derived Addresses (PDAs) in Anchor programs to create deterministic account addresses.</p></div><span><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-link h-3 w-3 my-auto inline mr-1"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>https://www.anchor-lang.com</span></div></div></a></div></div><div data-type="embedly" src="https://solana.com/docs/core/cpi" data="{&quot;provider_url&quot;:&quot;https://solana.com&quot;,&quot;description&quot;:&quot;Cross Program Invocation (CPI) on Solana - how programs call other programs using invoke and invoke_signed, handle PDA signers, and compose onchain functionality.&quot;,&quot;title&quot;:&quot;Cross Program Invocation&quot;,&quot;mean_alpha&quot;:82.4198412698,&quot;thumbnail_width&quot;:1200,&quot;url&quot;:&quot;https://solana.com/docs/core/cpi&quot;,&quot;thumbnail_url&quot;:&quot;https://storage.googleapis.com/papyrus_images/3875bd93d590912382b00973fa251d4942cbb154c7e4ac27a8900dfd58de44aa.png&quot;,&quot;version&quot;:&quot;1.0&quot;,&quot;provider_name&quot;:&quot;Solana&quot;,&quot;type&quot;:&quot;link&quot;,&quot;thumbnail_height&quot;:630,&quot;image&quot;:{&quot;base64&quot;:&quot;data:image/png;base64,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&quot;,&quot;img&quot;:{&quot;width&quot;:1200,&quot;height&quot;:630,&quot;src&quot;:&quot;https://storage.googleapis.com/papyrus_images/3875bd93d590912382b00973fa251d4942cbb154c7e4ac27a8900dfd58de44aa.png&quot;}}}" format="small"><link rel="preload" as="image" href="https://storage.googleapis.com/papyrus_images/3875bd93d590912382b00973fa251d4942cbb154c7e4ac27a8900dfd58de44aa.png"><div class="react-component embed my-5" data-drag-handle="true" data-node-view-wrapper="" style="white-space:normal"><a class="link-embed-link" href="https://solana.com/docs/core/cpi" target="_blank" rel="noreferrer"><div class="link-embed"><div class="flex-1"><div><h2>Cross Program Invocation</h2><p>Cross Program Invocation (CPI) on Solana - how programs call other programs using invoke and invoke_signed, handle PDA signers, and compose onchain functionality.</p></div><span><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-link h-3 w-3 my-auto inline mr-1"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>https://solana.com</span></div><img src="https://storage.googleapis.com/papyrus_images/3875bd93d590912382b00973fa251d4942cbb154c7e4ac27a8900dfd58de44aa.png" alt="Cross Program Invocation"></div></a></div></div><div data-type="embedly" src="https://doc.rust-lang.org/std/index.html?search=to_be_bytes" data="{&quot;provider_url&quot;:&quot;https://doc.rust-lang.org&quot;,&quot;description&quot;:&quot;The Rust Standard Library&quot;,&quot;title&quot;:&quot;std - Rust&quot;,&quot;url&quot;:&quot;https://doc.rust-lang.org/std/index.html?search=to_be_bytes&quot;,&quot;version&quot;:&quot;1.0&quot;,&quot;provider_name&quot;:&quot;Rust-lang&quot;,&quot;type&quot;:&quot;link&quot;}" format="small"><div class="react-component embed my-5" data-drag-handle="true" data-node-view-wrapper="" style="white-space:normal"><a class="link-embed-link" href="https://doc.rust-lang.org/std/index.html?search=to_be_bytes" target="_blank" rel="noreferrer"><div class="link-embed"><div class="flex-1"><div><h2>std - Rust</h2><p>The Rust Standard Library</p></div><span><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-link h-3 w-3 my-auto inline mr-1"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>https://doc.rust-lang.org</span></div></div></a></div></div><div data-type="embedly" src="https://docs.rs/anchor-lang/latest/anchor_lang/attr.account.html" data="{&quot;provider_url&quot;:&quot;https://docs.rs&quot;,&quot;description&quot;:&quot;An attribute for a data structure representing a Solana account.&quot;,&quot;title&quot;:&quot;account in anchor_lang - Rust&quot;,&quot;url&quot;:&quot;https://docs.rs/anchor-lang/latest/anchor_lang/attr.account.html&quot;,&quot;version&quot;:&quot;1.0&quot;,&quot;provider_name&quot;:&quot;Docs&quot;,&quot;type&quot;:&quot;link&quot;}" format="small"><div class="react-component embed my-5" data-drag-handle="true" data-node-view-wrapper="" style="white-space:normal"><a class="link-embed-link" href="https://docs.rs/anchor-lang/latest/anchor_lang/attr.account.html" target="_blank" rel="noreferrer"><div class="link-embed"><div class="flex-1"><div><h2>account in anchor_lang - Rust</h2><p>An attribute for a data structure representing a Solana account.</p></div><span><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-link h-3 w-3 my-auto inline mr-1"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>https://docs.rs</span></div></div></a></div></div><hr><figure float="none" data-type="figure" class="img-center"><img src="https://storage.googleapis.com/papyrus_images/b34c4310ed4a8429f54429ccfb1ffb8f03bc3657fa9f8f332bb4e2ae58b0ab84.avif" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACkQBADs=" nextheight="234" nextwidth="222" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p><em>Written by Maha.. thankyou </em></p><br>]]></content:encoded>
            <author>chakra-papers@newsletter.paragraph.com (Maha)</author>
            <category>@solana@anchor@rust</category>
            <enclosure url="https://storage.googleapis.com/papyrus_images/ef37f2a4c135a7ff4cf351ccbbda42da76e6b9a6fca47181735cd34f8b1594bc.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[The Off-Chain Wire]]></title>
            <link>https://paragraph.com/@chakra-papers/the-off-chain-wire</link>
            <guid>eqKLG5B2eec0Dp5MCsiR</guid>
            <pubDate>Thu, 11 Jun 2026 15:14:30 GMT</pubDate>
            <description><![CDATA[The off-chain wire how chakra connects its on-chain program to the Sentinel Network without a single trusted intermediary,]]></description>
            <content:encoded><![CDATA[<h2 id="h-chakra-series-6" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><em>CHAKRA SERIES - 6</em></h2><p><em>Welcome to Day 6.</em></p><p><em>Day 4 was </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://state.rs"><em>state.rs</em></a><em>. The data model. The containers. Day 5 was </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://errors.rs"><em>errors.rs</em></a><em>. The ten promises. The safety guarantees.</em></p><p><em>Today is </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://events.rs"><em>events.rs</em></a><em>. And this one is different from the last two.</em></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://state.rs"><em>state.rs</em></a><em> and </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://errors.rs"><em>errors.rs</em></a><em> are about what CHAKRA remembers and what CHAKRA refuses to do. They are internal. They define the shape and the limits of the protocol.</em></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://events.rs"><em>events.rs</em></a><em> is about how CHAKRA talks.</em></p><p><em>Specifically  - how the on-chain Solana program talks to the off-chain Sentinel Network without any trusted intermediary between them.</em></p><p><em>This is one of the most underrated design problems in all of cross-chain development. You have a Solana program running inside the Solana Virtual Machine. You have Rust processes running on three different servers. How do they communicate? Who tells the servers when to start working? How do the servers know what work to do?</em></p><p><em>The answer is in this file. Three structs. Thirty lines of code. Let me show you.</em></p><hr><p><strong><em>Part 1: What Is a Solana Event</em></strong></p><p><em>Before the code, the concept.</em></p><p><em>When an Anchor program emits an event, it writes structured data to the transaction logs of that transaction. Not to an account. Not to storage. To the logs.</em></p><p><em>Every Solana transaction produces logs. They are public. Anyone watching the network can read them. And crucially anyone can subscribe to them in real time via WebSocket.</em></p><p><em>So when CHAKRA's on-chain program emits a ControlIntent event, it is not sending a message to anyone specifically. It is publishing structured data to a public channel. The Sentinel Nodes are subscribed to that channel. They hear it. They act on it.</em></p><p><em>No intermediary. No message broker. No trusted relay. Just a public log and three processes listening to it.</em></p><p><em>This design means the communication channel itself cannot be corrupted. The Sentinel Nodes read directly from Solana. If they hear a ControlIntent event it is because the CHAKRA program actually emitted it, which means a user actually called initialize_intent, which means funds are actually locked in an escrow PDA. The Sentinel Nodes do not need to trust anyone who tells them to start working. They verify the source themselves.</em></p><hr><p><strong><em>Part 2: The File</em></strong></p><p><em>rust</em></p><pre data-type="codeBlock" text="use anchor_lang::prelude::*;

#[event]
pub struct ControlIntent {
    pub owner: Pubkey,
    pub target_chain_id: u64,
    pub nonce: u64,
    pub amount: u64,
    pub source_chain: [u8; 32],
    pub target_chain: [u8; 32],
    pub target_address: [u8; 64],
    pub escrow_pda: Pubkey,
    pub timeout_slot: u64,
}

#[event]
pub struct IntentFinalized {
    pub escrow_pda: Pubkey,
    pub tx_hash: [u8; 64],
}

#[event]
pub struct IntentCancelled {
    pub escrow_pda: Pubkey,
}"><code><span class="hljs-keyword">use</span> anchor_lang::prelude::*;

<span class="hljs-meta">#[event]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">ControlIntent</span> {
    <span class="hljs-keyword">pub</span> owner: Pubkey,
    <span class="hljs-keyword">pub</span> target_chain_id: <span class="hljs-type">u64</span>,
    <span class="hljs-keyword">pub</span> nonce: <span class="hljs-type">u64</span>,
    <span class="hljs-keyword">pub</span> amount: <span class="hljs-type">u64</span>,
    <span class="hljs-keyword">pub</span> source_chain: [<span class="hljs-type">u8</span>; <span class="hljs-number">32</span>],
    <span class="hljs-keyword">pub</span> target_chain: [<span class="hljs-type">u8</span>; <span class="hljs-number">32</span>],
    <span class="hljs-keyword">pub</span> target_address: [<span class="hljs-type">u8</span>; <span class="hljs-number">64</span>],
    <span class="hljs-keyword">pub</span> escrow_pda: Pubkey,
    <span class="hljs-keyword">pub</span> timeout_slot: <span class="hljs-type">u64</span>,
}

<span class="hljs-meta">#[event]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">IntentFinalized</span> {
    <span class="hljs-keyword">pub</span> escrow_pda: Pubkey,
    <span class="hljs-keyword">pub</span> tx_hash: [<span class="hljs-type">u8</span>; <span class="hljs-number">64</span>],
}

<span class="hljs-meta">#[event]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">IntentCancelled</span> {
    <span class="hljs-keyword">pub</span> escrow_pda: Pubkey,
}</code></pre><p><em>Three events. Each one marks a different moment in the lifecycle of a cross-chain intent.</em></p><hr><p><strong><em>Part 3: ControlIntent — The Starting Gun</em></strong></p><p><em>rust</em></p><pre data-type="codeBlock" text="#[event]
pub struct ControlIntent {
    pub owner: Pubkey,
    pub target_chain_id: u64,
    pub nonce: u64,
    pub amount: u64,
    pub source_chain: [u8; 32],
    pub target_chain: [u8; 32],
    pub target_address: [u8; 64],
    pub escrow_pda: Pubkey,
    pub timeout_slot: u64,
}"><code><span class="hljs-meta">#[event]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">ControlIntent</span> {
    <span class="hljs-keyword">pub</span> owner: Pubkey,
    <span class="hljs-keyword">pub</span> target_chain_id: <span class="hljs-type">u64</span>,
    <span class="hljs-keyword">pub</span> nonce: <span class="hljs-type">u64</span>,
    <span class="hljs-keyword">pub</span> amount: <span class="hljs-type">u64</span>,
    <span class="hljs-keyword">pub</span> source_chain: [<span class="hljs-type">u8</span>; <span class="hljs-number">32</span>],
    <span class="hljs-keyword">pub</span> target_chain: [<span class="hljs-type">u8</span>; <span class="hljs-number">32</span>],
    <span class="hljs-keyword">pub</span> target_address: [<span class="hljs-type">u8</span>; <span class="hljs-number">64</span>],
    <span class="hljs-keyword">pub</span> escrow_pda: Pubkey,
    <span class="hljs-keyword">pub</span> timeout_slot: <span class="hljs-type">u64</span>,
}</code></pre><p><em>This is the event that starts everything.</em></p><p><em>When a user calls initialize_intent and it succeeds funds locked, escrow created, all validations passed - the CHAKRA Controller emits this event to the transaction logs.</em></p><p><em>The Sentinel Nodes are watching. They parse it. They start the signing ceremony.</em></p><p><em>Let me walk through every field and explain why it is here.</em></p><p><strong><em>owner</em></strong><em> — the Pubkey of the user who created the intent. The Sentinel Nodes need this to verify that the escrow PDA they are about to work on actually belongs to the person they think it belongs to. It is also in the escrow account on-chain. The Sentinel cross-references both.</em></p><p><strong><em>target_chain_id</em></strong><em> — which blockchain to execute on. Base Sepolia is 84532. This number goes into the message payload that the Sentinel Nodes sign. The same number goes into the ChakraReceiver.sol call on the target chain. The ChakraReceiver verifies it. If the Sentinel tried to execute on a different chain than the one the user specified, the signature would not verify because the chain ID would be wrong.</em></p><p><strong><em>nonce</em></strong><em> — the uniqueness identifier for this specific intent. Combined with the owner's address and the chain ID it is part of the PDA seed. So when the Sentinel has the nonce, it can deterministically compute the escrow PDA address and fetch the account to cross-check all the other fields.</em></p><p><strong><em>amount</em></strong><em> — how many lamports are locked. This also goes into the signing payload. The ChakraReceiver contract verifies the signature over this amount. If a Sentinel tried to execute a different amount than what the user specified, the signature would fail verification on the target chain.</em></p><p><strong><em>source_chain and target_chain</em></strong><em> — both 32-byte arrays identifying the chains involved. source_chain is always "solana" in the current implementation. target_chain is "base" or "ethereum" etc. These go into the event so the Sentinel Nodes can route the execution correctly. If CHAKRA supports five target chains in the future, the Sentinel Network uses this field to know which chain's RPC to call.</em></p><p><strong><em>target_address</em></strong><em> — 64 bytes. The destination address on the target chain where the execution should happen or the funds should go. This is the most sensitive field in the event. If this were tampered with, the execution would go to the wrong address. The fact that it is signed as part of the TSS payload means any tampering would invalidate the signature and the ChakraReceiver would reject it.</em></p><p><strong><em>escrow_pda</em></strong><em> — the address of the escrow account holding the user's funds. The Sentinel Nodes use this to verify the event data matches what is actually on-chain before starting the signing ceremony. This is the cross-check. The event data could theoretically be crafted. But the escrow account on-chain cannot be faked. The Sentinel fetches the escrow by this PDA and verifies everything matches.</em></p><p><strong><em>timeout_slot</em></strong><em> — the absolute deadline. The Sentinel Network must complete execution and submit proof before this slot. The coordinator checks the current slot against this value before starting work. If it is already past the deadline — maybe the event was delayed, maybe the signing took too long — the coordinator aborts rather than wasting gas on an execution that can never be verified on time.</em></p><p><em>Every field in ControlIntent has a job. Nothing is there by accident.</em></p><hr><p><strong><em>Part 4: IntentFinalized — The Confirmation</em></strong></p><p><em>rust</em></p><pre data-type="codeBlock" text="#[event]
pub struct IntentFinalized {
    pub escrow_pda: Pubkey,
    pub tx_hash: [u8; 64],
}"><code><span class="hljs-meta">#[event]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">IntentFinalized</span> {
    <span class="hljs-keyword">pub</span> escrow_pda: Pubkey,
    <span class="hljs-keyword">pub</span> tx_hash: [<span class="hljs-type">u8</span>; <span class="hljs-number">64</span>],
}</code></pre><p><em>This event fires when submit_proof succeeds.</em></p><p><em>The TSS signature verified. The escrow closed. The lamports went to treasury.</em></p><p><em>Two fields.</em></p><p><strong><em>escrow_pda</em></strong><em> — which intent was finalized. Any indexer, any dashboard, any monitoring tool watching CHAKRA's program logs can use this to track intent states in real time.</em></p><p><strong><em>tx_hash</em></strong><em> — the transaction hash of the execution on the target chain. This is the receipt. When a user wants to verify that their intent was actually executed on Base Sepolia, this is the hash they take to the Base Sepolia block explorer. It is stored in the event so it is permanently on-chain as part of the Solana transaction record.</em></p><p><em>This matters for auditability. In a world where "trust me it happened on the other chain" is not acceptable, IntentFinalized puts the cross-chain receipt permanently on Solana. Every intent execution is publicly verifiable from the Solana side.</em></p><hr><p><strong><em>Part 5: IntentCancelled — The Safe Exit</em></strong></p><p><em>rust</em></p><pre data-type="codeBlock" text="#[event]
pub struct IntentCancelled {
    pub escrow_pda: Pubkey,
}"><code><span class="hljs-meta">#[event]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">IntentCancelled</span> {
    <span class="hljs-keyword">pub</span> escrow_pda: Pubkey,
}</code></pre><p><em>The simplest event. One field.</em></p><p><em>This fires when cancel_intent succeeds. The timeout passed. The Sentinel Network did not complete in time. The user called cancel. The funds returned.</em></p><p><em>One field because there is nothing else to record. The escrow PDA tells you which intent was cancelled. There is no target chain transaction hash because no target chain execution happened. There is no amount because the amount went back to the user and that is a Solana-side operation recorded in the cancel transaction itself.</em></p><p><em>The simplicity of IntentCancelled is actually a design signal. If you see mostly IntentFinalized events in the logs, the protocol is working well. If you start seeing a lot of IntentCancelled events, something is wrong with the Sentinel Network. The events tell the story of protocol health in real time.</em></p><hr><p><strong><em>Part 6: How the Sentinel Actually Reads These</em></strong></p><p><em>This is the part most people do not think about.</em></p><p><em>Anchor events are emitted as base64-encoded binary data in the transaction logs. They look like garbage to a human reading the raw logs. The Sentinel Nodes have to:</em></p><ol><li><p><em>Subscribe to Solana transaction logs for the CHAKRA program via WebSocket</em></p></li><li><p><em>Filter for logs that contain "Program data:" — that is where Anchor encodes events</em></p></li><li><p><em>Base64-decode the data</em></p></li><li><p><em>Check the first 8 bytes he Anchor event discriminator</em></p></li><li><p><em>Verify it matches the discriminator for ControlIntent specifically</em></p></li><li><p><em>Deserialize the remaining bytes into the ControlIntent struct</em></p></li></ol><p><em>The discriminator is computed as the first 8 bytes of SHA256("event:ControlIntent"). This is how the Sentinel knows it is looking at a ControlIntent and not some other event or log line.</em></p><p><em>If the discriminator does not match, the Sentinel ignores it and moves on. If it does match, the Sentinel trusts the data not because the event claimed to be valid, but because the event came from the CHAKRA program's address and the Solana validator already verified that the program executed correctly.</em></p><p><em>This is the security model of Solana events. You do not trust the data in isolation. You trust it because of where it came from and the fact that it is permanently embedded in a finalized transaction.</em></p><hr><hr><h3 id="h-part-7-the-core-foundations-are-set" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><em>Part 7: The Core Foundations Are Set</em></h3><p><em>With </em><code>state.rs</code><em>, </em><code>errors.rs</code><em>, and now </em><code>events.rs</code><em> completely laid out, you have seen the entire structural anatomy of CHAKRA:</em></p><ol><li><p><strong><em>The Container:</em></strong><em> How we pack every byte tightly on-chain to minimize user rent.</em></p></li><li><p><strong><em>The Guardrails:</em></strong><em> The ten strict promises that make our protocol refuse to collapse.</em></p></li><li><p><strong><em>The Voice:</em></strong><em> How we broadcast cryptographically verifiable signals to the off-chain world without middlemen.</em></p></li></ol><p><em>We are officially done with the setup files. The architectural blueprint is complete.</em></p><p><em>In the upcoming days, things turn even more interesting. We are shifting from structure to action opening up the heavy-hitting instruction engines, starting with the file that triggers the entire machine: </em><code>initialize_intent.rs</code><em>. Every read from here on out gets closer to the live mainframe.</em></p><p><em>and see y'all for Day 7.</em></p><hr><p><em>Thankyou for reading. And here are some resources you can refer to</em></p><div data-type="embedly" src="https://solana.com/developers/guides/advanced/confirmation" data="{&quot;provider_url&quot;:&quot;https://solana.com&quot;,&quot;description&quot;:&quot;Understand how Solana transaction confirmation and when a transaction expires (including recent blockhash checks).&quot;,&quot;title&quot;:&quot;Transaction Confirmation &amp; Expiration&quot;,&quot;mean_alpha&quot;:82.421031746,&quot;thumbnail_width&quot;:1200,&quot;url&quot;:&quot;https://solana.com/developers/guides/advanced/confirmation&quot;,&quot;thumbnail_url&quot;:&quot;https://storage.googleapis.com/papyrus_images/2e7300e63afb3d87f0c75673e8b53a75dcd024be0231df43559cb663d6748c58.png&quot;,&quot;version&quot;:&quot;1.0&quot;,&quot;provider_name&quot;:&quot;Solana&quot;,&quot;type&quot;:&quot;link&quot;,&quot;thumbnail_height&quot;:630,&quot;image&quot;:{&quot;base64&quot;:&quot;data:image/png;base64,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&quot;,&quot;img&quot;:{&quot;width&quot;:1200,&quot;height&quot;:630,&quot;src&quot;:&quot;https://storage.googleapis.com/papyrus_images/2e7300e63afb3d87f0c75673e8b53a75dcd024be0231df43559cb663d6748c58.png&quot;}}}" format="small"><link rel="preload" as="image" href="https://storage.googleapis.com/papyrus_images/2e7300e63afb3d87f0c75673e8b53a75dcd024be0231df43559cb663d6748c58.png"><div class="react-component embed my-5" data-drag-handle="true" data-node-view-wrapper="" style="white-space:normal"><a class="link-embed-link" href="https://solana.com/developers/guides/advanced/confirmation" target="_blank" rel="noreferrer"><div class="link-embed"><div class="flex-1"><div><h2>Transaction Confirmation &amp; Expiration</h2><p>Understand how Solana transaction confirmation and when a transaction expires (including recent blockhash checks).</p></div><span><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-link h-3 w-3 my-auto inline mr-1"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>https://solana.com</span></div><img src="https://storage.googleapis.com/papyrus_images/2e7300e63afb3d87f0c75673e8b53a75dcd024be0231df43559cb663d6748c58.png" alt="Transaction Confirmation &amp; Expiration"></div></a></div></div><div data-type="embedly" src="https://docs.rs/anchor-lang/latest/anchor_lang/" data="{&quot;provider_url&quot;:&quot;https://docs.rs&quot;,&quot;description&quot;:&quot;Anchor ⚓ is a framework for Solana's Sealevel runtime providing several convenient developer tools.&quot;,&quot;title&quot;:&quot;anchor_lang - Rust&quot;,&quot;url&quot;:&quot;https://docs.rs/anchor-lang/latest/anchor_lang/&quot;,&quot;version&quot;:&quot;1.0&quot;,&quot;provider_name&quot;:&quot;Docs&quot;,&quot;type&quot;:&quot;link&quot;}" format="small"><div class="react-component embed my-5" data-drag-handle="true" data-node-view-wrapper="" style="white-space:normal"><a class="link-embed-link" href="https://docs.rs/anchor-lang/latest/anchor_lang/" target="_blank" rel="noreferrer"><div class="link-embed"><div class="flex-1"><div><h2>anchor_lang - Rust</h2><p>Anchor ⚓ is a framework for Solana's Sealevel runtime providing several convenient developer tools.</p></div><span><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-link h-3 w-3 my-auto inline mr-1"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>https://docs.rs</span></div></div></a></div></div><div data-type="embedly" src="https://solana.com/docs/rpc/websocket/logssubscribe" data="{&quot;provider_url&quot;:&quot;https://solana.com&quot;,&quot;description&quot;:&quot;Subscribe to transaction log messages that match a log filter.&quot;,&quot;title&quot;:&quot;logsSubscribe&quot;,&quot;mean_alpha&quot;:82.421031746,&quot;thumbnail_width&quot;:1200,&quot;url&quot;:&quot;https://solana.com/docs/rpc/websocket/logssubscribe&quot;,&quot;thumbnail_url&quot;:&quot;https://storage.googleapis.com/papyrus_images/2e91cc25824280a68f22fc901f6290cb7e1c1f169fccb6858d3f761b0918c65d.png&quot;,&quot;version&quot;:&quot;1.0&quot;,&quot;provider_name&quot;:&quot;Solana&quot;,&quot;type&quot;:&quot;link&quot;,&quot;thumbnail_height&quot;:630,&quot;image&quot;:{&quot;base64&quot;:&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAARCAIAAAAzPjmrAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFhUlEQVR4nIWUa0xTBxiGz6IIau89PZf29PR6yqUthbaUUnqnQBEoFrBir1BopYAgQy4yuchFkRmVBXXKwkCCbG4MdMzN/VDCdMvQLcuWmWXOJVui2Uzm7j+WzZylsOzyQ5e8P75/T973/b4PoIE8KptLZaBUNpfC5tJBHh3ixwXy6WycCYlZkIQDEwichiIZPEQjQPUirkWK2G28/YFb92yPfgxHPw1PftRNkuhsNwIYM0VeBeZOw1wp3CIpmg/Q2BgFwZIkOBXFaGwsiYEk0uEEOkxhYixIuJXOpzNELBbBZqVAoAqD9ELIIkStajCs5dT7Fz+wk2TTPOn84TvNryvQmQFegj1HFNPwg2rcr+J75FgZsInOkqSUysYWWVaD1eTQGixWu1OrN2bpzTm5jlyTMzPTbNA7HfZKc25FocXvdsaK88JB02gWLeyqm2h5RPofkL1XSc0v79FHdmdZn7FzO42SFp0wqhXVqHAPkARCGEenU7dD0nRpipJIy0hVZfHEyUKJQpKsTk7VKeRGlcKmVTu16aV6ZUVuujdPU+vW9fjpc0Zp+9NT9zpIsvVnMkySJvK+4utTHMJlAzsKRL16Tt1O/jhAYXIRgXQzmwkAm4EEOrCBAjy1VZdrE0kUAMDm4+k0imTLBjErSQHRdAJ2vklZk4EGZRRfMToWYC7s1MzFOm62vP2w7SEZIUkjeQeebtuOn2mM3Kp+44sR/U/ABgrHYt/W0z8cie2Jxpqjsea2rgNnJiaHho+WlftHjoy1tvZ2d440Nxxs3j04Ovji1PjSka7Zk0NLe6tOF/JHA9hiJfPcTvl0466bDSv3ikky8/slnXtf9NIdz8d3XZ2XgEQ6nK41HOgb6ukfnpl9+djYeN/g4VhTa0/f8KHDx7q7h3t7RsefmxrqHe/tGNtXf/TQ/umx/vlLM6tD9fMO3kiZfDwiW45uXq5JXW589m4J+bvoypS7fabp8gOltdZ27S2ABvK2MNGNVE4SA7EXlmLiFCCBzoAFiRQulYGzWBKQncKmKrhgjgTJJ6DSDK4vG4vp4VaHYKCUOOGRz7jBSRBQqG2R2olPqle+DC/e3rNw31l3Nt1cU7CyEl9TWnz3eTSQByQykhgIA8ZpbD4DFDJBMQeSIbCSj+hEqEmKFigwt0YQMIga8iSdTmKwRHbcr3q1jHp2E8DNNe2NXrjTep9sv032LZAlxGmlLITITHEHf2v9xNZmPh3EmaCIwyFgSI7G7ytXijrSMFcm7tUL6y2itgLiYHHqiXL1VIPshhIIWI37D7z026FVcuB58miA3LfxMxjIZCcS/wH8S3zGGoANSWEoDYUzcUQv5tpTeEUqvicLDxvFLVbimW1poy7NhN+4VKO4Fsh+syvybav3q2DRu+15nw8mfsMG5BK84HEAHh3EGaCIBUkgOBmBVRgcT0mG5suxskzcmy2KGmVtBYqBkuyT5dZZX+mVmorV6srV6qpbAd/7warrY5o/zECvXT/wBMA/NcCQfN2EBLXJuE4F5laL/Aai0arqKtSPuvImd2xf9HmXg+GbociHwdpVT+iab9dyyHS9ovDi/wJEaymlInB6/NMhBjF3jYGXa4mAXtFkye5xOk6UVcx6gpe90RVv7EZV/Ts7IlddoaWi0HyB77XHAtZ7ZvxlgoDhNARW8RGtADHEffAL5ZJydVp1jq7Fau9zbh8vDZ53Ry9Wxi67G664oq+XhC86qxfyq+efAFivQcAE1z42RMBQnMGDNTiiF3LNhKAwVVaeoQ7qjc3m4l6H57gzNLGtdrqo7nxR3Vx+7Zyj5oI9/MqTAGsmBOsm1hkQJ54VCqv5iE6AGaXi/JTUMmWWP8saMxS1G8v7LVVHzL7jlsApS+gFS805U3juT4U1vWbaWfveAAAAAElFTkSuQmCC&quot;,&quot;img&quot;:{&quot;width&quot;:1200,&quot;height&quot;:630,&quot;src&quot;:&quot;https://storage.googleapis.com/papyrus_images/2e91cc25824280a68f22fc901f6290cb7e1c1f169fccb6858d3f761b0918c65d.png&quot;}}}" format="small"><link rel="preload" as="image" href="https://storage.googleapis.com/papyrus_images/2e91cc25824280a68f22fc901f6290cb7e1c1f169fccb6858d3f761b0918c65d.png"><div class="react-component embed my-5" data-drag-handle="true" data-node-view-wrapper="" style="white-space:normal"><a class="link-embed-link" href="https://solana.com/docs/rpc/websocket/logssubscribe" target="_blank" rel="noreferrer"><div class="link-embed"><div class="flex-1"><div><h2>logsSubscribe</h2><p>Subscribe to transaction log messages that match a log filter.</p></div><span><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-link h-3 w-3 my-auto inline mr-1"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>https://solana.com</span></div><img src="https://storage.googleapis.com/papyrus_images/2e91cc25824280a68f22fc901f6290cb7e1c1f169fccb6858d3f761b0918c65d.png" alt="logsSubscribe"></div></a></div></div><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://solanacookbook.com/references/programs.html#how-to-transfer-sol-in-a-program"><em>https://solanacookbook.com/references/programs.html#how-to-transfer-sol-in-a-program</em></a></p><hr><figure float="none" data-type="figure" class="img-center"><img src="https://storage.googleapis.com/papyrus_images/b34c4310ed4a8429f54429ccfb1ffb8f03bc3657fa9f8f332bb4e2ae58b0ab84.avif" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACkQBADs=" nextheight="234" nextwidth="222" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><br><p><em>Written by Maha..</em></p>]]></content:encoded>
            <author>chakra-papers@newsletter.paragraph.com (Maha)</author>
            <category>@solana</category>
            <category>@web3</category>
            <category>@anchor</category>
            <category>@rust</category>
            <enclosure url="https://storage.googleapis.com/papyrus_images/9a14fb85dc5d08b88451cf22af9a55e9e0a11f268cbdb2cea59cd5b3cbfd6045.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[The Real Errors of Chakra]]></title>
            <link>https://paragraph.com/@chakra-papers/the-real-errors-of-chakra</link>
            <guid>dBE0Ice2GudB018RZYx3</guid>
            <pubDate>Thu, 11 Jun 2026 14:51:45 GMT</pubDate>
            <description><![CDATA[An engineering system is defined by how it fails and how it resolves, handles errors]]></description>
            <content:encoded><![CDATA[<div data-type="x402Embed"></div><h2 id="h-chakra-series-5" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">CHAKRA SERIES - 5</h2><p>Welcome to Day 5.</p><p><em>Yesterday we looked at </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://state.rs"><em>state.rs</em></a><em>. The four structs. The data model. The containers that hold every piece of information CHAKRA needs to remember about a cross-chain intent.</em></p><p><em>If </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://state.rs"><em>state.rs</em></a><em> is the blueprint of the building, today's file is the list of ways the building is not allowed to collapse.</em></p><p><em>It is called </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://errors.rs"><em>errors.rs</em></a><em>. It is ten lines of code. And I want to spend today showing you why those ten lines tell the entire security story of CHAKRA more clearly than any whitepaper could.</em></p><hr><p><strong><em>Part 1: Why Error Codes Are Not Just Error Messages</em></strong></p><p><em>When most people think about errors in code they think about something going wrong. A crash. A bug. Something you did not plan for.</em></p><p><em>In a Solana program errors are the opposite of that.</em></p><p><em>In a Solana program errors are things you planned for very carefully. They are the explicit list of every situation where the program decides to reject an operation and return funds or stop execution rather than continue.</em></p><p><em>In a normal application when something goes wrong the worst case is maybe a bad user experience. Some wrong data. A UI that breaks.</em></p><p><em>In a cross-chain protocol when something goes wrong the worst case is someone losing real money. Permanently. With no way to recover it.</em></p><p><em>So errors are not afterthoughts. Errors are the contract CHAKRA makes with every user. Here is every situation where we will stop. Here is every situation where we will protect you. Here is every edge case we have thought about and decided how to handle.</em></p><p><em>Ten error codes. Ten promises.</em></p><hr><p><strong><em>Part 2: The File</em></strong></p><p><em>rust</em></p><pre data-type="codeBlock" text="use anchor_lang::prelude::*;

#[error_code]
pub enum ChakraError {
    #[msg(&quot;The requested timeout is too short for cross-chain finality.&quot;)]
    TimeoutTooShort,
    #[msg(&quot;The requested timeout is unreasonably long.&quot;)]
    TimeoutTooLong,
    #[msg(&quot;The cross-chain intent has not timed out yet.&quot;)]
    TimeoutNotReached,
    #[msg(&quot;The cross-chain intent has timed out and can no longer be finalized.&quot;)]
    TimeoutReached,
    #[msg(&quot;This intent has already been finalized by a ZK-Proof.&quot;)]
    AlreadyFinalized,
    #[msg(&quot;This intent has already been cancelled.&quot;)]
    AlreadyCancelled,
    #[msg(&quot;User is not authorized to manage this intent.&quot;)]
    Unauthorized,
    #[msg(&quot;The signer is not an authorized Sentinel node.&quot;)]
    UnauthorizedSentinel,
    #[msg(&quot;Mathematical overflow or underflow occurred.&quot;)]
    MathError,
    #[msg(&quot;The provided cryptographic proof is invalid or improperly signed.&quot;)]
    InvalidProof,
}"><code><span class="hljs-keyword">use</span> anchor_lang::prelude::*;

<span class="hljs-meta">#[error_code]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-keyword">enum</span> <span class="hljs-title class_">ChakraError</span> {
    <span class="hljs-meta">#[msg(<span class="hljs-string">"The requested timeout is too short for cross-chain finality."</span>)]</span>
    TimeoutTooShort,
    <span class="hljs-meta">#[msg(<span class="hljs-string">"The requested timeout is unreasonably long."</span>)]</span>
    TimeoutTooLong,
    <span class="hljs-meta">#[msg(<span class="hljs-string">"The cross-chain intent has not timed out yet."</span>)]</span>
    TimeoutNotReached,
    <span class="hljs-meta">#[msg(<span class="hljs-string">"The cross-chain intent has timed out and can no longer be finalized."</span>)]</span>
    TimeoutReached,
    <span class="hljs-meta">#[msg(<span class="hljs-string">"This intent has already been finalized by a ZK-Proof."</span>)]</span>
    AlreadyFinalized,
    <span class="hljs-meta">#[msg(<span class="hljs-string">"This intent has already been cancelled."</span>)]</span>
    AlreadyCancelled,
    <span class="hljs-meta">#[msg(<span class="hljs-string">"User is not authorized to manage this intent."</span>)]</span>
    Unauthorized,
    <span class="hljs-meta">#[msg(<span class="hljs-string">"The signer is not an authorized Sentinel node."</span>)]</span>
    UnauthorizedSentinel,
    <span class="hljs-meta">#[msg(<span class="hljs-string">"Mathematical overflow or underflow occurred."</span>)]</span>
    MathError,
    <span class="hljs-meta">#[msg(<span class="hljs-string">"The provided cryptographic proof is invalid or improperly signed."</span>)]</span>
    InvalidProof,
}</code></pre><p><em>That is the entire file.</em></p><p><em>Now let me walk through every single error and tell you exactly what situation it is protecting against.</em></p><hr><p><strong><em>Part 3: The Timeout Errors</em></strong></p><p><em>There are four errors that deal with time. This is not a coincidence. Time is the hardest thing to get right in a cross-chain system and I got it wrong twice before I got it right.</em></p><p><strong><em>TimeoutTooShort</em></strong></p><p><em>rust</em></p><pre data-type="codeBlock" text="#[msg(&quot;The requested timeout is too short for cross-chain finality.&quot;)]
TimeoutTooShort,"><code><span class="hljs-comment">#[msg("The requested timeout is too short for cross-chain finality.")]</span>
TimeoutTooShort,</code></pre><p><em>When you call initialize_intent you pass a timeout_slots parameter. This is how long the Sentinel Network has to complete execution before you can cancel and get a refund.</em></p><p><em>The minimum is 150 slots.</em></p><p><em>Why 150? Because Solana produces a slot approximately every 400 milliseconds. 150 slots is about 60 seconds. That is the absolute minimum time the Sentinel Network needs to hear the ControlIntent event, coordinate the threshold signing ceremony across three nodes, send the transaction to the target chain, wait for confirmation, and submit the proof back to Solana.</em></p><p><em>If you try to set a timeout shorter than that, TimeoutTooShort fires. Immediately. Before your funds are locked. Before anything happens.</em></p><p><em>This error protects you from accidentally setting a timeout so short that the Sentinel Network cannot physically complete the execution before you can cancel it. Without this check a malicious user could set a 1-slot timeout, immediately call cancel, and try to race the system into a broken state.</em></p><p><strong><em>TimeoutTooLong</em></strong></p><p><em>rust</em></p><pre data-type="codeBlock" text="#[msg(&quot;The requested timeout is unreasonably long.&quot;)]
TimeoutTooLong,"><code><span class="hljs-comment">#[msg("The requested timeout is unreasonably long.")]</span>
TimeoutTooLong,</code></pre><p><em>The maximum is 216000 slots. That is about 24 hours.</em></p><p><em>This might seem unnecessary. Why would you care if a timeout is too long?</em></p><p><em>Because if there is no upper limit, someone could create an intent with a 10-year timeout. Their funds would be locked in the escrow account for 10 years. The protocol would have to maintain that account forever. The rent would need to stay funded. And if the Sentinel Network ever changes — new keys, new nodes, upgraded protocol — that ancient intent with a 10-year timeout would be stuck in limbo with no clean way to handle it.</em></p><p><em>24 hours is long enough for any reasonable cross-chain execution. If it has not happened in 24 hours something has seriously gone wrong and you should get your money back.</em></p><p><strong><em>TimeoutNotReached</em></strong></p><p><em>rust</em></p><pre data-type="codeBlock" text="#[msg(&quot;The cross-chain intent has not timed out yet.&quot;)]
TimeoutNotReached,"><code><span class="hljs-comment">#[msg("The cross-chain intent has not timed out yet.")]</span>
TimeoutNotReached,</code></pre><p><em>This one fires when you try to call cancel_intent too early.</em></p><p><em>The Sentinel Network is still working. The execution might still succeed. The timeout has not passed yet. You do not get to cancel yet.</em></p><p><em>This protects the protocol's integrity. Imagine if you could cancel at any time regardless of timeout. You could initiate an intent, the Sentinel Network starts working, and then you immediately cancel and get your refund. The Sentinel Network continues executing on the target chain anyway — spending gas, completing transactions — and now the proof it tries to submit will fail because you already cancelled. The target chain loses fees for nothing.</em></p><p><em>TimeoutNotReached prevents that. The Sentinel Network gets the full timeout window to complete its work.</em></p><p><strong><em>TimeoutReached</em></strong></p><p><em>rust</em></p><pre data-type="codeBlock" text="#[msg(&quot;The cross-chain intent has timed out and can no longer be finalized.&quot;)]
TimeoutReached,"><code><span class="hljs-comment">#[msg("The cross-chain intent has timed out and can no longer be finalized.")]</span>
TimeoutReached,</code></pre><p><em>This is the mirror. If the Sentinel Network tries to submit a proof after the timeout has already passed, TimeoutReached fires.</em></p><p><em>The window is closed. The user has already cancelled or can cancel immediately. The protocol will not accept late proofs. A late proof could lead to a situation where the user already got their refund and then the target chain execution also happened — meaning double payment.</em></p><p><em>TimeoutReached is the hard deadline. It is enforced by the slot clock. No exceptions.</em></p><hr><p><strong><em>Part 4: The State Errors</em></strong></p><p><strong><em>AlreadyFinalized</em></strong></p><p><em>rust</em></p><pre data-type="codeBlock" text="#[msg(&quot;This intent has already been finalized by a ZK-Proof.&quot;)]
AlreadyFinalized,"><code><span class="hljs-comment">#[msg("This intent has already been finalized by a ZK-Proof.")]</span>
AlreadyFinalized,</code></pre><p><em>Once an intent is finalized it is done. The escrow is closed. The funds went to treasury. There is nothing left to do.</em></p><p><em>If anything tries to interact with a finalized intent — a second proof submission, a cancel attempt, anything — AlreadyFinalized fires.</em></p><p><em>Notice the error message says "ZK-Proof" even though the current implementation uses TSS. The message is forward-looking. The verification mechanism will upgrade. The principle stays the same. Once it is finalized, it is immutable.</em></p><p><strong><em>AlreadyCancelled</em></strong></p><p><em>rust</em></p><pre data-type="codeBlock" text="#[msg(&quot;This intent has already been cancelled.&quot;)]
AlreadyCancelled,"><code><span class="hljs-comment">#[msg("This intent has already been cancelled.")]</span>
AlreadyCancelled,</code></pre><p><em>Same logic in the other direction. Once cancelled the escrow is closed. The funds went back to the user. Nothing can change that.</em></p><p><em>AlreadyFinalized and AlreadyCancelled together create what engineers call mutual exclusion. An intent can reach exactly one terminal state. Not both. Not neither. Exactly one. The two booleans in EscrowState enforce this. These two errors enforce it at every interaction point.</em></p><p><em>This is what prevents double-spend and double-refund. Not by accident. By explicit design.</em></p><hr><p><strong><em>Part 5: The Authorization Errors</em></strong></p><p><strong><em>Unauthorized</em></strong></p><p><em>rust</em></p><pre data-type="codeBlock" text="#[msg(&quot;User is not authorized to manage this intent.&quot;)]
Unauthorized,"><code><span class="hljs-comment">#[msg("User is not authorized to manage this intent.")]</span>
Unauthorized,</code></pre><p><em>This fires when someone tries to cancel an intent they did not create.</em></p><p><em>The EscrowState stores the owner field. The cancel_intent instruction checks that the signer matches the owner. If they do not match, Unauthorized fires and nothing happens.</em></p><p><em>You cannot cancel someone else's escrow. You cannot steal someone else's refund. The program enforces ownership at the instruction level.</em></p><p><strong><em>UnauthorizedSentinel</em></strong></p><p><em>rust</em></p><pre data-type="codeBlock" text="#[msg(&quot;The signer is not an authorized Sentinel node.&quot;)]
UnauthorizedSentinel,"><code><span class="hljs-comment">#[msg("The signer is not an authorized Sentinel node.")]</span>
UnauthorizedSentinel,</code></pre><p><em>This fires when something tries to call submit_proof but is not a registered Sentinel Node.</em></p><p><em>Remember from yesterday — SentinelAccount exists in </em><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://state.rs"><em>state.rs</em></a><em>. The admin registers Sentinel Nodes explicitly. If you are not in that registry, you cannot submit proofs. Period.</em></p><p><em>This prevents anyone from submitting fake proofs. Even if someone figured out how to forge a valid TSS signature — which is cryptographically infeasible — they would still fail here because they are not in the authorized Sentinel registry.</em></p><p><em>Defense in depth. The cryptography is one layer. The authorization registry is a second layer.</em></p><hr><p><strong><em>Part 6: The Safety Errors</em></strong></p><p><strong><em>MathError</em></strong></p><p><em>rust</em></p><pre data-type="codeBlock" text="#[msg(&quot;Mathematical overflow or underflow occurred.&quot;)]
MathError,"><code>#<span class="hljs-selector-attr">[msg(<span class="hljs-string">"Mathematical overflow or underflow occurred."</span>)]</span>
MathError,</code></pre><p><em>On Solana, mathematical overflow in a program causes a panic by default in debug mode. In release mode the behavior depends on your settings.</em></p><p><em>In CHAKRA we use checked arithmetic everywhere that matters. Instead of </em><code>a + b</code><em> we use </em><code>a.checked_add(b).ok_or(ChakraError::MathError)?</code><em>. If the addition would overflow a u64, instead of wrapping around to a tiny number or crashing, we return MathError and stop.</em></p><p><em>Why does this matter for a cross-chain protocol?</em></p><p><em>Because amounts and slot numbers are u64 values. If someone crafted an intent where adding timeout_slots to the current slot would overflow, the resulting timeout_slot would be a tiny number in the past. The intent would immediately be cancellable. The Sentinel Network would never get a chance to execute.</em></p><p><em>MathError catches that. The intent never gets created.</em></p><p><strong><em>InvalidProof</em></strong></p><p><em>rust</em></p><pre data-type="codeBlock" text="#[msg(&quot;The provided cryptographic proof is invalid or improperly signed.&quot;)]
InvalidProof,"><code><span class="hljs-comment">#[msg("The provided cryptographic proof is invalid or improperly signed.")]</span>
InvalidProof,</code></pre><p><em>This is the last line of defense.</em></p><p><em>When a Sentinel Node calls submit_proof, the program reconstructs the message hash, calls secp256k1_recover with the provided signature components, and compares the recovered public key against the registered TSS public key.</em></p><p><em>If they do not match — if the signature is wrong, if the message was constructed incorrectly, if any byte is off — InvalidProof fires. The escrow stays locked. The funds stay safe. Nothing changes.</em></p><p><em>InvalidProof is what makes the cryptographic guarantee real. It is the moment where math meets money. Either the proof is valid and the escrow releases, or it is not and everything stays exactly as it was.</em></p><hr><p><strong><em>Part 7: What I Actually Got Wrong</em></strong></p><p><em>I want to be honest about something.</em></p><p><em>The first version of this file had seven errors. I was missing TimeoutTooLong, MathError, and UnauthorizedSentinel.</em></p><p><em>TimeoutTooLong I missed because I was thinking about the minimum but not the maximum. One pointed out the long-lock attack during a code review I did informally.</em></p><p><em>MathError I missed because in my test environment the numbers were always small. It was only when I started thinking about what a malicious actor could pass as inputs that I realized I needed checked arithmetic everywhere.</em></p><p><em>UnauthorizedSentinel I missed because early on there was only one Sentinel Node running — mine. No need for an authorization registry when there is only one node. As soon as I moved to three nodes the need became obvious.</em></p><p><em>Three errors added because I thought more carefully about attacks.</em></p><p><em>That is how you build security. Not by getting it right the first time. By thinking about every person who might want to break the thing you built and making sure you have thought about them too.</em></p><hr><p><em>The Contract Is Signed Every single one of these errors acts as a hard boundary. They make sure the program logic stays entirely inside its intended lane. Without them, a state-changing transaction isn't just code executing it's a massive vulnerability waiting to be triggered by an edge-case attack vector. By defining exactly how and why our system will refuse to act, we give users absolute predictability. They know their funds can never settle into an unpredictable, stuck state. It is either total cross-chain finality or an immediate, clean refund. Now that we have mapped out the blueprint in </em><code>state.rs</code><em> and defined our safety boundaries in </em><code>errors.rs</code><em>, we finally have everything we need to build the actual engine.</em></p><hr><p><em>Some resources for you to refer</em></p><div data-type="embedly" src="https://docs.rs/anchor-lang/latest/anchor_lang/attr.error_code.html" data="{&quot;provider_url&quot;:&quot;https://docs.rs&quot;,&quot;description&quot;:&quot;Generates `Error` and `type Result = Result ` types to be used as return types from Anchor instruction handlers. Importantly, the attribute implements `From` on the `ErrorCode` to support converting from the user defined error enum into the generated `Error`.&quot;,&quot;title&quot;:&quot;error_code in anchor_lang - Rust&quot;,&quot;url&quot;:&quot;https://docs.rs/anchor-lang/latest/anchor_lang/attr.error_code.html&quot;,&quot;version&quot;:&quot;1.0&quot;,&quot;provider_name&quot;:&quot;Docs&quot;,&quot;type&quot;:&quot;link&quot;}" format="small"><div class="react-component embed my-5" data-drag-handle="true" data-node-view-wrapper="" style="white-space:normal"><a class="link-embed-link" href="https://docs.rs/anchor-lang/latest/anchor_lang/attr.error_code.html" target="_blank" rel="noreferrer"><div class="link-embed"><div class="flex-1"><div><h2>error_code in anchor_lang - Rust</h2><p>Generates `Error` and `type Result = Result ` types to be used as return types from Anchor instruction handlers. Importantly, the attribute implements `From` on the `ErrorCode` to support converting from the user defined error enum into the generated `Error`.</p></div><span><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-link h-3 w-3 my-auto inline mr-1"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>https://docs.rs</span></div></div></a></div></div><div data-type="embedly" src="https://www.anchor-lang.com/docs/features/errors" data="{&quot;provider_url&quot;:&quot;https://www.anchor-lang.com&quot;,&quot;description&quot;:&quot;Learn how to implement custom error handling in Anchor programs.&quot;,&quot;title&quot;:&quot;Custom Errors&quot;,&quot;url&quot;:&quot;https://www.anchor-lang.com/docs/features/errors&quot;,&quot;version&quot;:&quot;1.0&quot;,&quot;provider_name&quot;:&quot;Anchor-lang&quot;,&quot;type&quot;:&quot;link&quot;}" format="small"><div class="react-component embed my-5" data-drag-handle="true" data-node-view-wrapper="" style="white-space:normal"><a class="link-embed-link" href="https://www.anchor-lang.com/docs/features/errors" target="_blank" rel="noreferrer"><div class="link-embed"><div class="flex-1"><div><h2>Custom Errors</h2><p>Learn how to implement custom error handling in Anchor programs.</p></div><span><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-link h-3 w-3 my-auto inline mr-1"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>https://www.anchor-lang.com</span></div></div></a></div></div><div data-type="embedly" src="https://doc.rust-lang.org/std/primitive.u64.html" data="{&quot;provider_url&quot;:&quot;https://doc.rust-lang.org&quot;,&quot;description&quot;:&quot;The 64-bit unsigned integer type.&quot;,&quot;title&quot;:&quot;u64 - Rust&quot;,&quot;url&quot;:&quot;https://doc.rust-lang.org/std/primitive.u64.html&quot;,&quot;version&quot;:&quot;1.0&quot;,&quot;provider_name&quot;:&quot;Rust-lang&quot;,&quot;type&quot;:&quot;link&quot;}" format="small"><div class="react-component embed my-5" data-drag-handle="true" data-node-view-wrapper="" style="white-space:normal"><a class="link-embed-link" href="https://doc.rust-lang.org/std/primitive.u64.html" target="_blank" rel="noreferrer"><div class="link-embed"><div class="flex-1"><div><h2>u64 - Rust</h2><p>The 64-bit unsigned integer type.</p></div><span><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-link h-3 w-3 my-auto inline mr-1"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>https://doc.rust-lang.org</span></div></div></a></div></div><figure float="none" data-type="figure" class="img-center"><img src="https://storage.googleapis.com/papyrus_images/b34c4310ed4a8429f54429ccfb1ffb8f03bc3657fa9f8f332bb4e2ae58b0ab84.avif" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACkQBADs=" nextheight="234" nextwidth="222" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p><em>Written by Maha thankyou for reading</em></p><br>]]></content:encoded>
            <author>chakra-papers@newsletter.paragraph.com (Maha)</author>
            <category>@solana@web3@anchor@rust</category>
            <enclosure url="https://storage.googleapis.com/papyrus_images/73ab94b0a681b0750e2b0abf8989526c1ead79e482e623e4148e2d128f9ab082.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[The 60 Lines That Define Everything]]></title>
            <link>https://paragraph.com/@chakra-papers/the-60-lines-that-define-everything</link>
            <guid>ZtY1qmPdWIwy78LAar8G</guid>
            <pubDate>Tue, 09 Jun 2026 15:44:56 GMT</pubDate>
            <description><![CDATA[Most protocols break because of bad data models we dive into state.rs showing how optimized Rust layout the zero-waste foundation for the entire cross-chain loop]]></description>
            <content:encoded><![CDATA[<h2 id="h-chakra-series-4" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">CHAKRA SERIES - 4</h2><p>Welcome to Day 4. Today, we leave the high-level concept papers behind and look at the actual code defining CHAKRA. Before we talk about threshold signatures or complex execution logic, we have to talk about the bedrock: how we model our data on-chain.</p><p>We are looking directly at the codebase, starting with the foundation that everything else rests on  - The Rust state.</p><p>Firstly let me tell you something about how protocols actually work.</p><p>Most people think the hardest part of building a cross-chain protocol is the cryptography. The threshold signatures. The distributed key generation. The Lagrange interpolation.</p><p>And yes that stuff is hard. I spent months on it.</p><p>But you know what breaks protocols more often than bad cryptography?</p><p>Bad data models.</p><p>Decisions made in the first week of writing code that you can never take back. Fields that are in the wrong place. Types that are too small. Booleans that should have been enums. Accounts that were never designed to be closed cleanly.</p><p>These decisions live forever in a deployed program. On Solana they live in accounts. And accounts cost rent. And rent means real money. So every byte you waste is real money wasted by real users.</p><p>Today I want to show you the file where I made all those decisions for CHAKRA.</p><p>It is called <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://state.rs">state.rs</a>. It is 60 lines of Rust. And it is the foundation that every single other piece of CHAKRA is built on.</p><hr><p><strong>Part 1: What Is State in an Anchor Program</strong></p><p>If you have never written Solana programs before this concept is important to understand.</p><p>On Ethereum when your smart contract saves data it goes into contract storage. The contract owns the storage. You cannot really separate them.</p><p>On Solana it works completely differently.</p><p>Programs do not store data. Programs are stateless. They are just logic. Pure logic.</p><p>Data lives in accounts. Separate accounts. Owned by the program but separate from it.</p><p>So when CHAKRA needs to remember something — who initiated an intent, how much they locked, what the timeout is, whether it has been finalized — all of that lives in an account. A specific account structure defined in <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://state.rs">state.rs</a>.</p><p>Think of <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://state.rs">state.rs</a> as the blueprint. The struct definitions. The shapes of the containers. The program logic uses these containers. The accounts on-chain hold the data. <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://state.rs">state.rs</a> is what connects the two.</p><hr><p><strong>Part 2: The Four Structs</strong></p><p>CHAKRA has four account types defined in <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://state.rs">state.rs</a>.</p><p>I am going to walk through each one slowly.</p><p><strong>TssConfig</strong></p><p>rust</p><pre data-type="codeBlock" text="#[account]
pub struct TssConfig {
    pub tss_pubkey: [u8; 64],
    pub threshold: u8,
    pub total_nodes: u8,
    pub admin: Pubkey,
    pub bump: u8,
}"><code><span class="hljs-meta">#[account]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">TssConfig</span> {
    <span class="hljs-keyword">pub</span> tss_pubkey: [<span class="hljs-type">u8</span>; <span class="hljs-number">64</span>],
    <span class="hljs-keyword">pub</span> threshold: <span class="hljs-type">u8</span>,
    <span class="hljs-keyword">pub</span> total_nodes: <span class="hljs-type">u8</span>,
    <span class="hljs-keyword">pub</span> admin: Pubkey,
    <span class="hljs-keyword">pub</span> bump: <span class="hljs-type">u8</span>,
}</code></pre><p>This is the most important account in the entire protocol.</p><p>tss_pubkey is a 64-byte array. Not 65 bytes. Not 33 bytes. 64 bytes exactly.</p><p>Why 64?</p><p>A secp256k1 public key in uncompressed form is 65 bytes. But the first byte is always 0x04 — it is a prefix that just says "this is an uncompressed key." It carries no information. So we strip it and store only the 64 bytes of actual key material.</p><p>This 64-byte value is what every TSS proof gets verified against. When the Sentinel Network signs an intent and submits the proof, Solana runs secp256k1_recover on the signature and recovers a public key. That recovered key gets compared byte by byte against this field. If they match the proof is valid. If they do not match the proof is rejected.</p><p>Everything depends on this field being correct.</p><p>threshold and total_nodes are u8 — unsigned 8-bit integers. Maximum value 255. We only have 3 nodes right now so u8 is more than enough. These fields exist so the program knows the signing rules. Currently 2-of-3.</p><p>admin is the Pubkey that is allowed to update the TssConfig. This is important for key rotation. If the Sentinel Network needs to refresh its keys in Milestone 2, the admin can call update_tss_config to register the new public key. Nobody else can.</p><p>bump is the canonical bump seed for the PDA derivation. I will explain why we store this in Part 3.</p><p>Space calculation: 8 (Anchor discriminator) + 64 + 1 + 1 + 32 + 1 = 107 bytes.</p><hr><p><strong>EscrowState</strong></p><p>rust</p><pre data-type="codeBlock" text="#[account]
pub struct EscrowState {
    pub owner: Pubkey,
    pub target_chain_id: u64,
    pub nonce: u64,
    pub amount: u64,
    pub start_slot: u64,
    pub timeout_slot: u64,
    pub is_finalized: bool,
    pub is_cancelled: bool,
    pub bump: u8,
    pub source_chain: [u8; 32],
    pub target_chain: [u8; 32],
    pub target_address: [u8; 64],
}"><code><span class="hljs-meta">#[account]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">EscrowState</span> {
    <span class="hljs-keyword">pub</span> owner: Pubkey,
    <span class="hljs-keyword">pub</span> target_chain_id: <span class="hljs-type">u64</span>,
    <span class="hljs-keyword">pub</span> nonce: <span class="hljs-type">u64</span>,
    <span class="hljs-keyword">pub</span> amount: <span class="hljs-type">u64</span>,
    <span class="hljs-keyword">pub</span> start_slot: <span class="hljs-type">u64</span>,
    <span class="hljs-keyword">pub</span> timeout_slot: <span class="hljs-type">u64</span>,
    <span class="hljs-keyword">pub</span> is_finalized: <span class="hljs-type">bool</span>,
    <span class="hljs-keyword">pub</span> is_cancelled: <span class="hljs-type">bool</span>,
    <span class="hljs-keyword">pub</span> bump: <span class="hljs-type">u8</span>,
    <span class="hljs-keyword">pub</span> source_chain: [<span class="hljs-type">u8</span>; <span class="hljs-number">32</span>],
    <span class="hljs-keyword">pub</span> target_chain: [<span class="hljs-type">u8</span>; <span class="hljs-number">32</span>],
    <span class="hljs-keyword">pub</span> target_address: [<span class="hljs-type">u8</span>; <span class="hljs-number">64</span>],
}</code></pre><p>This is the escrow account. One of these gets created every time a user initiates a cross-chain intent. It holds all the information about that specific intent.</p><p>Let me go field by field because every single one matters.</p><p>owner — the Pubkey of the user who created this intent. This is the person who gets refunded if the intent times out and cancel is called. The program enforces this. You cannot call cancel on someone else's escrow.</p><p>target_chain_id — a u64 representing the EIP-155 chain ID of the target blockchain. Base Sepolia is 84532. Ethereum mainnet is 1. Polygon is 137. This is a standard. We use it so the Sentinel Nodes and the ChakraReceiver contract can both independently verify they are working on the same chain.</p><p>nonce — a u64 that makes each intent unique. If you send 100 intents to Base you need 100 different nonces. The PDA seed includes the nonce so each intent gets its own account address. Without nonces all your intents would try to write to the same account and fail.</p><p>amount — how many lamports the user locked. u64 because lamport values can be large and u64 goes up to about 18 quintillion. More than enough.</p><p>start_slot — the Solana slot when the intent was created. We record this for analytics and for potential dispute resolution in future versions.</p><p>timeout_slot — the absolute slot number after which cancel_intent becomes callable. Not a duration. An absolute slot. This is important. When initialize_intent runs it takes the current slot and adds timeout_slots to get this value. After that the user can always recover their funds. The Sentinel Network has until this slot to complete execution and submit proof. After this slot the protocol gives up and refunds.</p><p>is_finalized — boolean. True when a valid TSS proof has been submitted and verified. The escrow account gets closed when this happens.</p><p>is_cancelled — boolean. True when the intent timed out and was cancelled. The escrow account gets closed when this happens too.</p><p>These two booleans are your safety net. Before any state-changing operation the program checks both. If is_finalized is true you cannot cancel. If is_cancelled is true you cannot finalize. You cannot accidentally double-spend. You cannot accidentally double-refund.</p><p>bump — PDA bump. Same reason as TssConfig.</p><p>source_chain — 32 bytes. Usually contains a string like "solana" zero-padded to 32 bytes. This tells the Sentinel Network where the intent originated.</p><p>target_chain — 32 bytes. Usually contains "base" or "ethereum" zero-padded.</p><p>target_address — 64 bytes. This is the destination address on the target chain. 64 bytes because we want to support any chain. An Ethereum address is 20 bytes. A Bitcoin address is much shorter. A Solana address is 32 bytes. By making this field 64 bytes we can support all of them with room to spare. The unused bytes are zero-padded.</p><p>Space calculation: 8 + 32 + 8 + 8 + 8 + 8 + 8 + 1 + 1 + 1 + 32 + 32 + 64 = 211 bytes.</p><hr><p><strong>GlobalConfig</strong></p><p>rust</p><pre data-type="codeBlock" text="#[account]
pub struct GlobalConfig {
    pub admin: Pubkey,
    pub treasury: Pubkey,
    pub is_initialized: bool,
    pub bump: u8,
}"><code><span class="hljs-meta">#[account]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">GlobalConfig</span> {
    <span class="hljs-keyword">pub</span> admin: Pubkey,
    <span class="hljs-keyword">pub</span> treasury: Pubkey,
    <span class="hljs-keyword">pub</span> is_initialized: <span class="hljs-type">bool</span>,
    <span class="hljs-keyword">pub</span> bump: <span class="hljs-type">u8</span>,
}</code></pre><p>The protocol-wide configuration. Created once by the admin when the program is first set up.</p><p>admin — who controls the protocol. Can add and remove Sentinel Nodes. Can update the TssConfig. Full protocol authority.</p><p>treasury — where the lamports go when an escrow is finalized. When submit_proof runs and verifies successfully, the escrow account closes and the lamports go here. This is how the protocol captures fees in future versions.</p><p>is_initialized — a simple guard against calling initialize_config twice. Once it is true, you cannot reinitialize. This prevents a class of attack where someone tries to reset protocol state after it is already set up.</p><p>bump — PDA bump.</p><p>Space calculation: 8 + 32 + 32 + 1 + 1 = 74 bytes.</p><hr><p><strong>SentinelAccount</strong></p><p>rust</p><pre data-type="codeBlock" text="#[account]
pub struct SentinelAccount {
    pub sentinel_pubkey: Pubkey,
    pub is_active: bool,
    pub bump: u8,
}"><code><span class="hljs-meta">#[account]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">SentinelAccount</span> {
    <span class="hljs-keyword">pub</span> sentinel_pubkey: Pubkey,
    <span class="hljs-keyword">pub</span> is_active: <span class="hljs-type">bool</span>,
    <span class="hljs-keyword">pub</span> bump: <span class="hljs-type">u8</span>,
}</code></pre><p>The authorization record for a Sentinel Node.</p><p>For a Sentinel Node to call submit_proof it needs to be in this registry. The admin creates a SentinelAccount for each node when it is authorized. The admin can deactivate a node by setting is_active to false without deleting the account.</p><p>sentinel_pubkey — the Solana keypair address of the Sentinel Node process. When a Sentinel Node signs and submits a transaction, this is the key it signs with.</p><p>is_active — the on/off switch. If a Sentinel Node is compromised, the admin sets this to false immediately. Any further proof submissions from that node fail the authorization check. The attack is contained.</p><p>bump — PDA bump.</p><p>Space calculation: 8 + 32 + 1 + 1 = 42 bytes.</p><hr><p><strong>Part 3: Why We Store the Bump</strong></p><p>Every single struct has a bump field. If you are new to Solana this might seem weird. Let me explain.</p><p>Program Derived Addresses on Solana are calculated from seeds and a bump value. The bump is a number from 0 to 255 that is decremented until the derived address is not on the secp256k1 curve — meaning it has no corresponding private key and only the program can sign for it.</p><p>The canonical bump is the highest valid bump. Anchor finds this automatically when you use the init constraint.</p><p>Why store it? Because every time you want to verify or use a PDA in a subsequent instruction you need to recalculate it from the seeds and bump. If you store the bump you save the computation of finding it again. More importantly you make the account addresses deterministic — given the same seeds and stored bump, you always get the same address. No ambiguity.</p><p>In production Solana programs storing the bump is standard practice. It also saves compute units which costs less in fees.</p><hr><p><strong>Part 4: What This File Does Not Contain</strong></p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://state.rs">state.rs</a> has no logic. Zero.</p><p>No function calls. No if statements. No calculations. Nothing.</p><p>Just structure definitions.</p><p>This is intentional. In Anchor the state and the logic are completely separate. The instructions in the instructions/ folder handle all the logic. <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://state.rs">state.rs</a> is purely the data model.</p><p>This separation makes the code much easier to audit. If you want to understand what can go wrong with an EscrowState account, you read the instructions that use it. The state definition itself cannot have bugs because it cannot do anything.</p><hr><p><strong>Part 5: The Cost of a Byte</strong></p><p>When I was designing these 4 structs, I spent hours counting bytes ofcourse with alot of errors. On Solana, data storage isn't free. Every byte you store requires "rent exemption," which means locking up real SOL. </p><p>If you design a sloppy data model that wastes 100 bytes per transaction, and your protocol processes 10,000 transactions a day, you are burning your users' money for nothing. That’s why <code>TssConfig</code> is exactly 107 bytes, and <code>EscrowState</code> is a tight 211 bytes. We store only what is mathematically required to guarantee safety.</p><p>This file is the bedrock. It doesn't execute anything, but it defines the boundaries of what CHAKRA can and cannot do. If the container is broken, the logic inside it won't matter.</p><p>I know looking at raw structs can feel dry if you're waiting for the action, but you can't build a mainframe without laying down the foundation first. Now that you know the exact shape of our state and why every single field exists... we are ready to make it move.</p><p>Tomorrow for Day 5, we are opening up the next file. We’ll look at the actual Rust logic that takes these structs, locks the funds, and spins the gears into motion. </p><hr><p>Here are some resources </p><div data-type="embedly" src="https://solana.com/docs/core/accounts" data="{&quot;provider_url&quot;:&quot;https://solana.com&quot;,&quot;description&quot;:&quot;Solana accounts are the fundamental data unit for storing state on the network. Account structure, addresses, types, rent, ownership, and modification rules.&quot;,&quot;title&quot;:&quot;Accounts&quot;,&quot;mean_alpha&quot;:82.421031746,&quot;thumbnail_width&quot;:1200,&quot;url&quot;:&quot;https://solana.com/docs/core/accounts&quot;,&quot;thumbnail_url&quot;:&quot;https://storage.googleapis.com/papyrus_images/8725827dc499ef9e4e5770d663e080d87129d10bccef36928de241ba3fceff69.png&quot;,&quot;version&quot;:&quot;1.0&quot;,&quot;provider_name&quot;:&quot;Solana&quot;,&quot;type&quot;:&quot;link&quot;,&quot;thumbnail_height&quot;:630,&quot;image&quot;:{&quot;base64&quot;:&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAARCAIAAAAzPjmrAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFcklEQVR4nIXUa0xTZxgH8GOEwVzv5/Rc2nNOe9qeUtpi6QUopeWUCrQIFApYsdArtFBAEbCAl6Hg5gU3ZQm6KYvxEmXRKaLOzX1QwnTL0C3LlpllziVbotlM5u4fls2cpeC3RZc8X95Pv/z/T94H4EFSLijhCjAuKOGAEj4k5cNEeiCCD5JCWCGClWKERhEthuZLUbMMs1ISRoW6yqSbg7fvlz3+NZb4Mnb0sy0si53aggJ2IxXQ4z4t7tVIqlRYBcADcQ6KZytJLobzQDxbgGbxkUw+whHiIlj+Ap/gCyiRiAZFGhgy4LBVDjNyzGmCYhZxZ+uFT1ws23OO9fzyk/nPefjwmDTTVUwlzUTIRLYaCL8OrwOyBUJFTq1mYlbktDkd5RYb43B5TAUlliJHcUl5id1tNDI2q6fc1VRa0uhmWuvcndWrYiH73gJezNs+1fuYbX3IjlxjzX98xN/TUeDc6pIM2ZW9hfKEhYoaSD+QBSIy1GrU9YKqPJUmT6XNzzUU0FqzSmNSqo0rDXatplify1hMq836amteI2MOlZnaai3b1nGn7apU/7H7gyzb9zsbY1kH+0D//SEx7S2DBiupEau4fS0xCfBhIksAK3LUmRwQyOADGbz0LOMAywXLMmAAgFBEB3I1wmy9HGFUiEeL+oySkAWP+6jJoHBmrXk6OXir9/1HA4/YOMva2bvI8YF68nB3/HbknW/2WH8DMrmwUpN/9vylSFtnKJKIxbtCkY4N/UOhSEc4mkxtGtk3fnB0+/5IMCWDSnVEnUkWsMoTDnm/UzFYS+8PSmaahCfW6o53r7vVNX+/mmWNP18u9G1KXLzr//yed+gisJwjLmEqO3r6hl8cW9831J/a3LVhYGTHrpHR3S/v3t/evjGZGNyz6+Bw/zgJleqJejMRLJYnGSrlVo5Wq171aibj6rnE83PR3Lnuffdq2L+pq8d8qZM9Vx7mOdvKrr8H8CApB5QAGbzlK8QcEMsWoCuEWCYHyeJKuAKSx6VEohwENErBIlpaqcN9ZlnQJu8qUw55VDtr6Qm/7qQPOgoBelNZvG3qi8j8t7ELd9bPPPC0H1lZGq2cn08DPEgqQEg+THBAydKTD5MCSC6EFDCiRpE8HLFQmEOFLQJEyCbvcioG3aqXatQHWg1v13GPPAdIShwbE2fu9j1gU3fY7TNsDf16njqMqh1PgP8MwYdIIUSJxTQC67D0/ypRYeVa3GskA1Z5J0MNVNKj1bkTDaZjXeqbeUDQad+87a2/di2wY2+wrwTZTRlfIYARzKKfCggWARBWIbAWQ4wkalVIXBpplYHwF5Axu6LXSW9drR33mqda7Zej+uvBoneH4z/2Bb4LVX2YWvX1zqwfQECnJCufBkj5ECmAKBGshJEcFDHgSCGFOdRYhQ6vM5KBIiphVw9U6sdqig42OE+11F6NNi5EmhYizbeDLR+Hmm+8Zv6nFBhxWceeARBLaxDD6nRLiyGUWJla4tHjPhPVaqO7nYZht3Xcu+romvoLLYG5UOxWOP5pqG3BH77esm4u7LjR6J79X4BabCkXRVamLx1qU0gWDbLBQget+h6m6EVP+URd4yl/6EogMR9I3mzu/GBN/Jo3fLkqfK6y5fxTgaU9C56EoBFEiyIGArXIUFs6B+HWKRtM2khxYa/Ttd1TP1kbOu1LzDYlr/i6rnoTl2pis57ITEXk3DOApTXIhNDixYZpBE4bUsRMola5pJSWuXPVDfmmkNW+obR6pNx/wBOeWt12vKr9dFX7dEXbdHn0jCt29lnAYgjZUoglAxanu8IQE4EWynC7SlGhya3LK2gtcCZtVSl7ww6meW9pywEmeIgJv8lETzhi0/8Ca2auFyYiUWgAAAAASUVORK5CYII=&quot;,&quot;img&quot;:{&quot;width&quot;:1200,&quot;height&quot;:630,&quot;src&quot;:&quot;https://storage.googleapis.com/papyrus_images/8725827dc499ef9e4e5770d663e080d87129d10bccef36928de241ba3fceff69.png&quot;}}}" format="small"><link rel="preload" as="image" href="https://storage.googleapis.com/papyrus_images/8725827dc499ef9e4e5770d663e080d87129d10bccef36928de241ba3fceff69.png"><div class="react-component embed my-5" data-drag-handle="true" data-node-view-wrapper="" style="white-space:normal"><a class="link-embed-link" href="https://solana.com/docs/core/accounts" target="_blank" rel="noreferrer"><div class="link-embed"><div class="flex-1"><div><h2>Accounts</h2><p>Solana accounts are the fundamental data unit for storing state on the network. Account structure, addresses, types, rent, ownership, and modification rules.</p></div><span><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-link h-3 w-3 my-auto inline mr-1"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>https://solana.com</span></div><img src="https://storage.googleapis.com/papyrus_images/8725827dc499ef9e4e5770d663e080d87129d10bccef36928de241ba3fceff69.png" alt="Accounts"></div></a></div></div><div data-type="embedly" src="https://docs.rs/anchor-lang/latest/anchor_lang/attr.account.html" data="{&quot;provider_url&quot;:&quot;https://docs.rs&quot;,&quot;description&quot;:&quot;An attribute for a data structure representing a Solana account.&quot;,&quot;title&quot;:&quot;account in anchor_lang - Rust&quot;,&quot;url&quot;:&quot;https://docs.rs/anchor-lang/latest/anchor_lang/attr.account.html&quot;,&quot;version&quot;:&quot;1.0&quot;,&quot;provider_name&quot;:&quot;Docs&quot;,&quot;type&quot;:&quot;link&quot;}" format="small"><div class="react-component embed my-5" data-drag-handle="true" data-node-view-wrapper="" style="white-space:normal"><a class="link-embed-link" href="https://docs.rs/anchor-lang/latest/anchor_lang/attr.account.html" target="_blank" rel="noreferrer"><div class="link-embed"><div class="flex-1"><div><h2>account in anchor_lang - Rust</h2><p>An attribute for a data structure representing a Solana account.</p></div><span><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-link h-3 w-3 my-auto inline mr-1"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>https://docs.rs</span></div></div></a></div></div><div data-type="embedly" src="https://docs.rs/anchor-lang/latest/anchor_lang/derive.Accounts.html" data="{&quot;provider_url&quot;:&quot;https://docs.rs&quot;,&quot;description&quot;:&quot;Implements an `Accounts` deserializer on the given struct. Can provide further functionality through the use of attributes.&quot;,&quot;title&quot;:&quot;Accounts in anchor_lang - Rust&quot;,&quot;url&quot;:&quot;https://docs.rs/anchor-lang/latest/anchor_lang/derive.Accounts.html&quot;,&quot;version&quot;:&quot;1.0&quot;,&quot;provider_name&quot;:&quot;Docs&quot;,&quot;type&quot;:&quot;link&quot;}" format="small"><div class="react-component embed my-5" data-drag-handle="true" data-node-view-wrapper="" style="white-space:normal"><a class="link-embed-link" href="https://docs.rs/anchor-lang/latest/anchor_lang/derive.Accounts.html" target="_blank" rel="noreferrer"><div class="link-embed"><div class="flex-1"><div><h2>Accounts in anchor_lang - Rust</h2><p>Implements an `Accounts` deserializer on the given struct. Can provide further functionality through the use of attributes.</p></div><span><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-link h-3 w-3 my-auto inline mr-1"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>https://docs.rs</span></div></div></a></div></div><hr><figure float="none" data-type="figure" class="img-center"><img src="https://storage.googleapis.com/papyrus_images/b34c4310ed4a8429f54429ccfb1ffb8f03bc3657fa9f8f332bb4e2ae58b0ab84.avif" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACkQBADs=" nextheight="234" nextwidth="222" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p><em>Written by Maha.. See y'all tomorrow.</em></p>]]></content:encoded>
            <author>chakra-papers@newsletter.paragraph.com (Maha)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/bb07f3f11e4dba5515143721ba90b71e22892d1ee621f1d95b9c3c5026c28ce5.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[This Is What It Looks Like When Chakra Actually Works]]></title>
            <link>https://paragraph.com/@chakra-papers/four-components-one-loop-the-proof</link>
            <guid>FG7GRjkPc6lbezZxJRza</guid>
            <pubDate>Mon, 08 Jun 2026 16:07:32 GMT</pubDate>
            <description><![CDATA[From abstract math to a running loop, here is the exact architectural breakdown of how I eliminate the bridge target entirely in chakra]]></description>
            <content:encoded><![CDATA[<h2 id="h-chakra-series-3" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">CHAKRA SERIES - 3</h2><h3 id="h-hello-everyone-for-day-3-of-building-chakra" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Hello everyone for day 3 of building chakra</h3><p>Day 1 was the problem. $2 billion stolen. The bridge honeypot. The architecture that makes theft inevitable.</p><p>Day 2 was the idea. Remote account ownership. Distributed key generation. The threshold that replaces the vault. The cryptographic foundation that removes the target from existence.</p><p>Today is the proof.</p><p>Not descriptions. Not diagrams. The actual system. Every component. Every step. Running on real testnets with real transactions that anyone can verify.</p><p>This is what CHAKRA looks like when it works ( actually I build alot of it ) will be learnt understood in upcoming days with how I tackled the errors scary parts etc etc ..</p><hr><h3 id="h-part-1-the-four-components" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Part 1: The Four Components</h3><p>CHAKRA is not one thing. It is four components working together. Each one has a specific job. Each one is necessary. Together they produce something that has not existed before in the Solana ecosystem.</p><p>Let me introduce each one before showing you how they connect.</p><p><strong>The CHAKRA Controller.</strong> An Anchor program deployed on Solana. This is the brain. It accepts cross-chain intents from any Solana program. It locks funds in atomic escrow. It fires events to the network. It verifies proof when execution is complete. It enforces rollback when execution fails. Everything starts and ends here.</p><p><strong>The Sentinel Node Network.</strong> Three independent processes each running a Rust application. Each one holds one shard of the distributed key. Each one listens to Solana via WebSocket. When they hear a ControlIntent event they begin the signing ceremony. They communicate with each other over HTTP. They produce a valid threshold signature without any single one of them ever holding the complete key.</p><p><strong>The ChakraReceiver.</strong> A Solidity contract deployed on Base Sepolia. It waits. When the Sentinel Nodes call it with a valid TSS signature it verifies the signature against the registered TSS public key. It marks the intent as executed to prevent replay attacks. It releases the funds or executes the action on the Base side. It confirms.</p><p><strong>The Atomic Escrow.</strong> This is not a separate component exactly. It is baked into the CHAKRA Controller. But it deserves its own introduction because it is the safety guarantee that makes the whole system trustworthy. When you initiate an intent your funds lock in a Program Derived Address. They cannot move until one of two things happens. Either valid proof of execution arrives from the Sentinel Nodes within the timeout window and the escrow releases. Or the timeout expires and you call cancel_intent and everything returns to you automatically. There is no third outcome. There is no stuck state. There is no lost funds scenario. The Solana program enforces this with the same finality that Solana enforces everything.<br>( I know it might sound so different as of now but its really worth the time )</p><hr><h3 id="h-part-2-what-actually-happens-when-you-use-chakra" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Part 2: What Actually Happens When You Use CHAKRA</h3><figure float="none" data-type="figure" class="img-center"><img src="https://storage.googleapis.com/papyrus_images/f1bfffde42114534972c734d92fa99e2e3711be8ce6e7a439c08dbba1feec2ff.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAgCAIAAABywqTfAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEUUlEQVR4nI1Vv4/URhQ2xjI3eMd4bp0ZM8NocMZnhjhWNobVxmJNTlaczR7opBXFStvQUAVQaK5AgvwB1Ed1JQXVpTpdTYN0HaRBkZBIwX9AR0Oi9VuW43RK+KrR+H3zfn3v2bI+IggChJBlWU4Lz/PgAF8RQhhj13VxC9d1l8QFtre3Dw4Onjx58uLFi729vb/fvNnf29/Z2cEYE0J+f/hwf3//7du3Ozs79+7du3XrFniaM+0Wj7cfP3v27I/d3ffv3+/u7v758uXTp08PDg6MMcPh8P79+8+fP//nw4fJZLK1tfUZ33Vd3/ejKCKEYIw551EUSXmO87NxHMN9mqZaawieMdbtdpepfQbP88qyzFtkLYqiMMZUVWWMOZ4DgCwOn33fV0p1cOekc/JYs2MAlccYO44jpayqSmsNZe/gzv/491osn3ddF50+DU0Fg5WVU5RSEpBj+H6LpemRp5dhR1HU7/ePkt0W1heAMTYejz3v9KcrQgiltNvtglRYC0IIZIsQklIyxjjnYRgSQqIoEkIsgrVtu9frbWxcq4bVeaW01g8ePLhz9w48YVkWpXQ6nT569Ghra6vX6+V5fuPGjZs3b2ZZtuALIZqmSZKEcz4YDDY3N69fv55lGaXUcRwhBDwBN2tra1euXGmaRuuvrY/hnVtfX9dan1fKGMMo1VpzzqHyGOPhcDgajcqyrKqhMaZpfoqiCGM8N8AYF0VRVVVZlkIIQghCiDEWBIHneZRShFAYhmfOnDm1smKMSZJkMpnkeY5azNtmjOn3+0VRgKqhEUqpMAxd14VxgEiF4Eqppml6vZ6PfYyxlWXZUiGgP9d1bdteX18fjX6u63rj2sblS5eAT+lXUsrZbFZVFQyVNR6PKaVtIU8sl4frur/dvfv69et37969evXX7du/toXoYIzjOB6PxxfSlHM+nU6tfr/f7XaP+LcsK01TkEAURUopGE3btjk/a8yFOI7DMDTGzG89zyOE+BifsG3wP0+1TZtSKoSIogguj4gaITTna62n02mSJISQMAyDIJBSgnPQdbfbFULAJ4RQ0GLRP8dxkpavtWaM1XX9y3hc13VZlqC/iLGqqobDYdM0RVForQctpJTzibJtO8/zsvzh8uVLUsq6rpumGY1GRVGA/jif92w2m5VlKaVMkmQwGNR1neffLjK5kKY/Xr36XZ5LKUGtlFJQ2Hy6gkAIUdd1kiRn2+GdTCabm5ufpti27dXV1cPL//Dmt20bOgLlCIIAIgqC4LOpLsvyS+a/43XmbTvcBqvt9nQ6JYT8125sjaFZy021wFwCQQBNDsMQBL+E4zhaa9/3oXmwUQ8bLADOsyyrqsrzPJgw4MxmM8bYMRygQcGAr5SK4xjcIoRAZ2magsxhkS6NF+FhjIUQ31y8qLWWUiqlkhZF8T3MfJIk54SI47jX6ymlfN8/+i8IggAEzxhbHsIw5JzDFIC64bCk/Qv38tVCjXV4vQAAAABJRU5ErkJggg==" nextheight="1536" nextwidth="1024" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>Let me walk you through the complete flow from start to finish. Every single step. Nothing skipped.</p><p><strong>You call the CHAKRA Controller on Solana.</strong></p><p>You call initialize_intent on the Anchor program. You pass it everything it needs. The target chain ID. The amount. The destination address on the target chain. The timeout in slots. This is your instruction to the system. This is where everything begins.</p><p><strong>Your funds lock in the escrow.</strong></p><p>The moment your instruction lands the CHAKRA Controller creates an EscrowState account. A Program Derived Address seeded with your wallet address, the target chain ID, and a nonce. Your funds transfer into this PDA via a system program CPI. They are now held by the program. Only the program. The program will not release them until proof of execution arrives or the timeout expires. This is enforced by Solana's consensus. Not by trust. By math.</p><p><strong>The Controller fires the ControlIntent event.</strong></p><p>The program emits an event to the blockchain. This event contains everything the Sentinel Nodes need. The owner's public key. The target chain ID. The nonce. The amount. The source chain identifier. The target chain identifier. The destination address. The escrow PDA address. The timeout slot. This event is visible to anyone listening to the Solana program's logs via WebSocket subscription.</p><p><strong>The Sentinel Nodes hear it.</strong></p><p>Three Sentinel Node processes are running. Each one has an open WebSocket subscription to the Solana Devnet watching for logs from the CHAKRA program ID. The moment the ControlIntent event fires all three nodes detect it. Each one parses the event data. Each one verifies it is properly formatted and legitimate. And then the signing ceremony begins.</p><p><strong>The signing ceremony.</strong></p><p>Each Sentinel Node takes the intent payload and constructs the same message. Target chain ID in big-endian bytes. Nonce in big-endian bytes. Amount in big-endian bytes. Destination address padded to 64 bytes. All concatenated together in the same order on every node. Then each node runs Keccak256 over this payload to produce the message hash. The same hash on every node because they all received the same intent data.</p><p>Now the threshold signing begins. Each node signs the message hash using its own key shard as if it were a complete secp256k1 private key. This produces a partial signature. Each partial signature has an r value, an s value, and a recovery ID. The coordinator node collects partial signatures from two of the three nodes. Then it runs Lagrange interpolation over the two s values using the node indices as the x coordinates. The interpolation reconstructs the combined s value that corresponds to the master key evaluated at x equals zero. The r value comes from one of the partial signatures. The result is one valid complete secp256k1 signature. The same signature that would have been produced if the complete master key had signed the message directly.</p><p>The complete master key was never reconstructed anywhere. The shards never combined. Only the signatures combined. This is the cryptographic core of the entire system.</p><p><strong>The Base Sepolia call.</strong></p><p>The coordinator node takes the combined signature and constructs an Ethereum transaction calling execute_intent on the ChakraReceiver contract on Base Sepolia. The call includes the target chain ID, the amount, the destination address, the nonce, and the r, s, v values of the TSS signature. The ChakraReceiver verifies the signature using ECDSA recovery. It checks the recovered address against the registered TSS public key. If they match the intent is valid. The contract marks it as executed. The funds move. The action happens.</p><p><strong>Proof back to Solana.</strong></p><p>After Base Sepolia confirms the transaction the coordinator node submits a proof transaction back to the CHAKRA Controller on Solana. It calls submit_proof passing the Base Sepolia transaction hash and the TSS signature components. The CHAKRA Controller verifies the signature on-chain using Solana's native secp256k1_recover syscall. It checks the recovered public key against the registered TSS public key in the TssConfig account. If they match the proof is valid. The escrow closes. The EscrowState account is closed and the lamports go to the treasury. The intent is finalized.</p><p><strong>If something goes wrong.</strong></p><p>The timeout slot arrives and no valid proof has been submitted. Anyone can now call cancel_intent. The CHAKRA Controller checks that the timeout has passed and the escrow is not already finalized or cancelled. It closes the EscrowState account and returns all funds directly to the original owner. Automatically. Completely. No questions asked. No human intervention. The program enforces it..</p><hr><h3 id="h-part-3-what-this-proves" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Part 3: What This Proves</h3><p>CHAKRA on Day 3 of building in public is a working proof of concept. Let me be precise about what that means.</p><p>It proves the architecture is sound. A Solana program can initiate a cross-chain action, lock funds atomically, coordinate a distributed signing ceremony across multiple nodes, produce a valid native signature for a foreign chain, execute on that chain, receive proof, and release the escrow. This full loop works. It has been demonstrated on real testnets.</p><p>It proves the security model is viable. The distributed key architecture means no single component holds anything that can be stolen to compromise the system. The atomic escrow means users are protected even if the Sentinel Network fails entirely.</p><p>It proves the foundation that everything else gets built on top of. The SDK that lets any Solana developer add cross-chain capability to their Anchor program in under 100 lines of code. The documentation. The integrations. The decentralized Sentinel Network. None of that exists yet. All of that gets built on this foundation.</p><p>The foundation is real. The architecture is proven. The next phase of building starts now.<br>You might not understand many things and that's okay.. </p><hr><p>I know the code, where is the code will be explaining each part of every thing I built so far without any breaks lets be patient because its a big project and most importantly its Rust so gotta be careful with what I show what I explain thankyou for reading<br><br>Here are some resources <br><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.anchor-lang.com/docs/references/account-types">https://www.anchor-lang.com/docs/references/account-types</a><br><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://toc.cryptobook.us/">https://toc.cryptobook.us/</a> ( a complicated read for the math nerds wondering how independent shards combine into a single valid signature without ever revealing the master key, this is the academic foundation. It covers the exact Lagrange interpolation primitives we use off-chain at $x = 0$.)<br><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/paulmillr/noble-secp256k1">https://github.com/paulmillr/noble-secp256k1</a> ( a complicated read if u want to see how production-grade nodes handle low-level cryptographic operations like Keccak256 hashing and signature recovery points off-chain, dive into this repository. This is the global open-source standard for secp256k1 implementation )<br><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.rs/solana-program/latest/solana_program/">https://docs.rs/solana-program/latest/solana_program/</a> ( to those who want to dig directly into the bare metal, this is the foundational core library for all on-chain Solana development. It contains the standard entry points, system interfaces, and optimized runtime memory utilities .. am still learning alot from this )</p><hr><figure float="none" data-type="figure" class="img-center"><img src="https://storage.googleapis.com/papyrus_images/b34c4310ed4a8429f54429ccfb1ffb8f03bc3657fa9f8f332bb4e2ae58b0ab84.avif" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACkQBADs=" nextheight="234" nextwidth="222" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p><em>Written by Maha.. See y'all tomorrow.</em></p><br>]]></content:encoded>
            <author>chakra-papers@newsletter.paragraph.com (Maha)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/8b7ddfbe682361be34d859fd70c2c64a8776cf3990b0f629eb67e85ca3034de0.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[One Signature. Every Chain. No Bridge.]]></title>
            <link>https://paragraph.com/@chakra-papers/one-signature-every-chain-no-bridge</link>
            <guid>Nz2fvDd0wYxK1W7kOdBK</guid>
            <pubDate>Sun, 07 Jun 2026 12:04:53 GMT</pubDate>
            <description><![CDATA[CHAKRA stops bridge hacks by eliminating the vault entirely, using distributed cryptographic keys so Solana can control your Ethereum assets exactly where they sit.]]></description>
            <content:encoded><![CDATA[<h1 id="h-chakra-series-2" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">CHAKRA SERIES - 2</h1><p>Hello everyone,<br><br>Welcome to the day 2 series of chakra. Yesterday I showed you the problem.</p><p>$2 billion stolen. Not because of bad luck. Not because of careless developers. Because of one architectural decision that every bridge in existence still uses. Lock real assets in a custody contract. Create a honeypot. Watch it get drained.</p><p>I ended with one question.</p><p>What if the honeypot simply did not need to exist.</p><p>Today I am going to answer that question. Not the full answer. Not yet. Today is just the idea. The insight. The thing that made me start building.</p><p>The actual system, the code, the proof that it works, that comes tomorrow.</p><p>Today, just understand what changes when you think about this differently.</p><hr><h3 id="h-part-1-the-root-cause-one-more-time" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Part 1: The Root Cause One More Time</h3><p>Before I show you the solution I need to make sure the problem is absolutely clear. Because the solution only makes sense if you understand exactly what it is solving.</p><p>The bridge problem is not a code quality problem. It is not a team quality problem. It is not a security process problem.</p><p>It is a structural problem.</p><p>The structure is this. To use an asset on another chain, bridges move that asset into custody. A smart contract holds it. That smart contract becomes a target. The target gets hit.</p><p>Every attempt to fix bridges works within this structure. Better validators. More audits. Time delays. Emergency pause buttons. Multi-signature controls. These are all improvements to the security of the custody contract. They make the target harder to hit. They do not remove the target.</p><p>The only real fix is to remove the target entirely.</p><p>And the only way to remove the target is to never put assets in custody in the first place.</p><p>Which means the only real fix is to find a way for one blockchain to control assets on another blockchain without moving those assets anywhere.</p><p>That is what CHAKRA does.</p><hr><h3 id="h-part-2-the-insight-that-changes-everything" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Part 2: The Insight That Changes Everything</h3><p>Here is the core insight behind CHAKRA. Read this slowly because it is the entire idea in two sentences.</p><p>Every blockchain account is controlled by a private key. Whoever holds that private key controls that account.</p><p>That is it. That is the whole thing. An Ethereum account is not controlled by a human being. It is not controlled by a company. It is not controlled by a government. It is controlled by 64 hexadecimal characters. A private key. Whoever possesses that key can sign transactions from that account. That is the only rule that matters on any blockchain that has ever been built.</p><p>So here is the question CHAKRA asks.</p><p>What if a Solana program possessed the private key to an Ethereum account.</p><p>Not a human. Not a company. A Solana program. A piece of code living on the Solana blockchain. What if that program held the key that controlled an Ethereum wallet.</p><p>Then that Solana program could sign Ethereum transactions. Directly. Natively. Without any bridge. Without any wrapped token. Without any custody contract sitting somewhere full of money waiting to be stolen.</p><p>You call the Solana program. The Solana program signs an Ethereum transaction. The Ethereum network receives a valid native signature and executes. Your ETH never moved. Your ETH was never locked. Your ETH sat in an Ethereum account the entire time. The only thing that changed is who controls that account. And the answer is a Solana program controls it. Through cryptography alone.</p><p>No honeypot. Because there is nothing to put in a honeypot. Because there is no vault. Because the ETH never left Ethereum.</p><p>This is the idea. Now let me show you the problem inside the idea.</p><hr><h3 id="h-part-3-the-signature-problem" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Part 3: The Signature Problem</h3><p>If you understood Part 2 your immediate next question is probably this.</p><p>How does a Solana program hold an Ethereum private key. A program is code. Code does not secretly store a key somewhere safe. And if the key is stored on-chain, anyone can read it. And if anyone can read it, anyone can steal it and take full control of the Ethereum account instantly.</p><p>This is the Signature Problem. It is the hardest technical challenge in all of cross-chain execution. Every project that has ever tried to do what CHAKRA does has had to answer this question. Most of them either gave up entirely or built something that required an entirely new blockchain to solve it. A new chain with its own token. Its own validators. Its own ecosystem you have to learn and trust.</p><p>CHAKRA solves it without leaving Solana. Without a new chain. Without a new token. Inside the SVM itself.</p><p>The answer is something called Distributed Key Generation.</p><p>Here is the idea.</p><p>Instead of one private key stored in one place, CHAKRA splits the key into multiple pieces called shards. These shards are distributed across a network of independent computers called Sentinel Nodes. Each Sentinel Node holds exactly one shard. No single Sentinel Node ever holds the complete key. No single node can do anything alone.</p><p>Think about what this means for security.</p><p>In a bridge there is one target. One custody contract. Find the flaw in that contract and take everything. This is what happened to Ronin. This is what happened to Wormhole. This is what happened to Nomad.</p><p>In CHAKRA there is no single target. The key exists nowhere in complete form. It has never existed in complete form in any one place since the moment it was generated. To forge a signature an attacker would need to compromise multiple independent Sentinel Nodes simultaneously. Nodes running on different hardware. In different locations. Operated by different people. Not one vulnerability to find. Many. All at the same time.</p><p>The math behind this is called Threshold Signature Schemes. TSS. The specific model CHAKRA uses is 2-of-3. Three Sentinel Nodes each hold one shard. Any two of them can cooperate to produce a valid signature. All three are never required. Which means even if one node goes completely offline the system still works. But an attacker who compromises only one node has absolutely nothing. One shard of three. Mathematically useless without the others.</p><p>This is the cryptographic foundation of CHAKRA. Not custody. Key distribution. Not a vault. A threshold. Not a target. A network.</p><p>The bridge creates something to steal. CHAKRA creates nothing to steal.</p><hr><h3 id="h-tomorrow" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Tomorrow</h3><p>The idea is clear. The problem inside the idea is clear. The cryptographic answer to that problem is clear.</p><p>But an idea is not a product. A concept is not a system. A threshold signature scheme described in plain English is not a threshold signature scheme running on real hardware producing real signatures on a real testnet.</p><p>In next few days I will show you what is actually running. The Anchor program on Solana Devnet. The Sentinel Nodes communicating over a real network. The signing ceremony happening in real terminals. The transaction landing on Base Sepolia hopefully.. slowly and steadily without any rush..</p><p>Not descriptions. Not slides. The actual system doing the actual thing.</p><hr><p>Here are some documents u can refer to study to get better understanding of the terms <br><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.near.org/chain-abstraction/chain-signatures">https://docs.near.org/chain-abstraction/chain-signatures</a><br><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.zetachain.com/whitepaper.pdf">https://www.zetachain.com/whitepaper.pdf</a> (The "Architectural Proof": Section 3 and 4 describe how their "Observer-Signer" nodes allow contracts to manage external assets natively. It’s the closest technical cousin to what am building)<br><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=11455143">https://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=11455143</a> ( this is a complicated read. This is the foundational research paper for Threshold Signature Schemes (TSS))<br><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://hackmd.io/Ge_rpr9GTnGGF2oULTGJrg">https://hackmd.io/Ge_rpr9GTnGGF2oULTGJrg</a> ( this is also a complicated tough read  as u read technical deep dive shows exactly how a 2-of-3 MPC model works in production for millions of users. It proves that chakras model "Sentinel Node" architecture is reliable and fast. )</p><hr><br><figure float="none" data-type="figure" class="img-center"><img src="https://storage.googleapis.com/papyrus_images/b34c4310ed4a8429f54429ccfb1ffb8f03bc3657fa9f8f332bb4e2ae58b0ab84.avif" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACkQBADs=" nextheight="234" nextwidth="222" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p><em>Written by Maha.. See y'all tomorrow.</em></p>]]></content:encoded>
            <author>chakra-papers@newsletter.paragraph.com (Maha)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/df7a6c9a2f89e774f69f671063a341651f77a233111dfaa684e095edef45b0e6.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[The $2 Billion Problem Nobody Talks About]]></title>
            <link>https://paragraph.com/@chakra-papers/chakra-papers-partone-beyond-bridges-1</link>
            <guid>eCPkYXQhHPpmxNsPr1Nq</guid>
            <pubDate>Sat, 06 Jun 2026 12:11:02 GMT</pubDate>
            <description><![CDATA[The hidden architectural flaw behind $2B+ in bridge hacks and the question that led to a different path]]></description>
            <content:encoded><![CDATA[<h1 id="h-chakra-series-1" class="text-4xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>CHAKRA SERIES - 1</strong></h1><h2 id="h-before-we-start" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Before We Start</strong></h2><p>I want to tell you something before any technology gets mentioned.</p><p>In 2022, more than $2 billion was stolen from the crypto industry.</p><p>Not through password hacks. Not through phishing emails. Not because developers were careless or stupid or asleep at the wheel.</p><p>It was stolen because of one single architectural decision. A decision so deeply embedded in how blockchains connect to each other that almost nobody questions it anymore. A decision that every single cross-chain project in existence still uses today.</p><p>This document is about that decision. What it is. Why it exists. Why it is broken beyond repair. And the question it forced me to ask.</p><p>Tomorrow I will show you what I built because of that question.</p><p>Today, just understand the problem.</p><hr><h2 id="h-part-1-the-island-problem" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Part 1: The Island Problem</strong></h2><figure float="none" data-type="figure" class="img-center"><img src="https://storage.googleapis.com/papyrus_images/1763e3131790e5fb93992c564540c1ec03c8ea6f64d4b3921b26babd0107681f.avif" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACkQBADs=" nextheight="720" nextwidth="1080" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>Imagine the internet. But instead of one global internet that everyone shares, every country has its own completely separate internet with zero connection to any other.</p><p>America has its internet. Japan has its internet. India has its internet.</p><p>A website that exists on America's internet does not exist on Japan's internet. They cannot share files. They cannot send messages to each other. They cannot even see each other. Completely isolated. Completely parallel. Like two universes that happen to exist at the same time but can never touch.</p><p>Now imagine each country's internet has its own money. Americans use dollars on their internet. Japanese use yen on theirs. Indians use rupees on theirs. And because the internets are completely separate, those currencies can never interact. Not because of exchange rates. Not because of government rules. Because the underlying systems have no way to communicate at all.</p><p>If you want to buy something from a Japanese website using your American dollars, you literally cannot do it. The two systems do not know each other exist.</p><p>This is exactly the situation with blockchains today. Not approximately. Exactly.</p><p><strong>Bitcoin</strong> is one island. Running since 2009. Extraordinarily secure. Extraordinarily valuable. Right now approximately $900 billion worth of Bitcoin exists in the world. Nine hundred thousand million dollars. And Bitcoin can only do one thing: record who owns which Bitcoin. That is it. It cannot run complex programs. It cannot talk to Ethereum. It does not know Ethereum exists. $900 billion sitting on an island with no bridge to anywhere.</p><p><strong>Ethereum</strong> is another island. It can run complex programs called smart contracts. It powers lending protocols, trading platforms, NFT markets, and thousands of applications. Genuinely remarkable technology. Also completely isolated from Bitcoin. Ethereum can see Bitcoin from a distance the way you can see a mountain through your window. It cannot touch it. Cannot reach it. Cannot do anything with it.</p><p><strong>Solana</strong> is a third island. The fastest blockchain ever built. 65,000 transactions per second. 150 millisecond finality. One of the most powerful financial computation engines humanity has ever created. Also completely cut off from everything else. Solana cannot send a transaction to Ethereum. Solana cannot control a Bitcoin wallet. Solana cannot reach outside of itself in any direction.</p><p>Every major blockchain is its own island. Polygon. Avalanche. Base. Arbitrum. Sui. Aptos. Each one brilliant in its own specific way. Each one completely isolated from all the others.</p><p>Now here is why this matters in real money with real consequences.</p><p>That $900 billion in Bitcoin cannot participate in lending on Ethereum. It cannot be used as collateral on Solana. It cannot be put to work in the broader financial ecosystem that has been built across every other chain. Nine hundred billion dollars essentially frozen. Sitting on one island. Unable to interact with anything outside that island. Locked away not by law or regulation but by architectural reality.</p><p>This is the single biggest limitation holding back the entire crypto industry. Trillions of dollars of value locked in isolated systems that were never designed to talk to each other.</p><p>People needed a solution. So they built one.</p><p>They called it a bridge.</p><hr><h2 id="h-part-2-how-bridges-work" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Part 2: How Bridges Work</strong></h2><br><figure float="none" data-type="figure" class="img-center"><img src="https://storage.googleapis.com/papyrus_images/41fa80ed8cfb22e2ed162ff7c2fbc891499b8a26691d8f1996fb57b36b372236.png" blurdataurl="data:image/png;base64,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" nextheight="1024" nextwidth="1536" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>To understand why bridges fail, you need to understand how they actually work. Not at a vague high level. At the real mechanical level. Because the failure does not live in the idea. It lives in the mechanics. And once you see the mechanics clearly you will never look at a bridge the same way again.</p><p>Simple example. You have 1 ETH on Ethereum. You want to use that ETH on Solana. Maybe there is a great lending protocol on Solana offering excellent interest rates and you want to deposit your ETH there as collateral.</p><p>Here is exactly what a bridge does.</p><p><strong>Step one. You send your ETH to the bridge contract on Ethereum.</strong></p><p>The bridge has a smart contract deployed on Ethereum. A piece of code living on the blockchain. You send your 1 ETH to that contract. The contract locks it. Your ETH is now sitting inside the bridge's vault. You no longer control it. The contract does. It is held in custody.</p><p><strong>Step two. The bridge mints a copy on Solana.</strong></p><p>The bridge now creates something on Solana. It is called wETH. Wrapped ETH. It looks exactly like ETH. Its price tracks ETH in real time. You can trade it, lend it, use it as collateral, do almost anything with it that you could do with real ETH. But it is not ETH. It is not even close to ETH. It is an IOU. A receipt. A promise printed on a different chain that says: this token represents 1 real ETH that is locked in a vault on Ethereum. Return this token and we will give your real ETH back.</p><p>You now have 1 wETH on Solana. You deposit it in the lending protocol. You earn your interest. Everything seems fine.</p><p><strong>Step three. When you want your real ETH back, you return the wETH.</strong></p><p>You send your 1 wETH back to the bridge. The bridge destroys it permanently. Then it sends a signal to the Ethereum side saying: someone just returned their wETH, release 1 real ETH to their wallet. The bridge contract on Ethereum unlocks your ETH and sends it back to you.</p><p>Complete cycle. Lock on chain A. Mint a copy on chain B. Return the copy. Unlock on chain A.</p><p>At a surface level this sounds reasonable. Your asset gets represented on another chain. You use it. You come back and get the real thing. Clean. Simple. Problem solved.</p><p>Except it is not clean. And it is not simple. And the problem is very much not solved.</p><hr><h2 id="h-part-3-the-honeypot-nobody-talks-about" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Part 3: The Honeypot Nobody Talks About</strong></h2><p><br></p><figure float="none" data-type="figure" class="img-center"><img src="https://storage.googleapis.com/papyrus_images/98cc698061eb9d6a056fb6b120855df1c0d2aad24087521cb00b3965520fb215.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAVCAIAAACor3u9AAAACXBIWXMAAAsTAAALEwEAmpwYAAAGbUlEQVR4nE1VW4wcRxWt7q5Hd1VXVVe/H9M9szOenfGud7Oe3ezDXnsfsb0Pe2PH9jp+xbaMsTZS1tgoMiRALHAcSJBQzIeRI+UH85AsLBBIID4SIRQJySJIyUcg8IsQHxbiByHEx6DuNRjpqrrUt+pc3XvrnAuAbujI1BHWMNUxtYLU8hMNWdBNUFTo3NWpNJwQJgOGm0Ivw1FOVAxlaCZ1s9YmXkJkQGSA3BRFBXRig0kNWUA3NEiqDTR1iDXCDNMGOma1QX/nrEYYVBEKaoYTQBmQIMNhjQSplTZI3kHJAAkyERc0bZGoEF5sORFUMYpzIkNEhWYQHZkG5ToygYYtgwrd5NiW2HawxaHtQOkjJ6R+AlXEo5pIcunHwk9oUrdrbTWwXcR1ERfJtu3hQFtEBQsyUhl1E8iVbnLD4rrJdYiApkMDW5By3WRSKmI7BiLIdkhYY81hpFKWtnjSFH7mRXnWGtzx1MjSbG9l7+il56bu3TrxwXc31w7Mqqwti7ZI6jzMsPCo7UBCscU1HQKg6ciA3KI6oYzaiHJsO5Ary09YlDfa7bm94+srvfUDo5eOTlw5Pbl5YvzNy72bFyeuvzBz7/VjP7+7sXFqwc2asug4aZP5icGklIpJB2ETaDrQIcLYZBYllBNiaZhaQlnSIyqkfnrrpYWfvLFy48LUjQtTtz+35+3N2ZvnRn722sTdl3qX13qfPTF/aGn65Grv2pk9XrNjJw1aZcCVMpltIiJMC4CyRIRYlFhUUTt1lC8dU3o8TKBKNo7uPL86vH/X9tfOTTz65bl/vX/y0YOl/j8+3//TqY/v7vv2tX2nVyeme62Xz0ydODgpkoZM68yPbcaZRSG2DIhBTO2Eld2AmDCLMmZzzqlwvCiVQfbCSnd9sf3ikR0f3Znr//n8b9/e+Zk59eE7S/2/vNj/aLn/ydF3r43Pj2VnVkavnp4e7Ha9pE4dXwippMDEJAgDjDBGiCAcCokJMS0qBBdS+VHGvWTzSPfhW+N//97Uwze69691js3V1+a2L4+pqweTv35/10+vN68cbh2e8D/5zvz1U+PNwcEoHZDKE1JSRhFEEGJgIlITwsQ4llIKRixTCe57vh9nTpCeXShurmc3jqU3joRPFWJtpji61Esjr1uT80PuUCPIE7eI5O1zxZ3L3WSgndbqnh/ajGGEE9uedn1gm1bDcXzXTULPDTzFectzXc+L0yxI8uWJ9PBMtm+HWuiIPBJ7Ok4npUrQsZw902KTTT5UKNcVWSgPTQT1ViutFa4fWow1pPQotSACiFINW4iJKIq8IIxd2Yn9MAzTWqGi2sVn8pdXozuXigdX6memxERB93XtRmBtLjn99xf/8M3h2+fSWsCOTTrHd8VFs5mkeeCHQgjBbYwQRAgwmwOglXSA2PGCNPKj0G0WWXNbJ80b3zg/+Ld70/0fz3z8evff96euH3IzB559WvTf29P/dLn/4f4fXWvsavHf3Bj86vOt0bHRene4XsuTQG3zfM5sYlnAtCgAeslpoNlcDBS5HwTdTmu815uZ6q3PDRyfVA+/Mvjpm0P9B9O/+nL7yrz85/2p/i9mH93d2X8wc+uY/+xk9LtvTd57ZXZldbE3vbvVasVJHPg+sShEBOgQV3wzAACEcj/OMDG9KEqyfHysM5B5AICvPx998YD69avtD15tv/eF1u/fGnqwUfzx9sjmAbV/PH3l1MgPvjT/w68tHX1uL1eBH2c2dyCpaAxKJmOgwy3FxjZHTJbkpkLHTHg+JibQYSv3nx5Kj++prc/VR7eFFxbzjYOttd0NJW1XOaM7mr2RZl6kcRoDDSEqDIhLTcUW0AwANEODSEMmpAJzp5KnsmKEikpsDR2ZmNlRHK0vtk8vDea1+OT+wbMr3TDwS8XXDSrVjuEmVy7cGgM61Kq1RC+T0PTKjBILosqha2USVfO18j8AmpRy81Dz4nLDcdSzM+nVw03OSp3RNN2AKE996aiyLLrx2Croat36/M8qX6nhiDxxAY0xtjoe7d7u6sReGPEuLKQY4cfhNSPwlfJc3WRlPTAFlUoDbSvAfyEew1UBtlry+ETlRZh0c1VE5dTblthrEz6zTACgVmUf+o7jODqm5XwktHoyT+7+X4Cy6Vu1wppBnqQFdE03fGk5DGuQRMpqJ4xbpDxfBTAtCxNS3tJh9SyrAKCE/Q/qXAW7cc4aUQAAAABJRU5ErkJggg==" nextheight="1024" nextwidth="1536" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>Here is the thing that every bridge builder knows but nobody says loudly enough.</p><p>When you lock 1 ETH in the bridge contract, that ETH has to sit somewhere. It does not disappear. It does not teleport to Solana. It does not cease to exist. It stays in a smart contract on Ethereum, waiting for someone to come back and claim it.</p><p>As more people use the bridge, more assets pile up in that contract.</p><p>100 ETH. Then 1,000 ETH. Then 10,000 ETH. Then 100,000 ETH.</p><p>The more popular the bridge becomes, the more successful it is, the more valuable assets accumulate inside that one smart contract. Sitting on a public blockchain. Where anyone in the world can see it. Where anyone can read its code. Where anyone can attempt to interact with it. Where the exact dollar value of everything locked inside is visible to everyone at all times.</p><p>You have just created the most attractive theft target in the history of finance.</p><p>Stop and really think about this from an attacker's perspective.</p><p>There is a smart contract sitting on a public blockchain. Its address is completely public. Its code is completely public. Anyone can read it line by line and study it for as long as they want. The exact amount of money sitting inside it is public information updated in real time. And that amount is hundreds of millions of dollars.</p><p>An attacker does not need a gun. They do not need to break into a building. They do not need to steal passwords or trick individual users. They just need to find one flaw. One incorrect assumption somewhere in the code. One edge case the developers did not think about. One vulnerability in the logic for deciding who is authorized to withdraw funds.</p><p>Find that one flaw and they can drain everything. Not $1,000. Not $1 million. Every single dollar that every single user has ever deposited into that bridge. All of it. In one transaction.</p><p>This is not a theoretical risk. This is not a worst case scenario that cautious engineers dream up. This is exactly what has happened. Repeatedly. To the biggest and most well-funded bridge projects in the industry.</p><hr><h2 id="h-part-4-the-dollar2-billion-evidence" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Part 4: The $2 Billion Evidence</strong></h2><figure float="none" data-type="figure" class="img-center"><img src="https://storage.googleapis.com/papyrus_images/3241cef3df564cda1f608ba45f6eabaef2cc6f363867c3916ff5c0b8c8171e4d.png" blurdataurl="data:image/png;base64,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" nextheight="941" nextwidth="1672" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p><strong>Ronin Bridge. March 2022. $625 million stolen.</strong></p><p>Ronin was the official bridge for Axie Infinity, one of the most popular blockchain games ever created. Millions of players. Billions of dollars in trading volume. A team with serious resources and serious security awareness.</p><p>Ronin used nine validators as the security model. Think of them as nine guards who each have to approve any withdrawal. To steal the money an attacker would need five of the nine guards to cooperate or be compromised simultaneously. That sounds robust.</p><p>But here is what nobody noticed until it was too late. Four of the nine validators were controlled by the same single organization. And one more validator had given standing signing authority to a third party for operational convenience. An attacker found and compromised that third party. Four validators from the one organization plus the one compromised external authority equals five. Exactly the threshold. Exactly enough.</p><p>One transaction. $625 million. Completely gone. The largest crypto hack in history at the time. Not because the cryptography was weak. Not because of a traditional software bug. Because of one incorrect assumption about how the validator structure was actually set up in practice.</p><p><strong>Wormhole. February 2022. $320 million stolen.</strong></p><p>Wormhole was built specifically for Solana. It was one of the primary ways the Solana ecosystem connected to Ethereum. Heavily used. Well funded. Built by a serious team.</p><p>An attacker found a flaw in how Wormhole verified guardian signatures on the Solana side of the bridge. The program had a function that was supposed to verify a legitimate guardian had signed a cross-chain message. But due to an error in account validation logic, the attacker was able to pass a fake account through the verification check. The program accepted it as valid.</p><p>Using this forged validation, the attacker convinced Wormhole to mint 120,000 wETH on Solana without depositing any real ETH on the Ethereum side. They created $320 million worth of tokens from absolute nothing. Then redeemed a portion of them for real ETH on the other side. Real money, extracted through a fake signature, from a vault that was supposed to be secure.</p><p><strong>Nomad. August 2022. $190 million stolen.</strong></p><p>This one is the most devastating to read about because of how it ended.</p><p>A routine upgrade to Nomad's code introduced a vulnerability. The nature of the bug meant that Nomad would accept almost any message as valid regardless of whether it was actually signed and authorized correctly. A single attacker found this first and began draining the bridge.</p><p>Then word got out. Not just to sophisticated hackers. To everyone. Regular users. Opportunists. People who had never written a line of exploit code in their lives.</p><p>People started copying the attacker's original transaction and changing only their own destination wallet address. That was all it took. Copy. Paste. Change the address. Submit. Receive money that was not yours. $190 million drained not by one sophisticated actor but by hundreds of ordinary people who stumbled into the easiest money they had ever seen. A public free-for-all on a supposedly secure financial bridge.</p><hr><h2 id="h-part-5-the-pattern-that-cannot-be-explained-away" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Part 5: The Pattern That Cannot Be Explained Away</strong></h2><p>Look at these three hacks side by side. They are completely different in their technical details. Different vulnerabilities. Different chains. Different teams. Different months. Different attack methods.</p><p>But they share one thing and only one thing.</p><p>Every single one happened because there was a massive pot of real money sitting in a smart contract. Visible to everyone. Permanently accessible to anyone with enough skill and patience to find the right flaw. Just waiting.</p><p>The Ronin team was competent. The Wormhole team was competent. The Nomad team was competent. These were not amateur operations. These were serious engineers with real security budgets and real audit processes and real awareness of the stakes involved.</p><p>It did not matter.</p><p>Because competence cannot eliminate the fundamental problem. The moment you lock real assets in a custody contract you have created an attack target. You can make that target harder to hit. You can audit the code. You can add multiple validators. You can introduce time delays and rate limits and emergency pause mechanisms. You can do everything right by every standard that exists.</p><p>And you will still lose eventually. Because eventually someone will find the one thing you missed. And when they find it on a bridge holding $500 million, they will not politely report it. They will take everything.</p><p>This is not pessimism. This is what the historical record shows without ambiguity. Billions of dollars. Dozens of bridges. Multiple years. Teams with every resource available to them.</p><p>The architecture produces the outcome. Every single time.</p><hr><h2 id="h-part-6-the-question" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>Part 6: The Question</strong></h2><br><figure float="none" data-type="figure" class="img-center"><img src="https://storage.googleapis.com/papyrus_images/33b9802039f55755651e13c805299850ac9b11429ed76550b8d7371823a77be4.avif" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACkQBADs=" nextheight="607" nextwidth="1080" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>After reading about these hacks over and over I kept returning to one question that I could not get out of my head.</p><p>Why does the asset have to sit somewhere?</p><p>The entire bridge model is built on one assumption so fundamental that nobody questions it. The assumption is that to use an asset on another chain, you have to move it. Or at minimum lock it somewhere while a copy lives on the other chain. The real asset must be custodied. Someone or something must hold it while you use the representation of it elsewhere.</p><p>But what if that assumption is wrong.</p><p>What if instead of moving assets between chains, a program on one chain could directly own and control an account on another chain. Natively. Without moving anything. Without locking anything in a vault. Without creating a target for anyone to attack.</p><p>What if the honeypot simply did not need to exist.</p><p>Not a smaller honeypot. Not a better protected honeypot. No honeypot at all.</p><p>If a Solana program could directly own an Ethereum account, it would not need to move ETH to Solana. It would not need to lock ETH somewhere and mint a copy. It would just control the ETH where it already lives, on Ethereum, using cryptography instead of custody.</p><p>No vault. No IOU. No target. No $625 million waiting to be found by the right attacker on the right day.</p><p>That question is what I started building three months ago.</p><p>Tomorrow I will show you exactly what the answer looks like.<br><br>Here are some reads which u can refer to<br>Statistics &amp; The "Island" Problem<br><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.chainalysis.com/blog/2022-biggest-year-ever-for-crypto-hacking/"><strong>https://www.chainalysis.com/blog/2022-biggest-year-ever-for-crypto-hacking/</strong></a><br><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.chainalysis.com/blog/cross-chain-bridge-hacks-2022/"><strong>https://www.chainalysis.com/blog/cross-chain-bridge-hacks-2022/</strong></a><br>Proof of the Hacks<br><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://medium.com/@alabs.ken/analysis-of-the-ronin-bridge-exploit-d9e1de8d1696"><strong>https://medium.com/@alabs.ken/analysis-of-the-ronin-bridge-exploit-d9e1de8d1696</strong></a><br><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://rekt.news/nomad-rekt"><strong>https://rekt.news/nomad-rekt</strong></a><br>Real-Time "Honeypot" Data<br><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://l2beat.com/interop/summary"><strong>https://l2beat.com/interop/summary</strong></a><br>This live dashboard shows the "Total Value Locked" (TVL) in every major bridge. It proves that the "honeypots" are still growing and still visible to everyone.<br><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://defillama.com/hacks"><strong>https://defillama.com/hacks</strong></a><br>A searchable database of every major hack. You can filter for "Bridge" to show the sheer frequency of these failures.<br><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.near.org/"><strong>https://docs.near.org/</strong></a><br>These are neardocs validates our Part 6 by explaining the move toward "account-based" cross-chain interaction instead of "asset-moving" interaction.</p><figure float="none" data-type="figure" class="img-center"><img src="https://storage.googleapis.com/papyrus_images/b34c4310ed4a8429f54429ccfb1ffb8f03bc3657fa9f8f332bb4e2ae58b0ab84.avif" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACkQBADs=" nextheight="234" nextwidth="222" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><hr><p><em>Written by Maha.. See y'all tomorrow.</em></p><br>]]></content:encoded>
            <author>chakra-papers@newsletter.paragraph.com (Maha)</author>
            <category>@solana @web3 @ethereum @blockchain</category>
            <enclosure url="https://storage.googleapis.com/papyrus_images/618ac2ced00eb932f26079e4005fb420870fafd9fc8283240c48401e0501b27e.jpg" length="0" type="image/jpg"/>
        </item>
    </channel>
</rss>