mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-06-03 07:39:28 +00:00
feat(wallet): add bridge withdraw command
This commit is contained in:
parent
619b087d57
commit
b9d9c802e9
3
Cargo.lock
generated
3
Cargo.lock
generated
@ -4102,6 +4102,7 @@ dependencies = [
|
||||
"bytesize",
|
||||
"common",
|
||||
"faucet_core",
|
||||
"futures",
|
||||
"hex",
|
||||
"indexer_ffi",
|
||||
"indexer_service_protocol",
|
||||
@ -4110,6 +4111,7 @@ dependencies = [
|
||||
"log",
|
||||
"logos-blockchain-core",
|
||||
"logos-blockchain-http-api-common",
|
||||
"logos-blockchain-zone-sdk",
|
||||
"nssa",
|
||||
"nssa_core",
|
||||
"reqwest",
|
||||
@ -10682,6 +10684,7 @@ dependencies = [
|
||||
"base58",
|
||||
"bincode",
|
||||
"bip39",
|
||||
"bridge_core",
|
||||
"clap",
|
||||
"common",
|
||||
"derive_more",
|
||||
|
||||
@ -46,7 +46,7 @@ _wallet() {
|
||||
cword=$COMP_CWORD
|
||||
}
|
||||
|
||||
local commands="auth-transfer chain-info account pinata token amm ata check-health config restore-keys deploy-program help"
|
||||
local commands="auth-transfer chain-info account pinata token amm ata bridge check-health config restore-keys deploy-program help"
|
||||
|
||||
# Find the main command and subcommand by scanning words before the cursor.
|
||||
# Global options that take a value are skipped along with their argument.
|
||||
@ -535,6 +535,26 @@ _wallet() {
|
||||
esac
|
||||
;;
|
||||
|
||||
bridge)
|
||||
case "$subcmd" in
|
||||
"")
|
||||
COMPREPLY=($(compgen -W "withdraw help" -- "$cur"))
|
||||
;;
|
||||
withdraw)
|
||||
case "$prev" in
|
||||
--from)
|
||||
_wallet_complete_account_id "$cur"
|
||||
;;
|
||||
--amount | --bedrock-account-pk)
|
||||
;; # no specific completion
|
||||
*)
|
||||
COMPREPLY=($(compgen -W "--from --amount --bedrock-account-pk" -- "$cur"))
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
|
||||
config)
|
||||
case "$subcmd" in
|
||||
"")
|
||||
|
||||
@ -25,6 +25,7 @@ _wallet() {
|
||||
'token:Token program interaction subcommand'
|
||||
'amm:AMM program interaction subcommand'
|
||||
'ata:Associated Token Account program interaction subcommand'
|
||||
'bridge:Bridge program interaction subcommand'
|
||||
'check-health:Check the wallet can connect to the node and builtin local programs match the remote versions'
|
||||
'config:Command to setup config, get and set config fields'
|
||||
'restore-keys:Restoring keys from given password at given depth'
|
||||
@ -56,6 +57,9 @@ _wallet() {
|
||||
ata)
|
||||
_wallet_ata
|
||||
;;
|
||||
bridge)
|
||||
_wallet_bridge
|
||||
;;
|
||||
config)
|
||||
_wallet_config
|
||||
;;
|
||||
@ -442,6 +446,35 @@ _wallet_ata() {
|
||||
esac
|
||||
}
|
||||
|
||||
# bridge subcommand
|
||||
_wallet_bridge() {
|
||||
local -a subcommands
|
||||
|
||||
_arguments -C \
|
||||
'1: :->subcommand' \
|
||||
'*:: :->args'
|
||||
|
||||
case $state in
|
||||
subcommand)
|
||||
subcommands=(
|
||||
'withdraw:Withdraw native tokens through the bridge'
|
||||
'help:Print this message or the help of the given subcommand(s)'
|
||||
)
|
||||
_describe -t subcommands 'bridge subcommands' subcommands
|
||||
;;
|
||||
args)
|
||||
case $line[1] in
|
||||
withdraw)
|
||||
_arguments \
|
||||
'--from[Sender account with privacy prefix]:from:_wallet_account_ids' \
|
||||
'--amount[Amount of native tokens to withdraw]:amount:' \
|
||||
'--bedrock-account-pk[Bedrock account public key (32-byte hex)]:bedrock_pk:'
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# config subcommand
|
||||
_wallet_config() {
|
||||
local -a subcommands
|
||||
@ -515,6 +548,7 @@ _wallet_help() {
|
||||
'token:Token program interaction subcommand'
|
||||
'amm:AMM program interaction subcommand'
|
||||
'ata:Associated Token Account program interaction subcommand'
|
||||
'bridge:Bridge program interaction subcommand'
|
||||
'check-health:Check the wallet can connect to the node'
|
||||
'config:Command to setup config, get and set config fields'
|
||||
'restore-keys:Restoring keys from given password at given depth'
|
||||
|
||||
@ -32,6 +32,7 @@ indexer_service_protocol.workspace = true
|
||||
anyhow.workspace = true
|
||||
log.workspace = true
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
|
||||
futures.workspace = true
|
||||
hex.workspace = true
|
||||
tempfile.workspace = true
|
||||
bytesize.workspace = true
|
||||
@ -39,3 +40,4 @@ reqwest.workspace = true
|
||||
borsh.workspace = true
|
||||
logos-blockchain-http-api-common.workspace = true
|
||||
logos-blockchain-core.workspace = true
|
||||
logos-blockchain-zone-sdk.workspace = true
|
||||
|
||||
@ -9,7 +9,8 @@ use std::time::Duration;
|
||||
use anyhow::Context as _;
|
||||
use borsh::BorshSerialize;
|
||||
use common::transaction::NSSATransaction;
|
||||
use integration_tests::{TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext};
|
||||
use futures::StreamExt as _;
|
||||
use integration_tests::{TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, public_mention};
|
||||
use log::info;
|
||||
use logos_blockchain_core::mantle::{ledger::Inputs, ops::channel::deposit::DepositOp};
|
||||
use logos_blockchain_http_api_common::bodies::{
|
||||
@ -19,6 +20,9 @@ use logos_blockchain_http_api_common::bodies::{
|
||||
transfer_funds::{WalletTransferFundsRequestBody, WalletTransferFundsResponseBody},
|
||||
},
|
||||
};
|
||||
use logos_blockchain_zone_sdk::{
|
||||
CommonHttpClient, ZoneMessage, adapter::NodeHttpClient, indexer::ZoneIndexer,
|
||||
};
|
||||
use nssa::{
|
||||
AccountId, execute_and_prove, privacy_preserving_transaction, program::Program,
|
||||
public_transaction,
|
||||
@ -26,6 +30,7 @@ use nssa::{
|
||||
use nssa_core::{InputAccountIdentity, account::AccountWithMetadata};
|
||||
use sequencer_service_rpc::RpcClient as _;
|
||||
use tokio::test;
|
||||
use wallet::cli::{Command, execute_subcommand, programs::bridge::BridgeSubcommand};
|
||||
|
||||
const TIME_TO_FINALIZE_DEPOSIT_EVENT_ON_BEDROCK: Duration = Duration::from_mins(2);
|
||||
|
||||
@ -194,6 +199,7 @@ async fn private_bridge_deposit_invocation_is_dropped() -> anyhow::Result<()> {
|
||||
|
||||
async fn submit_bedrock_deposit(
|
||||
bedrock_addr: std::net::SocketAddr,
|
||||
bedrock_account_pk: &str,
|
||||
recipient_id: AccountId,
|
||||
amount: u64,
|
||||
) -> anyhow::Result<()> {
|
||||
@ -206,15 +212,13 @@ async fn submit_bedrock_deposit(
|
||||
let metadata = borsh::to_vec(&DepositMetadata { recipient_id })
|
||||
.context("Failed to encode deposit metadata")?;
|
||||
|
||||
let funding_key = "2e03b2eff5a45478e7e79668d2a146cf2c5c7925bce927f2b1c67f2ab4fc0d26";
|
||||
|
||||
let channel_id = integration_tests::config::bedrock_channel_id();
|
||||
let client = reqwest::Client::new();
|
||||
|
||||
let query_balance = || async {
|
||||
let balance_response = client
|
||||
.get(format!(
|
||||
"http://{bedrock_addr}/wallet/{funding_key}/balance"
|
||||
"http://{bedrock_addr}/wallet/{bedrock_account_pk}/balance"
|
||||
))
|
||||
.send()
|
||||
.await
|
||||
@ -231,13 +235,13 @@ async fn submit_bedrock_deposit(
|
||||
let mut balance = query_balance().await?;
|
||||
|
||||
info!(
|
||||
"Queried Bedrock balance for key {funding_key}: {:?}",
|
||||
"Queried Bedrock balance for key {bedrock_account_pk}: {:?}",
|
||||
balance.balance
|
||||
);
|
||||
|
||||
if balance.balance < amount {
|
||||
anyhow::bail!(
|
||||
"Bedrock wallet with key {funding_key} has insufficient balance {:?} for deposit amount {:?}",
|
||||
"Bedrock wallet with key {bedrock_account_pk} has insufficient balance {:?} for deposit amount {:?}",
|
||||
balance.balance,
|
||||
amount
|
||||
);
|
||||
@ -363,11 +367,18 @@ async fn wait_for_vault_balance(
|
||||
})?
|
||||
}
|
||||
|
||||
/// Test deposit and withdraw round trip.
|
||||
///
|
||||
/// Implemented as one test instead of two separate tests for deposit and withdraw, because the
|
||||
/// withdraw test depends on the deposit to set up the necessary state (funds in vault) for testing
|
||||
/// withdraw functionality.
|
||||
#[test]
|
||||
async fn bedrock_deposit_mints_to_vault_then_claim_succeeds() -> anyhow::Result<()> {
|
||||
let ctx = TestContext::new().await?;
|
||||
async fn bedrock_deposit_claim_and_withdraw_round_trip_succeeds() -> anyhow::Result<()> {
|
||||
let mut ctx = TestContext::new().await?;
|
||||
|
||||
let bedrock_account_pk = "2e03b2eff5a45478e7e79668d2a146cf2c5c7925bce927f2b1c67f2ab4fc0d26";
|
||||
let recipient_id = ctx.existing_public_accounts()[0];
|
||||
let amount = 1_u64;
|
||||
let vault_program_id = Program::vault().id();
|
||||
let recipient_vault_id = vault_core::compute_vault_account_id(vault_program_id, recipient_id);
|
||||
|
||||
@ -381,10 +392,17 @@ async fn bedrock_deposit_mints_to_vault_then_claim_succeeds() -> anyhow::Result<
|
||||
.await?;
|
||||
|
||||
// Submit deposit to Bedrock
|
||||
submit_bedrock_deposit(ctx.bedrock_addr(), recipient_id, 1).await?;
|
||||
submit_bedrock_deposit(ctx.bedrock_addr(), bedrock_account_pk, recipient_id, amount)
|
||||
.await
|
||||
.context("Failed to submit Bedrock deposit for round-trip setup")?;
|
||||
|
||||
// Wait for vault to receive the deposit (minted from bridge to vault)
|
||||
wait_for_vault_balance(&ctx, recipient_vault_id, vault_balance_before + 1).await?;
|
||||
wait_for_vault_balance(
|
||||
&ctx,
|
||||
recipient_vault_id,
|
||||
vault_balance_before + u128::from(amount),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Now claim funds from vault back to recipient
|
||||
let nonces = ctx
|
||||
@ -404,7 +422,9 @@ async fn bedrock_deposit_mints_to_vault_then_claim_succeeds() -> anyhow::Result<
|
||||
vault_program_id,
|
||||
vec![recipient_id, recipient_vault_id],
|
||||
nonces,
|
||||
vault_core::Instruction::Claim { amount: 1 },
|
||||
vault_core::Instruction::Claim {
|
||||
amount: u128::from(amount),
|
||||
},
|
||||
)
|
||||
.context("Failed to build vault claim message")?;
|
||||
|
||||
@ -439,9 +459,88 @@ async fn bedrock_deposit_mints_to_vault_then_claim_succeeds() -> anyhow::Result<
|
||||
);
|
||||
assert_eq!(
|
||||
recipient_balance_after_claim,
|
||||
recipient_balance_before + 1,
|
||||
recipient_balance_before + u128::from(amount),
|
||||
"Recipient balance should increase by claimed amount"
|
||||
);
|
||||
|
||||
// Withdraw back to Bedrock and wait for finalized withdraw event.
|
||||
let sender_id = recipient_id;
|
||||
|
||||
let observer = create_zone_indexer_observer(ctx.bedrock_addr())?;
|
||||
let observe_fut = wait_for_finalized_withdraw_op(&observer, amount);
|
||||
|
||||
let withdraw_fut = execute_subcommand(
|
||||
ctx.wallet_mut(),
|
||||
Command::Bridge(BridgeSubcommand::Withdraw {
|
||||
from: public_mention(sender_id),
|
||||
amount,
|
||||
bedrock_account_pk: bedrock_account_pk.to_owned(),
|
||||
}),
|
||||
);
|
||||
|
||||
let (observe_result, withdraw_result) = tokio::join!(observe_fut, withdraw_fut);
|
||||
|
||||
withdraw_result.context("Failed to execute wallet bridge withdraw command")?;
|
||||
|
||||
observe_result
|
||||
.context("Failed while waiting for finalized withdraw event from zone indexer")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_zone_indexer_observer(
|
||||
bedrock_addr: std::net::SocketAddr,
|
||||
) -> anyhow::Result<ZoneIndexer<NodeHttpClient>> {
|
||||
let bedrock_url = integration_tests::config::addr_to_url(
|
||||
integration_tests::config::UrlProtocol::Http,
|
||||
bedrock_addr,
|
||||
)
|
||||
.context("Failed to convert Bedrock addr to URL for zone indexer observer")?;
|
||||
|
||||
let node = NodeHttpClient::new(CommonHttpClient::new(None), bedrock_url);
|
||||
|
||||
Ok(ZoneIndexer::new(
|
||||
integration_tests::config::bedrock_channel_id(),
|
||||
node,
|
||||
))
|
||||
}
|
||||
|
||||
async fn wait_for_finalized_withdraw_op(
|
||||
observer: &ZoneIndexer<NodeHttpClient>,
|
||||
expected_amount: u64,
|
||||
) -> anyhow::Result<()> {
|
||||
let timeout = TIME_TO_FINALIZE_DEPOSIT_EVENT_ON_BEDROCK
|
||||
+ Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS);
|
||||
|
||||
tokio::time::timeout(timeout, async {
|
||||
loop {
|
||||
let stream = observer
|
||||
.follow()
|
||||
.await
|
||||
.context("Failed to read zone indexer message batch")?;
|
||||
let mut stream = std::pin::pin!(stream);
|
||||
|
||||
while let Some(message) = stream.next().await {
|
||||
info!("Observed zone message {message:?}");
|
||||
|
||||
let ZoneMessage::Withdraw(withdraw) = message else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let amount = withdraw.outputs.amount().context(
|
||||
"Failed to compute finalized withdraw amount from zone indexer message",
|
||||
)?;
|
||||
|
||||
if amount == expected_amount {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
tokio::time::sleep(Duration::from_millis(500)).await;
|
||||
}
|
||||
})
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!("Timed out waiting for finalized withdraw message with amount {expected_amount}")
|
||||
})?
|
||||
}
|
||||
|
||||
@ -2,8 +2,8 @@ use std::{pin::Pin, sync::Arc, time::Duration};
|
||||
|
||||
use anyhow::{Context as _, Result};
|
||||
use common::block::Block;
|
||||
use log::warn;
|
||||
use logos_blockchain_core::mantle::{Note, ledger::Outputs};
|
||||
use log::{info, warn};
|
||||
use logos_blockchain_core::mantle::{Note, ledger::Outputs, ops::channel::inscribe::Inscription};
|
||||
pub use logos_blockchain_key_management_system_service::keys::{Ed25519Key, ZkKey};
|
||||
pub use logos_blockchain_zone_sdk::sequencer::SequencerCheckpoint;
|
||||
use logos_blockchain_zone_sdk::{
|
||||
@ -135,19 +135,23 @@ impl BlockPublisherTrait for ZoneSdkPublisher {
|
||||
bridge_withdrawals: Vec<BridgeWithdrawData>,
|
||||
) -> Result<()> {
|
||||
let data = borsh::to_vec(block).context("Failed to serialize block")?;
|
||||
let data_bounded = data
|
||||
let data_bounded: Inscription = data
|
||||
.try_into()
|
||||
.context("Block data exceeds maximum allowed size")?;
|
||||
let data_byte_size = data_bounded.len();
|
||||
|
||||
if bridge_withdrawals.is_empty() {
|
||||
self.handle
|
||||
.publish_message(data_bounded)
|
||||
.await
|
||||
.context("Failed to publish block")?;
|
||||
|
||||
info!("Published block with the size of {data_byte_size} bytes");
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let withdraws = bridge_withdrawals
|
||||
let withdraws: Vec<_> = bridge_withdrawals
|
||||
.into_iter()
|
||||
.map(|withdrawal| {
|
||||
let recipient_pk =
|
||||
@ -161,10 +165,16 @@ impl BlockPublisherTrait for ZoneSdkPublisher {
|
||||
})
|
||||
.collect();
|
||||
|
||||
let withdraw_count = withdraws.len();
|
||||
self.handle
|
||||
.publish_atomic_withdraw(data_bounded, withdraws)
|
||||
.await
|
||||
.context("Failed to publish block with withdrawals")?;
|
||||
|
||||
info!(
|
||||
"Published block with the size of {data_byte_size} bytes and {withdraw_count} bridge withdrawals",
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,16 +14,17 @@ common.workspace = true
|
||||
authenticated_transfer_core.workspace = true
|
||||
key_protocol.workspace = true
|
||||
sequencer_service_rpc = { workspace = true, features = ["client"] }
|
||||
token_core.workspace = true
|
||||
amm_core.workspace = true
|
||||
testnet_initial_state.workspace = true
|
||||
token_core.workspace = true
|
||||
ata_core.workspace = true
|
||||
bridge_core.workspace = true
|
||||
keycard_wallet.workspace = true
|
||||
|
||||
bip39.workspace = true
|
||||
pyo3.workspace = true
|
||||
rpassword = "7"
|
||||
zeroize = "1"
|
||||
keycard_wallet.workspace = true
|
||||
|
||||
anyhow.workspace = true
|
||||
thiserror.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
||||
@ -20,7 +20,7 @@ use crate::{
|
||||
group::GroupSubcommand,
|
||||
keycard::KeycardSubcommand,
|
||||
programs::{
|
||||
amm::AmmProgramAgnosticSubcommand, ata::AtaSubcommand,
|
||||
amm::AmmProgramAgnosticSubcommand, ata::AtaSubcommand, bridge::BridgeSubcommand,
|
||||
native_token_transfer::AuthTransferSubcommand, pinata::PinataProgramAgnosticSubcommand,
|
||||
token::TokenProgramAgnosticSubcommand,
|
||||
},
|
||||
@ -65,6 +65,9 @@ pub enum Command {
|
||||
/// Associated Token Account program interaction subcommand.
|
||||
#[command(subcommand)]
|
||||
Ata(AtaSubcommand),
|
||||
/// Bridge program interaction subcommand.
|
||||
#[command(subcommand)]
|
||||
Bridge(BridgeSubcommand),
|
||||
/// Group key management (create, invite, join, derive keys).
|
||||
#[command(subcommand)]
|
||||
Group(GroupSubcommand),
|
||||
@ -233,6 +236,9 @@ pub async fn execute_subcommand(
|
||||
Command::Token(token_subcommand) => token_subcommand.handle_subcommand(wallet_core).await?,
|
||||
Command::AMM(amm_subcommand) => amm_subcommand.handle_subcommand(wallet_core).await?,
|
||||
Command::Ata(ata_subcommand) => ata_subcommand.handle_subcommand(wallet_core).await?,
|
||||
Command::Bridge(bridge_subcommand) => {
|
||||
bridge_subcommand.handle_subcommand(wallet_core).await?
|
||||
}
|
||||
Command::Group(group_subcommand) => group_subcommand.handle_subcommand(wallet_core).await?,
|
||||
Command::Keycard(keycard_subcommand) => {
|
||||
keycard_subcommand.handle_subcommand(wallet_core).await?
|
||||
|
||||
64
wallet/src/cli/programs/bridge.rs
Normal file
64
wallet/src/cli/programs/bridge.rs
Normal file
@ -0,0 +1,64 @@
|
||||
use anyhow::{Context as _, Result};
|
||||
use clap::Subcommand;
|
||||
|
||||
use crate::{
|
||||
WalletCore,
|
||||
account::AccountIdWithPrivacy,
|
||||
cli::{CliAccountMention, SubcommandReturnValue, WalletSubcommand},
|
||||
program_facades::bridge::Bridge,
|
||||
};
|
||||
|
||||
/// Represents generic CLI subcommand for a wallet working with bridge program.
|
||||
#[derive(Subcommand, Debug, Clone)]
|
||||
pub enum BridgeSubcommand {
|
||||
/// Withdraw native tokens from a public account to Bedrock through the bridge.
|
||||
Withdraw {
|
||||
/// Sender account mention - account id with privacy prefix or a label.
|
||||
#[arg(long)]
|
||||
from: CliAccountMention,
|
||||
/// Amount of native tokens to withdraw.
|
||||
#[arg(long)]
|
||||
amount: u64,
|
||||
/// Bedrock account public key encoded as a 32-byte hex string.
|
||||
#[arg(long)]
|
||||
bedrock_account_pk: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl WalletSubcommand for BridgeSubcommand {
|
||||
async fn handle_subcommand(
|
||||
self,
|
||||
wallet_core: &mut WalletCore,
|
||||
) -> Result<SubcommandReturnValue> {
|
||||
match self {
|
||||
Self::Withdraw {
|
||||
from,
|
||||
amount,
|
||||
bedrock_account_pk,
|
||||
} => {
|
||||
let from = from.resolve(wallet_core.storage())?;
|
||||
let AccountIdWithPrivacy::Public(sender_account_id) = from else {
|
||||
anyhow::bail!("Bridge withdraw supports only public sender accounts");
|
||||
};
|
||||
|
||||
let bedrock_account_pk = parse_bedrock_account_pk(&bedrock_account_pk)?;
|
||||
|
||||
let tx_hash = Bridge(wallet_core)
|
||||
.send_withdraw(sender_account_id, amount, bedrock_account_pk)
|
||||
.await?;
|
||||
|
||||
println!("Transaction hash is {tx_hash}");
|
||||
|
||||
Ok(SubcommandReturnValue::Empty)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_bedrock_account_pk(raw: &str) -> Result<[u8; 32]> {
|
||||
let raw = raw.strip_prefix("0x").unwrap_or(raw);
|
||||
let mut bedrock_account_pk = [0_u8; 32];
|
||||
hex::decode_to_slice(raw, &mut bedrock_account_pk)
|
||||
.context("Invalid `bedrock-account-pk`: expected hex string of 32 bytes")?;
|
||||
Ok(bedrock_account_pk)
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
pub mod amm;
|
||||
pub mod ata;
|
||||
pub mod bridge;
|
||||
pub mod native_token_transfer;
|
||||
pub mod pinata;
|
||||
pub mod token;
|
||||
|
||||
35
wallet/src/program_facades/bridge.rs
Normal file
35
wallet/src/program_facades/bridge.rs
Normal file
@ -0,0 +1,35 @@
|
||||
use common::HashType;
|
||||
use nssa::{AccountId, program::Program};
|
||||
|
||||
use crate::{AccountIdentity, ExecutionFailureKind, WalletCore};
|
||||
|
||||
pub struct Bridge<'wallet>(pub &'wallet WalletCore);
|
||||
|
||||
impl Bridge<'_> {
|
||||
pub async fn send_withdraw(
|
||||
&self,
|
||||
sender_account_id: AccountId,
|
||||
amount: u64,
|
||||
bedrock_account_pk: [u8; 32],
|
||||
) -> Result<HashType, ExecutionFailureKind> {
|
||||
let program = Program::bridge();
|
||||
let bridge_account_id = nssa::system_bridge_account_id();
|
||||
let instruction = bridge_core::Instruction::Withdraw {
|
||||
amount,
|
||||
bedrock_account_pk,
|
||||
};
|
||||
let instruction_data =
|
||||
Program::serialize_instruction(instruction).expect("Instruction should serialize");
|
||||
|
||||
self.0
|
||||
.send_pub_tx(
|
||||
vec![
|
||||
AccountIdentity::Public(sender_account_id),
|
||||
AccountIdentity::PublicNoSign(bridge_account_id),
|
||||
],
|
||||
instruction_data,
|
||||
&program.into(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
@ -3,6 +3,7 @@
|
||||
|
||||
pub mod amm;
|
||||
pub mod ata;
|
||||
pub mod bridge;
|
||||
pub mod native_token_transfer;
|
||||
pub mod pinata;
|
||||
pub mod token;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user