parent
2747839bd4
commit
4ba7885c62
|
@ -16,6 +16,14 @@ Initializes a new Bridge at `bridge`.
|
||||||
| 3 | guardian_set | GuardianSet | | ✅ | ✅ | ✅ |
|
| 3 | guardian_set | GuardianSet | | ✅ | ✅ | ✅ |
|
||||||
| 4 | payer | Account | ✅ | | | |
|
| 4 | payer | Account | ✅ | | | |
|
||||||
|
|
||||||
|
#### PokeProposal
|
||||||
|
|
||||||
|
Pokes a `TransferOutProposal` so it is reprocessed by the guardians.
|
||||||
|
|
||||||
|
| Index | Name | Type | signer | writeable | empty | derived |
|
||||||
|
| ----- | ------ | ------------ | ------ | --------- | ----- | ------- |
|
||||||
|
| 0 | proposal | TransferOutProposal | | ✅ | ️ | ✅ |
|
||||||
|
|
||||||
#### TransferOut
|
#### TransferOut
|
||||||
|
|
||||||
Burns a wrapped asset `token` from `sender` on the Solana chain.
|
Burns a wrapped asset `token` from `sender` on the Solana chain.
|
||||||
|
@ -29,11 +37,12 @@ Parameters:
|
||||||
| 0 | bridge_p | BridgeProgram | | | ️ | |
|
| 0 | bridge_p | BridgeProgram | | | ️ | |
|
||||||
| 1 | sys | SystemProgram | | | ️ | |
|
| 1 | sys | SystemProgram | | | ️ | |
|
||||||
| 2 | token_program | SplToken | | | ️ | |
|
| 2 | token_program | SplToken | | | ️ | |
|
||||||
| 3 | token_account | TokenAccount | | ✅ | | |
|
| 3 | clock | Sysvar | | | ️ | ✅ |
|
||||||
| 4 | bridge | BridgeConfig | | | | |
|
| 4 | token_account | TokenAccount | | ✅ | | |
|
||||||
| 5 | proposal | TransferOutProposal | | ✅ | ✅ | ✅ |
|
| 5 | bridge | BridgeConfig | | | | |
|
||||||
| 6 | token | WrappedAsset | | ✅ | | ✅ |
|
| 6 | proposal | TransferOutProposal | | ✅ | ✅ | ✅ |
|
||||||
| 7 | payer | Account | ✅ | | | |
|
| 7 | token | WrappedAsset | | ✅ | | ✅ |
|
||||||
|
| 8 | payer | Account | ✅ | | | |
|
||||||
|
|
||||||
#### TransferOutNative
|
#### TransferOutNative
|
||||||
|
|
||||||
|
@ -47,12 +56,13 @@ The transfer proposal will be tracked at a new account `proposal` where a VAA wi
|
||||||
| 0 | bridge_p | BridgeProgram | | | ️ | |
|
| 0 | bridge_p | BridgeProgram | | | ️ | |
|
||||||
| 1 | sys | SystemProgram | | | ️ | |
|
| 1 | sys | SystemProgram | | | ️ | |
|
||||||
| 2 | token_program | SplToken | | | ️ | |
|
| 2 | token_program | SplToken | | | ️ | |
|
||||||
| 3 | token_account | TokenAccount | | ✅ | | |
|
| 3 | clock | Sysvar | | | ️ | ✅ |
|
||||||
| 4 | bridge | BridgeConfig | | | | |
|
| 4 | token_account | TokenAccount | | ✅ | | |
|
||||||
| 5 | proposal | TransferOutProposal | | ✅ | ✅ | ✅ |
|
| 5 | bridge | BridgeConfig | | | | |
|
||||||
| 6 | token | Mint | | ✅ | | |
|
| 6 | proposal | TransferOutProposal | | ✅ | ✅ | ✅ |
|
||||||
| 7 | payer | Account | ✅ | | | |
|
| 7 | token | Mint | | ✅ | | |
|
||||||
| 8 | custody_account | TokenAccount | | ✅ | opt | ✅ |
|
| 8 | payer | Account | ✅ | | | |
|
||||||
|
| 9 | custody_account | TokenAccount | | ✅ | opt | ✅ |
|
||||||
|
|
||||||
#### EvictTransferOut
|
#### EvictTransferOut
|
||||||
|
|
||||||
|
|
|
@ -137,14 +137,6 @@ impl Agent for AgentImpl {
|
||||||
|
|
||||||
println!("lockup changed in slot: {}", v.context.slot);
|
println!("lockup changed in slot: {}", v.context.slot);
|
||||||
|
|
||||||
let time = match rpc.get_block_time(v.context.slot) {
|
|
||||||
Ok(v) => v as u64,
|
|
||||||
Err(e) => {
|
|
||||||
println!("failed to fetch block time for event: {}", e);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let b = match Bridge::unpack_immutable::<TransferOutProposal>(
|
let b = match Bridge::unpack_immutable::<TransferOutProposal>(
|
||||||
v.value.account.data.as_slice(),
|
v.value.account.data.as_slice(),
|
||||||
) {
|
) {
|
||||||
|
@ -163,7 +155,7 @@ impl Agent for AgentImpl {
|
||||||
LockupEvent {
|
LockupEvent {
|
||||||
slot: v.context.slot,
|
slot: v.context.slot,
|
||||||
lockup_address: v.value.pubkey.to_string(),
|
lockup_address: v.value.pubkey.to_string(),
|
||||||
time,
|
time: b.lockup_time as u64,
|
||||||
event: Some(Event::New(LockupEventNew {
|
event: Some(Event::New(LockupEventNew {
|
||||||
nonce: b.nonce,
|
nonce: b.nonce,
|
||||||
source_chain: CHAIN_ID_SOLANA as u32,
|
source_chain: CHAIN_ID_SOLANA as u32,
|
||||||
|
@ -181,7 +173,7 @@ impl Agent for AgentImpl {
|
||||||
LockupEvent {
|
LockupEvent {
|
||||||
slot: v.context.slot,
|
slot: v.context.slot,
|
||||||
lockup_address: v.value.pubkey.to_string(),
|
lockup_address: v.value.pubkey.to_string(),
|
||||||
time,
|
time: b.lockup_time as u64,
|
||||||
event: Some(Event::VaaPosted(LockupEventVaaPosted {
|
event: Some(Event::VaaPosted(LockupEventVaaPosted {
|
||||||
nonce: b.nonce,
|
nonce: b.nonce,
|
||||||
source_chain: CHAIN_ID_SOLANA as u32,
|
source_chain: CHAIN_ID_SOLANA as u32,
|
||||||
|
|
|
@ -14,7 +14,7 @@ use solana_sdk::{
|
||||||
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::error::Error::VAATooLong;
|
use crate::error::Error::VAATooLong;
|
||||||
use crate::instruction::BridgeInstruction::{Initialize, PostVAA, TransferOut};
|
use crate::instruction::BridgeInstruction::{Initialize, PokeProposal, PostVAA, TransferOut};
|
||||||
use crate::state::{AssetMeta, Bridge, BridgeConfig};
|
use crate::state::{AssetMeta, Bridge, BridgeConfig};
|
||||||
use crate::vaa::{VAABody, VAA};
|
use crate::vaa::{VAABody, VAA};
|
||||||
|
|
||||||
|
@ -123,6 +123,9 @@ pub enum BridgeInstruction {
|
||||||
/// Deletes a `ExecutedVAA` after the `VAA_EXPIRATION_TIME` is over to free up space on chain.
|
/// Deletes a `ExecutedVAA` after the `VAA_EXPIRATION_TIME` is over to free up space on chain.
|
||||||
/// This returns the rent to the sender.
|
/// This returns the rent to the sender.
|
||||||
EvictClaimedVAA(),
|
EvictClaimedVAA(),
|
||||||
|
|
||||||
|
/// Pokes a proposal with no valid VAAs attached so guardians reprocess it.
|
||||||
|
PokeProposal(),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BridgeInstruction {
|
impl BridgeInstruction {
|
||||||
|
@ -153,6 +156,7 @@ impl BridgeInstruction {
|
||||||
let payload: VAAData = input[1..].to_vec();
|
let payload: VAAData = input[1..].to_vec();
|
||||||
PostVAA(payload)
|
PostVAA(payload)
|
||||||
}
|
}
|
||||||
|
5 => PokeProposal(),
|
||||||
_ => return Err(ProgramError::InvalidInstructionData),
|
_ => return Err(ProgramError::InvalidInstructionData),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -201,6 +205,9 @@ impl BridgeInstruction {
|
||||||
Self::EvictClaimedVAA() => {
|
Self::EvictClaimedVAA() => {
|
||||||
output[0] = 4;
|
output[0] = 4;
|
||||||
}
|
}
|
||||||
|
Self::PokeProposal() => {
|
||||||
|
output[0] = 5;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(output)
|
Ok(output)
|
||||||
}
|
}
|
||||||
|
@ -273,6 +280,7 @@ pub fn transfer_out(
|
||||||
AccountMeta::new_readonly(*program_id, false),
|
AccountMeta::new_readonly(*program_id, false),
|
||||||
AccountMeta::new_readonly(solana_sdk::system_program::id(), false),
|
AccountMeta::new_readonly(solana_sdk::system_program::id(), false),
|
||||||
AccountMeta::new_readonly(spl_token::id(), false),
|
AccountMeta::new_readonly(spl_token::id(), false),
|
||||||
|
AccountMeta::new_readonly(solana_sdk::sysvar::clock::id(), false),
|
||||||
AccountMeta::new(*token_account, false),
|
AccountMeta::new(*token_account, false),
|
||||||
AccountMeta::new(bridge_key, false),
|
AccountMeta::new(bridge_key, false),
|
||||||
AccountMeta::new(transfer_key, false),
|
AccountMeta::new(transfer_key, false),
|
||||||
|
@ -374,6 +382,23 @@ pub fn post_vaa(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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 mut accounts = vec![AccountMeta::new(*transfer_proposal, false)];
|
||||||
|
|
||||||
|
Ok(Instruction {
|
||||||
|
program_id: *program_id,
|
||||||
|
accounts,
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Unpacks a reference from a bytes buffer.
|
/// Unpacks a reference from a bytes buffer.
|
||||||
pub fn unpack<T>(input: &[u8]) -> Result<&T, ProgramError> {
|
pub fn unpack<T>(input: &[u8]) -> Result<&T, ProgramError> {
|
||||||
if input.len() < size_of::<u8>() + size_of::<T>() {
|
if input.len() < size_of::<u8>() + size_of::<T>() {
|
||||||
|
|
|
@ -59,6 +59,11 @@ impl Bridge {
|
||||||
|
|
||||||
Self::process_vaa(program_id, accounts, vaa_body, &vaa)
|
Self::process_vaa(program_id, accounts, vaa_body, &vaa)
|
||||||
}
|
}
|
||||||
|
PokeProposal() => {
|
||||||
|
info!("Instruction: PokeProposal");
|
||||||
|
|
||||||
|
Self::process_poke(program_id, accounts)
|
||||||
|
}
|
||||||
_ => panic!(""),
|
_ => panic!(""),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -133,6 +138,23 @@ impl Bridge {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Transfers a wrapped asset out
|
||||||
|
pub fn process_poke(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
|
||||||
|
let account_info_iter = &mut accounts.iter();
|
||||||
|
let proposal_info = next_account_info(account_info_iter)?;
|
||||||
|
|
||||||
|
let mut transfer_data = proposal_info.data.borrow_mut();
|
||||||
|
let mut proposal: &mut TransferOutProposal = Self::unpack(&mut transfer_data)?;
|
||||||
|
if proposal.vaa_time != 0 {
|
||||||
|
return Err(Error::VAAAlreadySubmitted.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increase poke counter
|
||||||
|
proposal.poke_counter += 1;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Transfers a wrapped asset out
|
/// Transfers a wrapped asset out
|
||||||
pub fn process_transfer_out(
|
pub fn process_transfer_out(
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
|
@ -144,6 +166,7 @@ impl Bridge {
|
||||||
next_account_info(account_info_iter)?; // Bridge program
|
next_account_info(account_info_iter)?; // Bridge program
|
||||||
next_account_info(account_info_iter)?; // System program
|
next_account_info(account_info_iter)?; // System program
|
||||||
next_account_info(account_info_iter)?; // Token program
|
next_account_info(account_info_iter)?; // Token program
|
||||||
|
let clock_info = next_account_info(account_info_iter)?;
|
||||||
let sender_account_info = next_account_info(account_info_iter)?;
|
let sender_account_info = next_account_info(account_info_iter)?;
|
||||||
let bridge_info = next_account_info(account_info_iter)?;
|
let bridge_info = next_account_info(account_info_iter)?;
|
||||||
let transfer_info = next_account_info(account_info_iter)?;
|
let transfer_info = next_account_info(account_info_iter)?;
|
||||||
|
@ -153,6 +176,7 @@ impl Bridge {
|
||||||
let sender = Bridge::token_account_deserialize(sender_account_info)?;
|
let sender = Bridge::token_account_deserialize(sender_account_info)?;
|
||||||
let bridge = Bridge::bridge_deserialize(bridge_info)?;
|
let bridge = Bridge::bridge_deserialize(bridge_info)?;
|
||||||
let mint = Bridge::mint_deserialize(mint_info)?;
|
let mint = Bridge::mint_deserialize(mint_info)?;
|
||||||
|
let clock = Clock::from_account_info(clock_info)?;
|
||||||
|
|
||||||
// Does the token belong to the mint
|
// Does the token belong to the mint
|
||||||
if sender.mint != *mint_info.key {
|
if sender.mint != *mint_info.key {
|
||||||
|
@ -209,6 +233,7 @@ impl Bridge {
|
||||||
transfer.foreign_address = t.target;
|
transfer.foreign_address = t.target;
|
||||||
transfer.amount = t.amount;
|
transfer.amount = t.amount;
|
||||||
transfer.to_chain_id = t.chain_id;
|
transfer.to_chain_id = t.chain_id;
|
||||||
|
transfer.lockup_time = clock.unix_timestamp as u32;
|
||||||
|
|
||||||
// Make sure decimals are correct
|
// Make sure decimals are correct
|
||||||
transfer.asset = AssetMeta {
|
transfer.asset = AssetMeta {
|
||||||
|
@ -231,6 +256,7 @@ impl Bridge {
|
||||||
next_account_info(account_info_iter)?; // Bridge program
|
next_account_info(account_info_iter)?; // Bridge program
|
||||||
next_account_info(account_info_iter)?; // System program
|
next_account_info(account_info_iter)?; // System program
|
||||||
next_account_info(account_info_iter)?; // Token program
|
next_account_info(account_info_iter)?; // Token program
|
||||||
|
let clock_info = next_account_info(account_info_iter)?;
|
||||||
let sender_account_info = next_account_info(account_info_iter)?;
|
let sender_account_info = next_account_info(account_info_iter)?;
|
||||||
let bridge_info = next_account_info(account_info_iter)?;
|
let bridge_info = next_account_info(account_info_iter)?;
|
||||||
let transfer_info = next_account_info(account_info_iter)?;
|
let transfer_info = next_account_info(account_info_iter)?;
|
||||||
|
@ -241,6 +267,7 @@ impl Bridge {
|
||||||
let sender = Bridge::token_account_deserialize(sender_account_info)?;
|
let sender = Bridge::token_account_deserialize(sender_account_info)?;
|
||||||
let mint = Bridge::mint_deserialize(mint_info)?;
|
let mint = Bridge::mint_deserialize(mint_info)?;
|
||||||
let bridge = Bridge::bridge_deserialize(bridge_info)?;
|
let bridge = Bridge::bridge_deserialize(bridge_info)?;
|
||||||
|
let clock = Clock::from_account_info(clock_info)?;
|
||||||
|
|
||||||
// Does the token belong to the mint
|
// Does the token belong to the mint
|
||||||
if sender.mint != *mint_info.key {
|
if sender.mint != *mint_info.key {
|
||||||
|
@ -317,6 +344,7 @@ impl Bridge {
|
||||||
transfer.source_address = sender_account_info.key.to_bytes();
|
transfer.source_address = sender_account_info.key.to_bytes();
|
||||||
transfer.foreign_address = t.target;
|
transfer.foreign_address = t.target;
|
||||||
transfer.nonce = t.nonce;
|
transfer.nonce = t.nonce;
|
||||||
|
transfer.lockup_time = clock.unix_timestamp as u32;
|
||||||
|
|
||||||
// Don't use the user-given data as we don't check mint = AssetMeta.address
|
// Don't use the user-given data as we don't check mint = AssetMeta.address
|
||||||
transfer.asset = AssetMeta {
|
transfer.asset = AssetMeta {
|
||||||
|
|
|
@ -69,6 +69,10 @@ pub struct TransferOutProposal {
|
||||||
pub vaa: [u8; MAX_VAA_SIZE + 1],
|
pub vaa: [u8; MAX_VAA_SIZE + 1],
|
||||||
/// time the vaa was submitted
|
/// time the vaa was submitted
|
||||||
pub vaa_time: u32,
|
pub vaa_time: u32,
|
||||||
|
/// time the lockup was created
|
||||||
|
pub lockup_time: u32,
|
||||||
|
/// times the proposal has been poked
|
||||||
|
pub poke_counter: u8,
|
||||||
|
|
||||||
/// Is `true` if this structure has been initialized.
|
/// Is `true` if this structure has been initialized.
|
||||||
pub is_initialized: bool,
|
pub is_initialized: bool,
|
||||||
|
|
|
@ -85,6 +85,18 @@ fn command_deploy_bridge(
|
||||||
Ok(Some(transaction))
|
Ok(Some(transaction))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn command_poke_proposal(config: &Config, bridge: &Pubkey, proposal: &Pubkey) -> CommmandResult {
|
||||||
|
println!("Poking lockup");
|
||||||
|
|
||||||
|
let ix = poke_proposal(bridge, proposal)?;
|
||||||
|
let mut transaction = Transaction::new_with_payer(&[ix], Some(&config.fee_payer.pubkey()));
|
||||||
|
|
||||||
|
let (recent_blockhash, fee_calculator) = config.rpc_client.get_recent_blockhash()?;
|
||||||
|
check_fee_payer_balance(config, fee_calculator.calculate_fee(&transaction.message()))?;
|
||||||
|
transaction.sign(&[&config.fee_payer, &config.owner], recent_blockhash);
|
||||||
|
Ok(Some(transaction))
|
||||||
|
}
|
||||||
|
|
||||||
fn command_lock_tokens(
|
fn command_lock_tokens(
|
||||||
config: &Config,
|
config: &Config,
|
||||||
bridge: &Pubkey,
|
bridge: &Pubkey,
|
||||||
|
@ -954,6 +966,34 @@ fn main() {
|
||||||
.help("The vaa to be posted"),
|
.help("The vaa to be posted"),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
.subcommand(
|
||||||
|
SubCommand::with_name("poke")
|
||||||
|
.about("Poke a proposal so it's retried")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("bridge")
|
||||||
|
.long("bridge")
|
||||||
|
.value_name("BRIDGE_KEY")
|
||||||
|
.validator(is_pubkey_or_keypair)
|
||||||
|
.takes_value(true)
|
||||||
|
.index(1)
|
||||||
|
.required(true)
|
||||||
|
.help(
|
||||||
|
"Specify the bridge program public key"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("proposal")
|
||||||
|
.long("proposal")
|
||||||
|
.value_name("PROPOSAL_KEY")
|
||||||
|
.validator(is_pubkey_or_keypair)
|
||||||
|
.takes_value(true)
|
||||||
|
.index(2)
|
||||||
|
.required(true)
|
||||||
|
.help(
|
||||||
|
"Specify the transfer proposal to poke"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("wrapped-address")
|
SubCommand::with_name("wrapped-address")
|
||||||
.about("Derive wrapped asset address")
|
.about("Derive wrapped asset address")
|
||||||
|
@ -1114,6 +1154,11 @@ fn main() {
|
||||||
let vaa = hex::decode(vaa_string).unwrap();
|
let vaa = hex::decode(vaa_string).unwrap();
|
||||||
command_submit_vaa(&config, &bridge, vaa.as_slice())
|
command_submit_vaa(&config, &bridge, vaa.as_slice())
|
||||||
}
|
}
|
||||||
|
("poke", Some(arg_matches)) => {
|
||||||
|
let bridge = pubkey_of(arg_matches, "bridge").unwrap();
|
||||||
|
let proposal = pubkey_of(arg_matches, "proposal").unwrap();
|
||||||
|
command_poke_proposal(&config, &bridge, &proposal)
|
||||||
|
}
|
||||||
("wrapped-address", Some(arg_matches)) => {
|
("wrapped-address", Some(arg_matches)) => {
|
||||||
let bridge = pubkey_of(arg_matches, "bridge").unwrap();
|
let bridge = pubkey_of(arg_matches, "bridge").unwrap();
|
||||||
let chain = value_t_or_exit!(arg_matches, "chain", u8);
|
let chain = value_t_or_exit!(arg_matches, "chain", u8);
|
||||||
|
|
|
@ -9,7 +9,9 @@ import {WormholeFactory} from "../contracts/WormholeFactory";
|
||||||
import {BRIDGE_ADDRESS} from "../config";
|
import {BRIDGE_ADDRESS} from "../config";
|
||||||
import {keccak256} from "ethers/utils";
|
import {keccak256} from "ethers/utils";
|
||||||
import BN from 'bn.js';
|
import BN from 'bn.js';
|
||||||
import {PublicKey} from "@solana/web3.js";
|
import {PublicKey, Transaction} from "@solana/web3.js";
|
||||||
|
import KeyContext from "../providers/KeyContext";
|
||||||
|
import ClientContext from "../providers/ClientContext";
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
window.ethereum.enable();
|
window.ethereum.enable();
|
||||||
|
@ -32,6 +34,8 @@ function TransferProposals() {
|
||||||
let t = useContext(SolanaTokenContext);
|
let t = useContext(SolanaTokenContext);
|
||||||
let tokens = useContext(SolanaTokenContext);
|
let tokens = useContext(SolanaTokenContext);
|
||||||
let b = useContext(BridgeContext);
|
let b = useContext(BridgeContext);
|
||||||
|
let k = useContext(KeyContext);
|
||||||
|
let c = useContext(ClientContext);
|
||||||
|
|
||||||
let [lockups, setLockups] = useState<LockupWithStatus[]>([])
|
let [lockups, setLockups] = useState<LockupWithStatus[]>([])
|
||||||
|
|
||||||
|
@ -84,13 +88,31 @@ function TransferProposals() {
|
||||||
message.loading({content: "Waiting for transaction to be mined...", key: "eth_tx", duration: 1000})
|
message.loading({content: "Waiting for transaction to be mined...", key: "eth_tx", duration: 1000})
|
||||||
await tx.wait(1)
|
await tx.wait(1)
|
||||||
message.success({content: "Execution of VAA succeeded", key: "eth_tx"})
|
message.success({content: "Execution of VAA succeeded", key: "eth_tx"})
|
||||||
|
}
|
||||||
|
|
||||||
|
let pokeProposal = async (proposalAddress: PublicKey) => {
|
||||||
|
message.loading({content: "Poking lockup ...", key: "poke"}, 1000)
|
||||||
|
|
||||||
|
let ix = await b.createPokeProposalInstruction(proposalAddress);
|
||||||
|
let recentHash = await c.getRecentBlockhash();
|
||||||
|
let tx = new Transaction();
|
||||||
|
tx.recentBlockhash = recentHash.blockhash
|
||||||
|
tx.add(ix)
|
||||||
|
tx.sign(k)
|
||||||
|
try {
|
||||||
|
await c.sendTransaction(tx, [k])
|
||||||
|
message.success({content: "Poke succeeded", key: "poke"})
|
||||||
|
} catch (e) {
|
||||||
|
message.error({content: "Poke failed", key: "poke"})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let statusToPrompt = (v: LockupWithStatus) => {
|
let statusToPrompt = (v: LockupWithStatus) => {
|
||||||
switch (v.status) {
|
switch (v.status) {
|
||||||
case LockupStatus.AWAITING_VAA:
|
case LockupStatus.AWAITING_VAA:
|
||||||
return ("Awaiting VAA");
|
return (<>Awaiting VAA (<a onClick={() => {
|
||||||
|
pokeProposal(v.lockupAddress)
|
||||||
|
}}>poke</a>)</>);
|
||||||
case LockupStatus.UNCLAIMED_VAA:
|
case LockupStatus.UNCLAIMED_VAA:
|
||||||
return (<Button onClick={() => {
|
return (<Button onClick={() => {
|
||||||
executeVAA(v)
|
executeVAA(v)
|
||||||
|
|
|
@ -14,6 +14,7 @@ export interface AssetMeta {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Lockup {
|
export interface Lockup {
|
||||||
|
lockupAddress: PublicKey,
|
||||||
amount: BN,
|
amount: BN,
|
||||||
toChain: number,
|
toChain: number,
|
||||||
sourceAddress: PublicKey,
|
sourceAddress: PublicKey,
|
||||||
|
@ -24,6 +25,7 @@ export interface Lockup {
|
||||||
nonce: number,
|
nonce: number,
|
||||||
vaa: Uint8Array,
|
vaa: Uint8Array,
|
||||||
vaaTime: number,
|
vaaTime: number,
|
||||||
|
pokeCounter: number,
|
||||||
initialized: boolean,
|
initialized: boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,6 +95,7 @@ class SolanaBridge {
|
||||||
{pubkey: this.programID, isSigner: false, isWritable: false},
|
{pubkey: this.programID, isSigner: false, isWritable: false},
|
||||||
{pubkey: solanaWeb3.SystemProgram.programId, isSigner: false, isWritable: false},
|
{pubkey: solanaWeb3.SystemProgram.programId, isSigner: false, isWritable: false},
|
||||||
{pubkey: this.tokenProgram, isSigner: false, isWritable: false},
|
{pubkey: this.tokenProgram, isSigner: false, isWritable: false},
|
||||||
|
{pubkey: solanaWeb3.SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false},
|
||||||
{pubkey: tokenAccount, isSigner: false, isWritable: true},
|
{pubkey: tokenAccount, isSigner: false, isWritable: true},
|
||||||
{pubkey: configKey, isSigner: false, isWritable: false},
|
{pubkey: configKey, isSigner: false, isWritable: false},
|
||||||
|
|
||||||
|
@ -115,6 +118,30 @@ class SolanaBridge {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createPokeProposalInstruction(
|
||||||
|
proposalAccount: PublicKey,
|
||||||
|
): TransactionInstruction {
|
||||||
|
const dataLayout = BufferLayout.struct([BufferLayout.u8('instruction'),]);
|
||||||
|
|
||||||
|
const data = Buffer.alloc(dataLayout.span);
|
||||||
|
dataLayout.encode(
|
||||||
|
{
|
||||||
|
instruction: 5, // PokeProposal instruction
|
||||||
|
},
|
||||||
|
data,
|
||||||
|
);
|
||||||
|
|
||||||
|
const keys = [
|
||||||
|
{pubkey: proposalAccount, isSigner: false, isWritable: true},
|
||||||
|
];
|
||||||
|
|
||||||
|
return new TransactionInstruction({
|
||||||
|
keys,
|
||||||
|
programId: this.programID,
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// fetchAssetMeta fetches the AssetMeta for an SPL token
|
// fetchAssetMeta fetches the AssetMeta for an SPL token
|
||||||
async fetchAssetMeta(
|
async fetchAssetMeta(
|
||||||
mint: PublicKey,
|
mint: PublicKey,
|
||||||
|
@ -183,14 +210,15 @@ class SolanaBridge {
|
||||||
BufferLayout.blob(1001, 'vaa'),
|
BufferLayout.blob(1001, 'vaa'),
|
||||||
BufferLayout.seq(BufferLayout.u8(), 3), // 4 byte alignment because a u32 is following
|
BufferLayout.seq(BufferLayout.u8(), 3), // 4 byte alignment because a u32 is following
|
||||||
BufferLayout.u32('vaaTime'),
|
BufferLayout.u32('vaaTime'),
|
||||||
|
BufferLayout.u8('pokeCounter'),
|
||||||
BufferLayout.u8('initialized'),
|
BufferLayout.u8('initialized'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let accounts: Lockup[] = [];
|
let accounts: Lockup[] = [];
|
||||||
for (let acc of raw_accounts) {
|
for (let acc of raw_accounts) {
|
||||||
acc = acc.account;
|
let parsedAccount = dataLayout.decode(bs58.decode(acc.account.data))
|
||||||
let parsedAccount = dataLayout.decode(bs58.decode(acc.data))
|
|
||||||
accounts.push({
|
accounts.push({
|
||||||
|
lockupAddress: acc.pubkey,
|
||||||
amount: new BN(parsedAccount.amount, 2, "le"),
|
amount: new BN(parsedAccount.amount, 2, "le"),
|
||||||
assetAddress: parsedAccount.assetAddress,
|
assetAddress: parsedAccount.assetAddress,
|
||||||
assetChain: parsedAccount.assetChain,
|
assetChain: parsedAccount.assetChain,
|
||||||
|
@ -201,7 +229,8 @@ class SolanaBridge {
|
||||||
targetAddress: parsedAccount.targetAddress,
|
targetAddress: parsedAccount.targetAddress,
|
||||||
toChain: parsedAccount.toChain,
|
toChain: parsedAccount.toChain,
|
||||||
vaa: parsedAccount.vaa,
|
vaa: parsedAccount.vaa,
|
||||||
vaaTime: parsedAccount.vaaTime
|
vaaTime: parsedAccount.vaaTime,
|
||||||
|
pokeCounter: parsedAccount.pokeCounter
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue