# Pulling The Trigger: The Start of a Cross-Chain Intent

*Inside the primary instruction that validates inputs, creates PDAs, and initiates the Chakra cross-chain lifecycle.*

By [Chakra](https://paragraph.com/@chakra-papers) · 2026-06-14

@solana@anchor@rust

---

_CHAKRA SERIES - 7_
-------------------

**_Pulling The Trigger_**_  
Inside initialize\__[_intent.rs_](http://intent.rs)_: How CHAKRA Locks Funds And Starts The Clock_

_Welcome to Day 7._

_Day 4 was_ [_state.rs_](http://state.rs)_, the containers. Day 5 was_ [_errors.rs_](http://errors.rs)_, the ten promises. Day 6 was_ [_events.rs_](http://events.rs)_, the voice that talks to the Sentinel Network without any middleman._

_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._

_Something has to actually call them. Something has to be the first thing that runs when a real user does a real thing._

_That something is_ `initialize_intent.rs`

_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._

* * *

**_Part 1: The Accounts Struct_**

_rust_

    #[derive(Accounts)]
    #[instruction(target_chain_id: u64, nonce: u64)]
    pub struct InitializeIntent<'info> {
    #[account(mut)]
    pub user: Signer<'info>,
    #[account(
        init,
        payer = user,
        space = EscrowState::LEN,
        seeds = [b"escrow", user.key().as_ref(), &target_chain_id.to_le_bytes(), &nonce.to_le_bytes()],
        bump
    )]
    pub escrow_account: Account<'info, EscrowState>,
    
    pub system_program: Program<'info, System>,
    }

  

_Three accounts. That is it._

_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._

_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._

_system\_program is there because we are about to do a CPI, and I will explain what that means in Part 4._

_One small detail before we move on. Notice target\_chain\__[_id.to_](http://id.to)_\_le\_bytes() and_ [_nonce.to_](http://nonce.to)_\_le\_bytes(). LE means little endian. Keep that in your head. Hold onto it. When we get to submit\__[_proof.rs_](http://proof.rs) _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._

* * *

**_Part 2: The Three Gatekeepers_**

_Before anything gets written, before any lamport moves, the function runs three checks._

_rust_

    require!(amount > 0, ChakraError::MathError);
    require!(timeout_slots >= 150, ChakraError::TimeoutTooShort);
    require!(timeout_slots <= 216000, ChakraError::TimeoutTooLong);

_You already met all three of these errors back on Day 5. amount > 0 stops someone from creating a pointless empty intent. timeout\_slots >= 150 stops someone from setting a timeout so short the Sentinel Network physically cannot finish in time. timeout\_slots <= 216000 stops someone from locking an account open for years._

_This is what I mean when I say_ [_errors.rs_](http://errors.rs) _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._

_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._

* * *

**_Part 3: Filling In The Blueprint_**

_Once the gatekeepers pass, we start writing into the escrow account that Anchor just created for us._

_rust_

    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;

_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._

_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._

* * *

  

**_Part 4: The Transfer, And What CPI Even Means_**

_rust_

    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)?;

_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._

_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._

_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._

_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._

* * *

**_Part 5: The Signal_**

_rust_

    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,
    });

_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._

_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._

* * *

**_Part 6: What This One Function Actually Proves_**

_Step back for a second. This single function, from top to bottom, is the entire user facing promise of CHAKRA in miniature._

_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._

_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._

* * *

**_In part 8_**

_we look at the file on the opposite end of the spectrum. initialize\__[_intent.rs_](http://intent.rs) _is the longest instruction in the program. Next file, cancel\__[_intent.rs_](http://intent.rs)_, 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._

_See y'all for Part 8._

* * *

_Here are some resources if u want to dig deeper_

[

Program Derived Address
-----------------------

Learn how to use Program Derived Addresses (PDAs) in Anchor programs to create deterministic account addresses.

https://www.anchor-lang.com



](https://www.anchor-lang.com/docs/basics/pda)

[

Cross Program Invocation
------------------------

Cross Program Invocation (CPI) on Solana - how programs call other programs using invoke and invoke\_signed, handle PDA signers, and compose onchain functionality.

https://solana.com

![Cross Program Invocation](https://storage.googleapis.com/papyrus_images/3875bd93d590912382b00973fa251d4942cbb154c7e4ac27a8900dfd58de44aa.png)

](https://solana.com/docs/core/cpi)

[

std - Rust
----------

The Rust Standard Library

https://doc.rust-lang.org



](https://doc.rust-lang.org/std/index.html?search=to_be_bytes)

[

account in anchor\_lang - Rust
------------------------------

An attribute for a data structure representing a Solana account.

https://docs.rs



](https://docs.rs/anchor-lang/latest/anchor_lang/attr.account.html)

* * *

![](https://storage.googleapis.com/papyrus_images/b34c4310ed4a8429f54429ccfb1ffb8f03bc3657fa9f8f332bb4e2ae58b0ab84.avif)

_Written by Maha.. thankyou_

---

*Originally published on [Chakra](https://paragraph.com/@chakra-papers/the-start-of-cross-chain-intent)*
