1

I got a thirty program which only IDL and programID are available. Let call it HelloWorld program.

If we call HelloWorld directly, it is simple, just as :

const idl = require(<PATH_TO_IDL>);
const programId = new PublicKey(<HELLO_WORLD_PROGRAM_ID>)

// Create program
const helloworldProgram = new Program(idl, programId, provider);

// create transaction
const transaction = await helloworldProgram.methods
  .helloWorld({greetTo: "xxx"})
  .accounts({
    authority: wallet.publicKey,
    ...,
  })
  .transaction()
  
  // ... send Tx ...

But,

now , I need to call it from my own program, assume it is RelayProgram

#[program]
pub mod relay_program {
    use super::*;

    pub fn to_call_helloworld(
        ctx: Context<...>,
        dataOfHelloWorldCall: &[u8] 
    ) -> Result<()> {

            ...
    let helloIX = Instruction::new_with_bytes(
        ctx.accounts.hello_program_account.key, // HelloWorld program's ID
        dataOfHelloWorldCall,   // <== This is the data that we crafted in client JS
        vec![ctx.accounts...],  //  accounts
    );

    invoke(&helloIX, &[...])
    
    }
}

Now the client code would be:

const idl_1 = require(<PATH_TO_HELLO_WORLD_IDL>);
const programId_1 = new PublicKey(<HELLO_WORLD_PROGRAM_ID>)
     ...

// Create program
const helloworldProgram = new Program(idl_1, programId_1, provider);

        ...
// Create program
const relayProgram = new Program(idl_2, programId_2, provider);

// Question: how to build data that passed to relayProgram and then apply on HelloWorld program ?
let _dataOfHelloWorldCall = ?  // [u8] array of data
    
// create transaction
const transaction = await relayProgram.methods
  .to_call_helloworld({dataOfHelloWorldCall: _dataOfHelloWorldCall})
  .accounts({
     authority: wallet.publicKey,
     programB: PROGRAM_B_ID,
    // rest is the same as those as call 'helloWorld' directory
  })
  .transaction()
  
  // ... send Tx ...

Now, the questions are:

  1. does this idea feasible? If not, please share workable idea.

  2. how can I build the IX data ?

Thanks

1 Answer 1

1

You can do it by invoking the raw instruction with AccountMeta. First you'll need the instruction's name in the source, which is probably the same as the idl but in snake_case:

/// The function discrminator is constructed from these 8 bytes. Typically, the namespace is
/// "global" or "state"
pub fn get_function_hash(namespace: &str, name: &str) -> [u8; 8] {
    let preimage = format!("{}:{}", namespace, name);
    let mut sighash = [0u8; 8];
    sighash.copy_from_slice(&hash::hash(preimage.as_bytes()).to_bytes()[..8]);
    sighash
}

Now construct the raw Instruction from metadata and pack the args (if any) after the function discriminator:

pub fn some_program_cpi_instruction(
    ctx: &Context<YourContext>,
    program_id: Pubkey,
    some_arg: u8,
) -> Result<Instruction> {
    let accounts = vec![
        AccountMeta::new(ctx.accounts.payer.key(), true), // a mutable signer account (like a payer/funder)
        AccountMeta::new_readonly(ctx.accounts.some_acc.key(), false), // an immutable non-signer account
       // ... the rest of your accounts like so (note order matters, so follow the order you see in the CPI)
    ];

    let instruction = Instruction {
        program_id,
        accounts,
        data: open_position_ix_data(some_arg),
    };
    Ok(instruction)
}

#[derive(AnchorSerialize, AnchorDeserialize)]
struct SomeProgramCpiArgs {
    some_arg: u8
}

pub fn some_program_ix_data(
    some_arg: u8
) -> Vec<u8> {
    let hash = get_function_hash("global", "the_ix_name");
    let mut buf: Vec<u8> = vec![];
    buf.extend_from_slice(&hash);
    let args = SomeProgramCpiArgs {
        some_arg,
    };
    args.serialize(&mut buf).unwrap();
    buf
}

Now finally from your instruction, call the CPI with invoke or invoke_signed:

    let some_ix: Instruction = some_program_cpi_instruction(
        &ctx,
        ctx.accounts.some_program.key(),
        some_arg
    )?;
    let acc_infos = vec![
        ctx.accounts.payer.to_account_info(), 
        ctx.accounts.some_acc.to_account_info(), 
        // Same accounts, again noting that order matters...
    ];
    invoke(&some_ix, &acc_infos)?;
1
  • thanks, I am trying on it. Will feedback while result outcome. Commented Mar 8 at 2:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.