refactor message posting ix, remove old ixs
Change-Id: Idfa487a189ec2fb3ed2029ddce10fc02aef1255b
This commit is contained in:
parent
aad19ff8f8
commit
dfa746476f
|
@ -3,8 +3,8 @@
|
||||||
#![cfg(not(feature = "no-entrypoint"))]
|
#![cfg(not(feature = "no-entrypoint"))]
|
||||||
|
|
||||||
use solana_program::{
|
use solana_program::{
|
||||||
account_info::AccountInfo,entrypoint, entrypoint::ProgramResult, program_error::PrintProgramError,
|
account_info::AccountInfo, entrypoint, entrypoint::ProgramResult,
|
||||||
pubkey::Pubkey,
|
program_error::PrintProgramError, pubkey::Pubkey,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{error::Error, state::Bridge};
|
use crate::{error::Error, state::Bridge};
|
||||||
|
|
|
@ -3,20 +3,20 @@
|
||||||
|
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
|
|
||||||
use primitive_types::U256;
|
|
||||||
use solana_program::{
|
use solana_program::{
|
||||||
instruction::{AccountMeta, Instruction},
|
instruction::{AccountMeta, Instruction},
|
||||||
program_error::ProgramError,
|
program_error::ProgramError,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::instruction::BridgeInstruction::PublishMessage;
|
||||||
use crate::{
|
use crate::{
|
||||||
instruction::BridgeInstruction::{
|
instruction::BridgeInstruction::{Initialize, PostVAA, VerifySignatures},
|
||||||
CreateWrapped, Initialize, PokeProposal, PostVAA, TransferOut, VerifySignatures,
|
state::{Bridge, BridgeConfig},
|
||||||
},
|
|
||||||
state::{AssetMeta, Bridge, BridgeConfig},
|
|
||||||
vaa::{VAABody, VAA},
|
vaa::{VAABody, VAA},
|
||||||
};
|
};
|
||||||
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
use std::io::{Cursor, Read, Write};
|
||||||
|
|
||||||
/// chain id of this chain
|
/// chain id of this chain
|
||||||
pub const CHAIN_ID_SOLANA: u8 = 1;
|
pub const CHAIN_ID_SOLANA: u8 = 1;
|
||||||
|
@ -24,6 +24,8 @@ pub const CHAIN_ID_SOLANA: u8 = 1;
|
||||||
pub const MAX_LEN_GUARDIAN_KEYS: usize = 20;
|
pub const MAX_LEN_GUARDIAN_KEYS: usize = 20;
|
||||||
/// maximum size of a posted VAA
|
/// maximum size of a posted VAA
|
||||||
pub const MAX_VAA_SIZE: usize = 1000;
|
pub const MAX_VAA_SIZE: usize = 1000;
|
||||||
|
/// maximum size of a posted VAA
|
||||||
|
pub const MAX_PAYLOAD_SIZE: usize = 400;
|
||||||
/// size of a foreign address in bytes
|
/// size of a foreign address in bytes
|
||||||
const FOREIGN_ADDRESS_SIZE: usize = 32;
|
const FOREIGN_ADDRESS_SIZE: usize = 32;
|
||||||
|
|
||||||
|
@ -45,34 +47,21 @@ pub struct InitializePayload {
|
||||||
pub config: BridgeConfig,
|
pub config: BridgeConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
pub struct PublishMessagePayload {
|
||||||
#[derive(Clone, Copy, Debug)]
|
/// unique nonce for this message
|
||||||
pub struct TransferOutPayload {
|
|
||||||
/// amount to transfer
|
|
||||||
pub amount: U256,
|
|
||||||
/// chain id to transfer to
|
|
||||||
pub chain_id: u8,
|
|
||||||
/// Information about the asset to be transferred
|
|
||||||
pub asset: AssetMeta,
|
|
||||||
/// address on the foreign chain to transfer to
|
|
||||||
pub target: ForeignAddress,
|
|
||||||
/// unique nonce of the transfer
|
|
||||||
pub nonce: u32,
|
pub nonce: u32,
|
||||||
|
/// message payload
|
||||||
|
pub payload: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
impl Clone for PublishMessagePayload {
|
||||||
#[derive(Clone, Copy, Debug)]
|
fn clone(&self) -> PublishMessagePayload {
|
||||||
pub struct TransferOutPayloadRaw {
|
let payload = self.payload.clone();
|
||||||
/// amount to transfer
|
return PublishMessagePayload {
|
||||||
pub amount: [u8; 32],
|
payload,
|
||||||
/// chain id to transfer to
|
nonce: self.nonce,
|
||||||
pub chain_id: u8,
|
};
|
||||||
/// Information about the asset to be transferred
|
}
|
||||||
pub asset: AssetMeta,
|
|
||||||
/// address on the foreign chain to transfer to
|
|
||||||
pub target: ForeignAddress,
|
|
||||||
/// unique nonce of the transfer
|
|
||||||
pub nonce: u32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
@ -98,50 +87,16 @@ pub enum BridgeInstruction {
|
||||||
/// 4. `[signer]` The fee payer for new account creation
|
/// 4. `[signer]` The fee payer for new account creation
|
||||||
Initialize(InitializePayload),
|
Initialize(InitializePayload),
|
||||||
|
|
||||||
/// Burns or locks a (wrapped) asset `token` from `sender` on the Solana chain.
|
/// Publishes a message over the Wormhole network.
|
||||||
///
|
/// See docs for accounts
|
||||||
/// Wrapped asset transfer out
|
PublishMessage(PublishMessagePayload),
|
||||||
/// 0. `[writable]` The from token account
|
|
||||||
/// 1. `[]` The System program.
|
|
||||||
/// 2. `[]` The spl token program.
|
|
||||||
/// 3. `[]` The clock SysVar
|
|
||||||
/// 4. `[derived]` The bridge config
|
|
||||||
/// 5. `[writable, derived, empty]` The new transfer out tracking account
|
|
||||||
/// 6. `[writable, derived]` The mint of the wrapped asset
|
|
||||||
/// 7. ..7+M '[signer]' M signer accounts (from token authority)
|
|
||||||
///
|
|
||||||
/// Native token transfer out
|
|
||||||
/// 0. `[writable]` The from token account
|
|
||||||
/// 1. `[]` The System program.
|
|
||||||
/// 2. `[]` The spl token program.
|
|
||||||
/// 3. `[]` The clock SysVar
|
|
||||||
/// 4. `[derived]` The bridge config
|
|
||||||
/// 5. `[writable, derived, empty]` The new transfer out tracking account
|
|
||||||
/// 6. `[writable, derived]` The mint of the wrapped asset
|
|
||||||
/// 7. `[writable, derived]` The custody token account of the bridge
|
|
||||||
/// 8. ..8+M '[signer]' M signer accounts (from token authority)
|
|
||||||
TransferOut(TransferOutPayload),
|
|
||||||
|
|
||||||
/// Submits a VAA signed by `guardian` on a valid `proposal`.
|
/// Submits a VAA signed by `guardian` on a valid `proposal`.
|
||||||
/// See docs for accounts
|
/// See docs for accounts
|
||||||
PostVAA(VAAData),
|
PostVAA(VAAData),
|
||||||
|
|
||||||
/// Deletes a `proposal` after the `VAA_EXPIRATION_TIME` is over to free up space on chain.
|
|
||||||
/// This returns the rent to the sender.
|
|
||||||
EvictTransferOut(),
|
|
||||||
|
|
||||||
/// Deletes a `ExecutedVAA` after the `VAA_EXPIRATION_TIME` is over to free up space on chain.
|
|
||||||
/// This returns the rent to the sender.
|
|
||||||
EvictClaimedVAA(),
|
|
||||||
|
|
||||||
/// Pokes a proposal with no valid VAAs attached so guardians reprocess it.
|
|
||||||
PokeProposal(),
|
|
||||||
|
|
||||||
/// Verifies signature instructions
|
/// Verifies signature instructions
|
||||||
VerifySignatures(VerifySigPayload),
|
VerifySignatures(VerifySigPayload),
|
||||||
|
|
||||||
/// Creates a new wrapped asset
|
|
||||||
CreateWrapped(AssetMeta),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BridgeInstruction {
|
impl BridgeInstruction {
|
||||||
|
@ -157,32 +112,32 @@ impl BridgeInstruction {
|
||||||
Initialize(*payload)
|
Initialize(*payload)
|
||||||
}
|
}
|
||||||
1 => {
|
1 => {
|
||||||
let payload: &TransferOutPayloadRaw = unpack(input)?;
|
let mut payload_data = Cursor::new(input);
|
||||||
let amount = U256::from_big_endian(&payload.amount);
|
|
||||||
|
|
||||||
TransferOut(TransferOutPayload {
|
let nonce = payload_data
|
||||||
amount,
|
.read_u32::<BigEndian>()
|
||||||
chain_id: payload.chain_id,
|
.map_err(|_| ProgramError::InvalidArgument)?;
|
||||||
asset: payload.asset,
|
let mut message_payload: Vec<u8> = vec![];
|
||||||
target: payload.target,
|
payload_data
|
||||||
nonce: payload.nonce,
|
.read(&mut message_payload)
|
||||||
})
|
.map_err(|_| ProgramError::InvalidArgument)?;
|
||||||
|
|
||||||
|
let payload: PublishMessagePayload = PublishMessagePayload {
|
||||||
|
nonce,
|
||||||
|
payload: message_payload,
|
||||||
|
};
|
||||||
|
|
||||||
|
PublishMessage(payload)
|
||||||
}
|
}
|
||||||
2 => {
|
2 => {
|
||||||
let payload: VAAData = input[1..].to_vec();
|
let payload: VAAData = input[1..].to_vec();
|
||||||
PostVAA(payload)
|
PostVAA(payload)
|
||||||
}
|
}
|
||||||
5 => PokeProposal(),
|
3 => {
|
||||||
6 => {
|
|
||||||
let payload: &VerifySigPayload = unpack(input)?;
|
let payload: &VerifySigPayload = unpack(input)?;
|
||||||
|
|
||||||
VerifySignatures(*payload)
|
VerifySignatures(*payload)
|
||||||
}
|
}
|
||||||
7 => {
|
|
||||||
let payload: &AssetMeta = unpack(input)?;
|
|
||||||
|
|
||||||
CreateWrapped(*payload)
|
|
||||||
}
|
|
||||||
_ => return Err(ProgramError::InvalidInstructionData),
|
_ => return Err(ProgramError::InvalidInstructionData),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -201,24 +156,15 @@ impl BridgeInstruction {
|
||||||
};
|
};
|
||||||
*value = payload;
|
*value = payload;
|
||||||
}
|
}
|
||||||
Self::TransferOut(payload) => {
|
Self::PublishMessage(payload) => {
|
||||||
output.resize(size_of::<TransferOutPayloadRaw>() + 1, 0);
|
let mut v: Cursor<Vec<u8>> = Cursor::new(Vec::new());
|
||||||
output[0] = 1;
|
v.write_u8(1).map_err(|_| ProgramError::InvalidArgument)?;
|
||||||
#[allow(clippy::cast_ptr_alignment)]
|
v.write_u32::<BigEndian>(payload.nonce)
|
||||||
let value = unsafe {
|
.map_err(|_| ProgramError::InvalidArgument)?;
|
||||||
&mut *(&mut output[size_of::<u8>()] as *mut u8 as *mut TransferOutPayloadRaw)
|
v.write(&payload.payload)
|
||||||
};
|
.map_err(|_| ProgramError::InvalidArgument)?;
|
||||||
|
|
||||||
let mut amount_bytes = [0u8; 32];
|
output = v.into_inner();
|
||||||
payload.amount.to_big_endian(&mut amount_bytes);
|
|
||||||
|
|
||||||
*value = TransferOutPayloadRaw {
|
|
||||||
amount: amount_bytes,
|
|
||||||
chain_id: payload.chain_id,
|
|
||||||
asset: payload.asset,
|
|
||||||
target: payload.target,
|
|
||||||
nonce: payload.nonce,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
Self::PostVAA(payload) => {
|
Self::PostVAA(payload) => {
|
||||||
output.resize(1, 0);
|
output.resize(1, 0);
|
||||||
|
@ -226,35 +172,15 @@ impl BridgeInstruction {
|
||||||
#[allow(clippy::cast_ptr_alignment)]
|
#[allow(clippy::cast_ptr_alignment)]
|
||||||
output.extend_from_slice(&payload);
|
output.extend_from_slice(&payload);
|
||||||
}
|
}
|
||||||
Self::EvictTransferOut() => {
|
|
||||||
output.resize(1, 0);
|
|
||||||
output[0] = 3;
|
|
||||||
}
|
|
||||||
Self::EvictClaimedVAA() => {
|
|
||||||
output.resize(1, 0);
|
|
||||||
output[0] = 4;
|
|
||||||
}
|
|
||||||
Self::PokeProposal() => {
|
|
||||||
output.resize(1, 0);
|
|
||||||
output[0] = 5;
|
|
||||||
}
|
|
||||||
Self::VerifySignatures(payload) => {
|
Self::VerifySignatures(payload) => {
|
||||||
output.resize(size_of::<VerifySigPayload>() + 1, 0);
|
output.resize(size_of::<VerifySigPayload>() + 1, 0);
|
||||||
output[0] = 6;
|
output[0] = 3;
|
||||||
#[allow(clippy::cast_ptr_alignment)]
|
#[allow(clippy::cast_ptr_alignment)]
|
||||||
let value = unsafe {
|
let value = unsafe {
|
||||||
&mut *(&mut output[size_of::<u8>()] as *mut u8 as *mut VerifySigPayload)
|
&mut *(&mut output[size_of::<u8>()] as *mut u8 as *mut VerifySigPayload)
|
||||||
};
|
};
|
||||||
*value = payload;
|
*value = payload;
|
||||||
}
|
}
|
||||||
Self::CreateWrapped(payload) => {
|
|
||||||
output.resize(size_of::<AssetMeta>() + 1, 0);
|
|
||||||
output[0] = 7;
|
|
||||||
#[allow(clippy::cast_ptr_alignment)]
|
|
||||||
let value =
|
|
||||||
unsafe { &mut *(&mut output[size_of::<u8>()] as *mut u8 as *mut AssetMeta) };
|
|
||||||
*value = payload;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(output)
|
Ok(output)
|
||||||
}
|
}
|
||||||
|
@ -300,48 +226,36 @@ pub fn initialize(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates an 'TransferOut' instruction.
|
/// Creates an 'PublishMessage' instruction.
|
||||||
#[cfg(not(target_arch = "bpf"))]
|
#[cfg(not(target_arch = "bpf"))]
|
||||||
pub fn transfer_out(
|
pub fn publish_message(
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
payer: &Pubkey,
|
payer: &Pubkey,
|
||||||
token_account: &Pubkey,
|
t: &PublishMessagePayload,
|
||||||
token_mint: &Pubkey,
|
|
||||||
t: &TransferOutPayload,
|
|
||||||
) -> Result<Instruction, ProgramError> {
|
) -> Result<Instruction, ProgramError> {
|
||||||
let data = BridgeInstruction::TransferOut(*t).serialize()?;
|
|
||||||
|
|
||||||
let bridge_key = Bridge::derive_bridge_id(program_id)?;
|
let bridge_key = Bridge::derive_bridge_id(program_id)?;
|
||||||
let transfer_key = Bridge::derive_transfer_id(
|
|
||||||
|
let message_key = Bridge::derive_message_id(
|
||||||
program_id,
|
program_id,
|
||||||
&bridge_key,
|
&bridge_key,
|
||||||
t.asset.chain,
|
CHAIN_ID_SOLANA,
|
||||||
t.asset.address,
|
payer.to_bytes(),
|
||||||
t.chain_id,
|
|
||||||
t.target,
|
|
||||||
token_account.to_bytes(),
|
|
||||||
t.nonce,
|
t.nonce,
|
||||||
|
&t.payload,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let mut accounts = vec![
|
let accounts = vec![
|
||||||
AccountMeta::new_readonly(*program_id, false),
|
AccountMeta::new_readonly(*program_id, false),
|
||||||
AccountMeta::new_readonly(solana_program::system_program::id(), false),
|
AccountMeta::new_readonly(solana_program::system_program::id(), false),
|
||||||
AccountMeta::new_readonly(spl_token::id(), false),
|
|
||||||
AccountMeta::new_readonly(solana_program::sysvar::rent::id(), false),
|
AccountMeta::new_readonly(solana_program::sysvar::rent::id(), false),
|
||||||
AccountMeta::new_readonly(solana_program::sysvar::clock::id(), false),
|
AccountMeta::new_readonly(solana_program::sysvar::clock::id(), false),
|
||||||
AccountMeta::new_readonly(solana_program::sysvar::instructions::id(), false),
|
AccountMeta::new_readonly(solana_program::sysvar::instructions::id(), false),
|
||||||
AccountMeta::new(*token_account, false),
|
|
||||||
AccountMeta::new_readonly(bridge_key, false),
|
AccountMeta::new_readonly(bridge_key, false),
|
||||||
AccountMeta::new(transfer_key, false),
|
AccountMeta::new(message_key, false),
|
||||||
AccountMeta::new(*token_mint, false),
|
|
||||||
AccountMeta::new(*payer, true),
|
AccountMeta::new(*payer, true),
|
||||||
];
|
];
|
||||||
|
|
||||||
// If the token is a native solana token add a custody account
|
let data = BridgeInstruction::PublishMessage(t.clone()).serialize()?;
|
||||||
if t.asset.chain == CHAIN_ID_SOLANA {
|
|
||||||
let custody_key = Bridge::derive_custody_id(program_id, &bridge_key, token_mint)?;
|
|
||||||
accounts.push(AccountMeta::new(custody_key, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Instruction {
|
Ok(Instruction {
|
||||||
program_id: *program_id,
|
program_id: *program_id,
|
||||||
|
@ -429,103 +343,28 @@ pub fn post_vaa(
|
||||||
// Make program writeable
|
// Make program writeable
|
||||||
accounts[0] = AccountMeta::new(*program_id, false);
|
accounts[0] = AccountMeta::new(*program_id, false);
|
||||||
accounts.push(AccountMeta::new(u.buffer, false));
|
accounts.push(AccountMeta::new(u.buffer, false));
|
||||||
let (programdata_address, _) = Pubkey::find_program_address(&[program_id.as_ref()], &solana_program::bpf_loader_upgradeable::id());
|
let (programdata_address, _) = Pubkey::find_program_address(
|
||||||
|
&[program_id.as_ref()],
|
||||||
|
&solana_program::bpf_loader_upgradeable::id(),
|
||||||
|
);
|
||||||
accounts.push(AccountMeta::new(programdata_address, false));
|
accounts.push(AccountMeta::new(programdata_address, false));
|
||||||
accounts.push(AccountMeta::new_readonly(solana_program::bpf_loader_upgradeable::id(), false));
|
accounts.push(AccountMeta::new_readonly(
|
||||||
|
solana_program::bpf_loader_upgradeable::id(),
|
||||||
|
false,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
VAABody::Transfer(t) => {
|
VAABody::Message(t) => {
|
||||||
if t.source_chain == CHAIN_ID_SOLANA {
|
let message_key = Bridge::derive_message_id(
|
||||||
// Solana (any) -> Ethereum (any)
|
|
||||||
let transfer_key = Bridge::derive_transfer_id(
|
|
||||||
program_id,
|
program_id,
|
||||||
&bridge_key,
|
&bridge_key,
|
||||||
t.asset.chain,
|
t.emitter_chain,
|
||||||
t.asset.address,
|
t.emitter_address,
|
||||||
t.target_chain,
|
|
||||||
t.target_address,
|
|
||||||
t.source_address,
|
|
||||||
t.nonce,
|
t.nonce,
|
||||||
|
&t.data,
|
||||||
)?;
|
)?;
|
||||||
accounts.push(AccountMeta::new(transfer_key, false))
|
accounts.push(AccountMeta::new(message_key, false))
|
||||||
} else if t.asset.chain == CHAIN_ID_SOLANA {
|
|
||||||
// Foreign (wrapped) -> Solana (native)
|
|
||||||
let mint_key = Pubkey::new(&t.asset.address);
|
|
||||||
let custody_key = Bridge::derive_custody_id(program_id, &bridge_key, &mint_key)?;
|
|
||||||
accounts.push(AccountMeta::new_readonly(spl_token::id(), false));
|
|
||||||
accounts.push(AccountMeta::new(mint_key, false));
|
|
||||||
accounts.push(AccountMeta::new(Pubkey::new(&t.target_address), false));
|
|
||||||
accounts.push(AccountMeta::new(custody_key, false));
|
|
||||||
} else {
|
|
||||||
// Foreign (native) -> Solana (wrapped)
|
|
||||||
let wrapped_key = Bridge::derive_wrapped_asset_id(
|
|
||||||
program_id,
|
|
||||||
&bridge_key,
|
|
||||||
t.asset.chain,
|
|
||||||
t.asset.decimals,
|
|
||||||
t.asset.address,
|
|
||||||
)?;
|
|
||||||
let wrapped_meta_key =
|
|
||||||
Bridge::derive_wrapped_meta_id(program_id, &bridge_key, &wrapped_key)?;
|
|
||||||
accounts.push(AccountMeta::new_readonly(spl_token::id(), false));
|
|
||||||
accounts.push(AccountMeta::new(wrapped_key, false));
|
|
||||||
accounts.push(AccountMeta::new(Pubkey::new(&t.target_address), false));
|
|
||||||
accounts.push(AccountMeta::new(wrapped_meta_key, false));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Instruction {
|
|
||||||
program_id: *program_id,
|
|
||||||
accounts,
|
|
||||||
data,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a 'CreateWrapped' instruction.
|
|
||||||
pub fn create_wrapped(
|
|
||||||
program_id: &Pubkey,
|
|
||||||
payer: &Pubkey,
|
|
||||||
meta: AssetMeta,
|
|
||||||
) -> Result<Instruction, ProgramError> {
|
|
||||||
let data = BridgeInstruction::CreateWrapped(meta).serialize()?;
|
|
||||||
|
|
||||||
let bridge_key = Bridge::derive_bridge_id(program_id)?;
|
|
||||||
let wrapped_mint_key = Bridge::derive_wrapped_asset_id(
|
|
||||||
program_id,
|
|
||||||
&bridge_key,
|
|
||||||
meta.chain,
|
|
||||||
meta.decimals,
|
|
||||||
meta.address,
|
|
||||||
)?;
|
|
||||||
let wrapped_meta_key =
|
|
||||||
Bridge::derive_wrapped_meta_id(program_id, &bridge_key, &wrapped_mint_key)?;
|
|
||||||
|
|
||||||
let accounts = vec![
|
|
||||||
AccountMeta::new_readonly(solana_program::system_program::id(), false),
|
|
||||||
AccountMeta::new_readonly(spl_token::id(), false),
|
|
||||||
AccountMeta::new_readonly(solana_program::sysvar::rent::id(), false),
|
|
||||||
AccountMeta::new_readonly(bridge_key, false),
|
|
||||||
AccountMeta::new(*payer, true),
|
|
||||||
AccountMeta::new(wrapped_mint_key, false),
|
|
||||||
AccountMeta::new(wrapped_meta_key, false),
|
|
||||||
];
|
|
||||||
|
|
||||||
Ok(Instruction {
|
|
||||||
program_id: *program_id,
|
|
||||||
accounts,
|
|
||||||
data,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates an 'PokeProposal' instruction.
|
|
||||||
#[cfg(not(target_arch = "bpf"))]
|
|
||||||
pub fn poke_proposal(
|
|
||||||
program_id: &Pubkey,
|
|
||||||
transfer_proposal: &Pubkey,
|
|
||||||
) -> Result<Instruction, ProgramError> {
|
|
||||||
let data = BridgeInstruction::PokeProposal().serialize()?;
|
|
||||||
|
|
||||||
let accounts = vec![AccountMeta::new(*transfer_proposal, false)];
|
|
||||||
|
|
||||||
Ok(Instruction {
|
Ok(Instruction {
|
||||||
program_id: *program_id,
|
program_id: *program_id,
|
||||||
|
|
Loading…
Reference in New Issue