Solana State Sender

The solana-state-sender program is named as solana-dojima-bridge. This program maintains the mapping of registered programs on solana blockchain(Only registered programs can interact with state-sender program). Solana programs should register and call this state-sender program to interact with other contracts on destination chains.

//admin will create the registry - (token mapping)
   pub fn create_registry(
ctx: Context<CreateRegistry>, 
solanaprogaddress: Pubkey,
dojimatoken: String, 
authority: Pubkey) -> Result<()> 
       if ctx.accounts.user.key() != ctx.accounts.admin.admin {
           return  err!(MyError::NotOwner);
       ctx.accounts.contract_mapping.dojimatokenaddress = dojimatoken;
       ctx.accounts.contract_mapping.authority = authority;
   //lock_program(eg;) => (dojimatoken+authority)
   pub struct ContractMapping {
    dojimatokenaddress: String,
    authority: Pubkey

   #[instruction(solanaprogaddress: Pubkey)]
   pub struct CreateRegistry<'info> {
    //error handling
    pub user: Signer<'info>,
        payer = user,
        seeds = [solanaprogaddress.key().as_ref(), b"dojima_contract_mapping"],
        space = 8 + 64 + 64,
    pub contract_mapping: Account<'info, ContractMapping>,
    pub system_program: Program<'info, System>,
        seeds = [b"dojima_bridge_admin4"],
    pub admin: Account<'info, Admin>,
  • Create_registry - Admin will register the programs and create a contract mapping for every program.
  • dojimatoken - This is the equivalent of solana token on dojima chain.
  • authority - PDA of a program which signs to interact with the state-sender contract should be passed as an authority.
pub fn transfer_payload(
       ctx: Context<TransferPayload>,
       destination_contract: String,
       payload: String
   ) -> Result<()> {
       //only authority(ie; PDA of locking program) is allowed to call this function
       if ctx.accounts.contract_mapping.authority.key() != ctx.accounts.user.key() {
           return  err!(MyError::UnauthorizedContract);
       if ctx.accounts.contract_mapping.dojimatokenaddress != destination_contract {
           return  err!(MyError::UnauthorizedContract);
       let nonce =  ctx.accounts.counter.count;
       ctx.accounts.counter.count += 1;
           "{} {} {} {}",
  • transfer_payload - This function is used to transfer abi-encoded payload to dojima chain contracts.
  • destination_contract - Which contract to call on the destination chain.
  • payload - abi-encoded data that should be passed to the destination chain.
  • A check will be made whether the calling program is registered in the registry or not.
  • destination_contract should be the same as the registered dojima token address.
  • Counter keeps track of the number of interactions happening with the state-sender program.
  • It will increment the counter by one for everytime a transfer_payload (or) token_transfer_from_solana function is called.
  • After incrementing the counter TransferPayload message(event) is emitted which is filtered by solana-client(narada) and submitted to hermes.
pub fn token_transfer_to_solana(
ctx: Context<TokenTransferToSolana>, 
destination_prog_data: Vec<u8>
) -> Result<()> {
       //check whether the signer is TSS

       //Define seeds for signing
       let seeds: &[&[u8]] = &[
       let signer_seeds:&[&[&[u8]]] = &[&seeds[..]];

       //unpack accounts
       let destination_prog = &ctx.accounts.destination_program;
       let sender_ATA = &ctx.accounts.from_token_account;
       let receiver_ATA = &ctx.accounts.to_token_account;
       let tkn_prog = &ctx.accounts.token_program;
       let auth = &ctx.accounts.authority;
       let bridge_owner_pda = &ctx.accounts.bridge_owner_pda;

       //Construct accoount_metas for CPI invocation
       let account_metas = vec![
           AccountMeta::new(sender_ATA.key(), false),
           AccountMeta::new(receiver_ATA.key(), false),
           AccountMeta::new_readonly(tkn_prog.key(), false),
           AccountMeta::new_readonly(auth.key(), false),
           AccountMeta::new_readonly(bridge_owner_pda.key(), false),

       //Instruction identifier of global:execute_state
       let inst_identifier:Vec<u8> = vec![34, 26, 147, 217, 17, 18, 70, 124];

       //Prepare data for CPI
       let destination_prog_data_len = destination_prog_data.len() as u32;
       let destnation_prog_data_len_bytes = destination_prog_data_len.to_le_bytes();
       let mut data = inst_identifier.to_vec();

       let account_infos = vec![

       //Construct instruction
        let inst = Instruction{
           program_id: destination_prog.key(),
           accounts: account_metas,
           data: data,

       //Invoke CPI
       invoke_signed(&inst, &account_infos, signer_seeds);

  • token_transfer_to_solana - This function will be used by cross-chain dapp developers to transfer (or) mint SPL tokens from the destination chain.
  • destination_prog_data - Encoded byte data that is passed from destination chain(Developer can choose the way data gets encoded on destination chain.)
  • All the accounts and encoded byte data passed from the destination chain are unpacked in this function and sent to a given destination contract on solana.
  • The unpacked data will be passed to the execute_state function of the destination contract(Destination contract should implement execute_state function).
  • Prepare the required data to construct instruction and CPI is done using invoke_signed.
  • The instruction will be signed using a PDA derived from defined seeds.