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;
Ok(())
}
//lock_program(eg;) => (dojimatoken+authority)
#[account]
pub struct ContractMapping {
dojimatokenaddress: String,
authority: Pubkey
}
#[derive(Accounts)]
#[instruction(solanaprogaddress: Pubkey)]
pub struct CreateRegistry<'info> {
//error handling
#[account(mut)]
pub user: Signer<'info>,
#[account(
init,
payer = user,
seeds = [solanaprogaddress.key().as_ref(), b"dojima_contract_mapping"],
bump,
space = 8 + 64 + 64,
)]
pub contract_mapping: Account<'info, ContractMapping>,
pub system_program: Program<'info, System>,
#[account(
seeds = [b"dojima_bridge_admin4"],
bump,
)]
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;
msg!(
"{} {} {} {}",
"TransferPayload",
nonce,
destination_contract,
payload
);
Ok(())
}
- 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]] = &[
b"dojima_bridge_authority",
&[254]
];
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(ctx.accounts.signing_pda.key(),true),
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();
data.extend(destnation_prog_data_len_bytes.to_vec());
data.extend(destination_prog_data);
let account_infos = vec![
ctx.accounts.signing_pda.to_account_info(),
sender_ATA.to_account_info(),
receiver_ATA.to_account_info(),
tkn_prog.to_account_info(),
auth.to_account_info(),
bridge_owner_pda.to_account_info(),
];
//Construct instruction
let inst = Instruction{
program_id: destination_prog.key(),
accounts: account_metas,
data: data,
};
//Invoke CPI
invoke_signed(&inst, &account_infos, signer_seeds);
Ok(())
}
- 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.