Fix bridge fee and implement CLI
Change-Id: Ib17b335e05359fd4baf614d0b4eaae459814b04d
This commit is contained in:
parent
d677311d70
commit
ece0de4bef
|
@ -1,4 +1,5 @@
|
|||
target
|
||||
bin
|
||||
cli/target
|
||||
agent/target
|
||||
bridge/target
|
||||
solitaire/target
|
||||
modules/token_bridge/target
|
|
@ -34,7 +34,7 @@ dependencies = [
|
|||
"bridge",
|
||||
"bs58",
|
||||
"byteorder",
|
||||
"clap 2.33.3",
|
||||
"clap",
|
||||
"futures 0.3.15",
|
||||
"hex",
|
||||
"libc",
|
||||
|
@ -463,44 +463,12 @@ dependencies = [
|
|||
"ansi_term",
|
||||
"atty",
|
||||
"bitflags",
|
||||
"strsim 0.8.0",
|
||||
"textwrap 0.11.0",
|
||||
"strsim",
|
||||
"textwrap",
|
||||
"unicode-width",
|
||||
"vec_map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.0.0-beta.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bd1061998a501ee7d4b6d449020df3266ca3124b941ec56cf2005c3779ca142"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
"clap_derive",
|
||||
"indexmap",
|
||||
"lazy_static",
|
||||
"os_str_bytes",
|
||||
"strsim 0.10.0",
|
||||
"termcolor",
|
||||
"textwrap 0.12.1",
|
||||
"unicode-width",
|
||||
"vec_map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "3.0.0-beta.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "370f715b81112975b1b69db93e0b56ea4cd4e5002ac43b2da8474106a54096a1"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
"proc-macro2 1.0.27",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.73",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "client"
|
||||
version = "0.1.0"
|
||||
|
@ -508,9 +476,12 @@ dependencies = [
|
|||
"anyhow",
|
||||
"borsh",
|
||||
"bridge",
|
||||
"clap 3.0.0-beta.2",
|
||||
"clap",
|
||||
"hex",
|
||||
"rand 0.7.3",
|
||||
"shellexpand",
|
||||
"solana-clap-utils",
|
||||
"solana-cli-config",
|
||||
"solana-client",
|
||||
"solana-program",
|
||||
"solana-sdk",
|
||||
|
@ -2024,12 +1995,6 @@ dependencies = [
|
|||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "afb2e1c3ee07430c2cf76151675e583e0f19985fa6efae47d6848a3e2c824f85"
|
||||
|
||||
[[package]]
|
||||
name = "ouroboros"
|
||||
version = "0.5.1"
|
||||
|
@ -2282,30 +2247,6 @@ dependencies = [
|
|||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2 1.0.27",
|
||||
"quote 1.0.9",
|
||||
"syn 1.0.73",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.27",
|
||||
"quote 1.0.9",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.19"
|
||||
|
@ -3026,7 +2967,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "484288242b2b175bf2b7554497318e39b23ee921989f976387dbe48e60b2f256"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap 2.33.3",
|
||||
"clap",
|
||||
"rpassword",
|
||||
"solana-remote-wallet",
|
||||
"solana-sdk",
|
||||
|
@ -3059,7 +3000,7 @@ dependencies = [
|
|||
"base64 0.13.0",
|
||||
"bincode",
|
||||
"bs58",
|
||||
"clap 2.33.3",
|
||||
"clap",
|
||||
"indicatif",
|
||||
"jsonrpc-core",
|
||||
"log",
|
||||
|
@ -3132,7 +3073,7 @@ checksum = "ed5e6adf551ca4e761c3395bb684ba5d907a051007a6dbf2d57cb99d2691e031"
|
|||
dependencies = [
|
||||
"bincode",
|
||||
"byteorder",
|
||||
"clap 2.33.3",
|
||||
"clap",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
|
@ -3222,7 +3163,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "fa68e25fb6452b85733cf5c301988b56fd2d5d5a8e93c75cf38cbec06efc2eae"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"clap 2.33.3",
|
||||
"clap",
|
||||
"log",
|
||||
"nix",
|
||||
"rand 0.7.3",
|
||||
|
@ -3591,12 +3532,6 @@ version = "0.8.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "1.0.0"
|
||||
|
@ -3711,15 +3646,6 @@ dependencies = [
|
|||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "203008d98caf094106cfaba70acfed15e18ed3ddb7d94e49baec153a2b462789"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.25"
|
||||
|
|
|
@ -8,11 +8,14 @@ edition = "2018"
|
|||
anyhow = "1.0.40"
|
||||
borsh = "0.8.1"
|
||||
bridge = { path = "../program", features = ["no-idl", "no-entrypoint", "client"] }
|
||||
clap = "3.0.0-beta.2"
|
||||
clap = "2.33.0"
|
||||
rand = "0.7.3"
|
||||
shellexpand = "2.1.0"
|
||||
solana-client = "=1.7.0"
|
||||
solana-program = "=1.7.0"
|
||||
solana-sdk = "=1.7.0"
|
||||
solana-cli-config = "=1.7.0"
|
||||
solitaire = { path = "../../solitaire/program" }
|
||||
solitaire-client = { path = "../../solitaire/client" }
|
||||
solana-clap-utils = "=1.7.0"
|
||||
hex = "0.4.3"
|
|
@ -1,97 +1,336 @@
|
|||
use borsh::BorshSerialize;
|
||||
use bridge::{
|
||||
api,
|
||||
types,
|
||||
#![feature(const_generics)]
|
||||
#![allow(warnings)]
|
||||
|
||||
use std::{
|
||||
fmt::Display,
|
||||
mem::size_of,
|
||||
process::exit,
|
||||
};
|
||||
|
||||
use borsh::BorshDeserialize;
|
||||
use bridge::{
|
||||
accounts::{
|
||||
Bridge,
|
||||
FeeCollector,
|
||||
},
|
||||
types::BridgeData,
|
||||
};
|
||||
use clap::{
|
||||
crate_description,
|
||||
crate_name,
|
||||
crate_version,
|
||||
value_t,
|
||||
App,
|
||||
AppSettings,
|
||||
Arg,
|
||||
SubCommand,
|
||||
};
|
||||
use hex;
|
||||
use solana_clap_utils::{
|
||||
input_parsers::{
|
||||
keypair_of,
|
||||
pubkey_of,
|
||||
value_of,
|
||||
},
|
||||
input_validators::{
|
||||
is_keypair,
|
||||
is_pubkey_or_keypair,
|
||||
is_url,
|
||||
},
|
||||
};
|
||||
use clap::Clap;
|
||||
use solana_client::{
|
||||
rpc_client::RpcClient,
|
||||
rpc_config::RpcSendTransactionConfig,
|
||||
};
|
||||
use solana_program::pubkey::Pubkey;
|
||||
use solana_sdk::{
|
||||
commitment_config::CommitmentConfig,
|
||||
native_token::*,
|
||||
program_error::ProgramError::AccountAlreadyInitialized,
|
||||
pubkey::Pubkey,
|
||||
signature::{
|
||||
read_keypair_file,
|
||||
Signer as SolSigner,
|
||||
Keypair,
|
||||
Signer,
|
||||
},
|
||||
system_instruction::transfer,
|
||||
transaction::Transaction,
|
||||
};
|
||||
use solitaire_client::{
|
||||
AccEntry,
|
||||
ToInstruction,
|
||||
};
|
||||
|
||||
use bridge::accounts::{
|
||||
GuardianSet,
|
||||
GuardianSetDerivationData,
|
||||
};
|
||||
use solitaire::{
|
||||
processors::seeded::Seeded,
|
||||
AccountState,
|
||||
};
|
||||
use std::error;
|
||||
|
||||
#[derive(Clap)]
|
||||
pub struct Opts {
|
||||
#[clap(long)]
|
||||
bridge_address: Pubkey,
|
||||
struct Config {
|
||||
rpc_client: RpcClient,
|
||||
owner: Keypair,
|
||||
fee_payer: Keypair,
|
||||
commitment_config: CommitmentConfig,
|
||||
}
|
||||
|
||||
pub type ErrBox = Box<dyn error::Error>;
|
||||
type Error = Box<dyn std::error::Error>;
|
||||
type CommmandResult = Result<Option<Transaction>, Error>;
|
||||
|
||||
pub const DEFAULT_MESSAGE_FEE: u64 = 42;
|
||||
pub const DEFAULT_GUARDIAN_SET_EXPIRATION_TIME: u32 = 42;
|
||||
fn command_deploy_bridge(
|
||||
config: &Config,
|
||||
bridge: &Pubkey,
|
||||
_initial_guardian: Vec<[u8; 20]>,
|
||||
guardian_expiration: u32,
|
||||
message_fee: u64,
|
||||
) -> CommmandResult {
|
||||
println!("Initializing Wormhole bridge {}", bridge);
|
||||
|
||||
fn main() -> Result<(), ErrBox> {
|
||||
let opts = Opts::parse();
|
||||
let minimum_balance_for_rent_exemption = config
|
||||
.rpc_client
|
||||
.get_minimum_balance_for_rent_exemption(size_of::<BridgeData>())?;
|
||||
|
||||
let payer = read_keypair_file(&*shellexpand::tilde("~/.config/solana/id.json"))
|
||||
.expect("Example requires a keypair file");
|
||||
let ix = bridge::client_instructions::initialize(
|
||||
*bridge,
|
||||
config.owner.pubkey(),
|
||||
message_fee,
|
||||
guardian_expiration,
|
||||
)
|
||||
.unwrap();
|
||||
println!("config account: {}, ", ix.accounts[0].pubkey.to_string());
|
||||
let mut transaction = Transaction::new_with_payer(&[ix], Some(&config.fee_payer.pubkey()));
|
||||
|
||||
// Keypair is not Clone
|
||||
let payer_for_tx = read_keypair_file(&*shellexpand::tilde("~/.config/solana/id.json"))
|
||||
.expect("Example requires a keypair file");
|
||||
let url = "http://localhost:8899".to_owned();
|
||||
let (recent_blockhash, fee_calculator) = config.rpc_client.get_recent_blockhash()?;
|
||||
check_fee_payer_balance(
|
||||
config,
|
||||
minimum_balance_for_rent_exemption + fee_calculator.calculate_fee(&transaction.message()),
|
||||
)?;
|
||||
transaction.sign(&[&config.fee_payer, &config.owner], recent_blockhash);
|
||||
Ok(Some(transaction))
|
||||
}
|
||||
|
||||
let client = RpcClient::new(url);
|
||||
fn command_post_message(
|
||||
config: &Config,
|
||||
bridge: &Pubkey,
|
||||
nonce: u32,
|
||||
payload: Vec<u8>,
|
||||
) -> CommmandResult {
|
||||
println!("Posting a message to the wormhole");
|
||||
|
||||
let program_id = opts.bridge_address;
|
||||
// Fetch the message fee
|
||||
let bridge_config_account = config
|
||||
.rpc_client
|
||||
.get_account(&Bridge::<'_, { AccountState::Initialized }>::key(
|
||||
None, bridge,
|
||||
))?;
|
||||
let bridge_config = BridgeData::try_from_slice(bridge_config_account.data.as_slice())?;
|
||||
println!("Message fee: {} lamports", bridge_config.config.fee);
|
||||
|
||||
use AccEntry::*;
|
||||
let init = api::InitializeAccounts {
|
||||
bridge: Derived(program_id.clone()),
|
||||
guardian_set: Unprivileged(<GuardianSet<'_, { AccountState::Uninitialized }>>::key(
|
||||
&GuardianSetDerivationData { index: 0 },
|
||||
&program_id,
|
||||
)),
|
||||
payer: Signer(payer),
|
||||
let transfer_ix = transfer(
|
||||
&config.owner.pubkey(),
|
||||
&FeeCollector::key(None, bridge),
|
||||
bridge_config.config.fee,
|
||||
);
|
||||
let ix = bridge::client_instructions::post_message(
|
||||
*bridge,
|
||||
config.owner.pubkey(),
|
||||
config.fee_payer.pubkey(),
|
||||
nonce,
|
||||
payload,
|
||||
)
|
||||
.unwrap();
|
||||
let mut transaction =
|
||||
Transaction::new_with_payer(&[transfer_ix, 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 main() {
|
||||
let matches = App::new(crate_name!())
|
||||
.about(crate_description!())
|
||||
.version(crate_version!())
|
||||
.setting(AppSettings::SubcommandRequiredElseHelp)
|
||||
.arg({
|
||||
let arg = Arg::with_name("config_file")
|
||||
.short("C")
|
||||
.long("config")
|
||||
.value_name("PATH")
|
||||
.takes_value(true)
|
||||
.global(true)
|
||||
.help("Configuration file to use");
|
||||
if let Some(ref config_file) = *solana_cli_config::CONFIG_FILE {
|
||||
arg.default_value(&config_file)
|
||||
} else {
|
||||
arg
|
||||
}
|
||||
})
|
||||
.arg(
|
||||
Arg::with_name("json_rpc_url")
|
||||
.long("url")
|
||||
.value_name("URL")
|
||||
.takes_value(true)
|
||||
.validator(is_url)
|
||||
.help("JSON RPC URL for the cluster. Default from the configuration file."),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("owner")
|
||||
.long("owner")
|
||||
.value_name("KEYPAIR")
|
||||
.validator(is_keypair)
|
||||
.takes_value(true)
|
||||
.help(
|
||||
"Specify the contract payer account. \
|
||||
This may be a keypair file, the ASK keyword. \
|
||||
Defaults to the client keypair.",
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("fee_payer")
|
||||
.long("fee-payer")
|
||||
.value_name("KEYPAIR")
|
||||
.validator(is_keypair)
|
||||
.takes_value(true)
|
||||
.help(
|
||||
"Specify the fee-payer account. \
|
||||
This may be a keypair file, the ASK keyword. \
|
||||
Defaults to the client keypair.",
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("create-bridge")
|
||||
.about("Create a new bridge")
|
||||
.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 address"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("guardian")
|
||||
.validator(is_hex)
|
||||
.value_name("GUARDIAN_ADDRESS")
|
||||
.takes_value(true)
|
||||
.index(2)
|
||||
.required(true)
|
||||
.help("Address of the initial guardian"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("guardian_set_expiration")
|
||||
.validator(is_u32)
|
||||
.value_name("GUARDIAN_SET_EXPIRATION")
|
||||
.takes_value(true)
|
||||
.index(3)
|
||||
.required(true)
|
||||
.help("Time in seconds after which a guardian set expires after an update"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("message_fee")
|
||||
.validator(is_u64)
|
||||
.value_name("MESSAGE_FEE")
|
||||
.takes_value(true)
|
||||
.index(4)
|
||||
.required(true)
|
||||
.help("Initial message posting fee"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("post-message")
|
||||
.about("Post a message via Wormhole")
|
||||
.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 address"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("nonce")
|
||||
.validator(is_u32)
|
||||
.value_name("NONCE")
|
||||
.takes_value(true)
|
||||
.index(2)
|
||||
.required(true)
|
||||
.help("Nonce of the message"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("data")
|
||||
.validator(is_hex)
|
||||
.value_name("DATA")
|
||||
.takes_value(true)
|
||||
.index(3)
|
||||
.required(true)
|
||||
.help("Payload of the message"),
|
||||
),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
let config = {
|
||||
let cli_config = if let Some(config_file) = matches.value_of("config_file") {
|
||||
solana_cli_config::Config::load(config_file).unwrap_or_default()
|
||||
} else {
|
||||
solana_cli_config::Config::default()
|
||||
};
|
||||
let json_rpc_url = value_t!(matches, "json_rpc_url", String)
|
||||
.unwrap_or_else(|_| cli_config.json_rpc_url.clone());
|
||||
|
||||
let client_keypair = || {
|
||||
read_keypair_file(&cli_config.keypair_path).unwrap_or_else(|err| {
|
||||
eprintln!("Unable to read {}: {}", cli_config.keypair_path, err);
|
||||
exit(1)
|
||||
})
|
||||
};
|
||||
|
||||
let init_args = bridge::instruction::Instruction::Initialize(types::BridgeConfig {
|
||||
guardian_set_expiration_time: DEFAULT_GUARDIAN_SET_EXPIRATION_TIME,
|
||||
fee: DEFAULT_MESSAGE_FEE,
|
||||
});
|
||||
let owner = keypair_of(&matches, "owner").unwrap_or_else(client_keypair);
|
||||
let fee_payer = keypair_of(&matches, "fee_payer").unwrap_or_else(client_keypair);
|
||||
|
||||
let ix_data = init_args.try_to_vec()?;
|
||||
Config {
|
||||
rpc_client: RpcClient::new(json_rpc_url),
|
||||
owner,
|
||||
fee_payer,
|
||||
commitment_config: CommitmentConfig::processed(),
|
||||
}
|
||||
};
|
||||
|
||||
let (ix, signers) = init.to_ix(program_id, ix_data.as_slice())?;
|
||||
let (recent_blockhash, _) = client.get_recent_blockhash()?;
|
||||
println!("Instruction ready.");
|
||||
println!(
|
||||
"Signing for {} signer(s): {:?}",
|
||||
signers.len(),
|
||||
signers.iter().map(|s| s.pubkey()).collect::<Vec<_>>()
|
||||
);
|
||||
let _ = match matches.subcommand() {
|
||||
("create-bridge", Some(arg_matches)) => {
|
||||
let bridge = pubkey_of(arg_matches, "bridge").unwrap();
|
||||
let initial_guardian: String = value_of(arg_matches, "guardian").unwrap();
|
||||
let initial_data = hex::decode(initial_guardian).unwrap();
|
||||
let guardian_expiration: u32 =
|
||||
value_of(arg_matches, "guardian_set_expiration").unwrap();
|
||||
let msg_fee: u64 = value_of(arg_matches, "message_fee").unwrap();
|
||||
|
||||
let mut tx = Transaction::new_with_payer(&[ix], Some(&payer_for_tx.pubkey()));
|
||||
let mut guardian = [0u8; 20];
|
||||
guardian.copy_from_slice(&initial_data);
|
||||
command_deploy_bridge(
|
||||
&config,
|
||||
&bridge,
|
||||
vec![guardian],
|
||||
guardian_expiration,
|
||||
msg_fee,
|
||||
)
|
||||
}
|
||||
("post-message", Some(arg_matches)) => {
|
||||
let bridge = pubkey_of(arg_matches, "bridge").unwrap();
|
||||
let data_str: String = value_of(arg_matches, "data").unwrap();
|
||||
let data = hex::decode(data_str).unwrap();
|
||||
let nonce: u32 = value_of(arg_matches, "nonce").unwrap();
|
||||
|
||||
tx.try_sign(&signers.iter().collect::<Vec<_>>(), recent_blockhash)?;
|
||||
println!("Transaction signed.");
|
||||
command_post_message(&config, &bridge, nonce, data)
|
||||
}
|
||||
|
||||
let signature = client.send_and_confirm_transaction_with_spinner_and_config(
|
||||
&tx,
|
||||
CommitmentConfig::processed(),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
.and_then(|transaction| {
|
||||
if let Some(transaction) = transaction {
|
||||
let signature = config
|
||||
.rpc_client
|
||||
.send_and_confirm_transaction_with_spinner_and_config(
|
||||
&transaction,
|
||||
config.commitment_config,
|
||||
RpcSendTransactionConfig {
|
||||
skip_preflight: true,
|
||||
preflight_commitment: None,
|
||||
|
@ -99,6 +338,77 @@ fn main() -> Result<(), ErrBox> {
|
|||
},
|
||||
)?;
|
||||
println!("Signature: {}", signature);
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.map_err(|err| {
|
||||
eprintln!("{}", err);
|
||||
exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn is_u8<T>(amount: T) -> Result<(), String>
|
||||
where
|
||||
T: AsRef<str> + Display,
|
||||
{
|
||||
if amount.as_ref().parse::<u8>().is_ok() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!(
|
||||
"Unable to parse input amount as integer, provided: {}",
|
||||
amount
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_u32<T>(amount: T) -> Result<(), String>
|
||||
where
|
||||
T: AsRef<str> + Display,
|
||||
{
|
||||
if amount.as_ref().parse::<u32>().is_ok() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!(
|
||||
"Unable to parse input amount as integer, provided: {}",
|
||||
amount
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_u64<T>(amount: T) -> Result<(), String>
|
||||
where
|
||||
T: AsRef<str> + Display,
|
||||
{
|
||||
if amount.as_ref().parse::<u64>().is_ok() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!(
|
||||
"Unable to parse input amount as integer, provided: {}",
|
||||
amount
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_hex<T>(value: T) -> Result<(), String>
|
||||
where
|
||||
T: AsRef<str> + Display,
|
||||
{
|
||||
hex::decode(value.to_string())
|
||||
.map(|_| ())
|
||||
.map_err(|e| format!("{}", e))
|
||||
}
|
||||
|
||||
fn check_fee_payer_balance(config: &Config, required_balance: u64) -> Result<(), Error> {
|
||||
let balance = config.rpc_client.get_balance(&config.fee_payer.pubkey())?;
|
||||
if balance < required_balance {
|
||||
Err(format!(
|
||||
"Fee payer, {}, has insufficient balance: {} required, {} available",
|
||||
config.fee_payer.pubkey(),
|
||||
lamports_to_sol(required_balance),
|
||||
lamports_to_sol(balance)
|
||||
)
|
||||
.into())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::{
|
||||
accounts::{
|
||||
Bridge,
|
||||
FeeCollector,
|
||||
GuardianSet,
|
||||
GuardianSetDerivationData,
|
||||
},
|
||||
|
@ -17,6 +18,7 @@ type Payer<'a> = Signer<Info<'a>>;
|
|||
pub struct Initialize<'b> {
|
||||
pub bridge: Bridge<'b, { AccountState::Uninitialized }>,
|
||||
pub guardian_set: GuardianSet<'b, { AccountState::Uninitialized }>,
|
||||
pub fee_collector: FeeCollector<'b>,
|
||||
pub payer: Payer<'b>,
|
||||
}
|
||||
|
||||
|
@ -49,5 +51,15 @@ pub fn initialize(
|
|||
accs.bridge.guardian_set_index = 0;
|
||||
accs.bridge.config = config;
|
||||
|
||||
// Initialize the fee collector account so it's rent exempt and will keep funds
|
||||
accs.fee_collector.create(
|
||||
ctx,
|
||||
accs.payer.key,
|
||||
Exempt,
|
||||
0,
|
||||
&solana_program::system_program::id(),
|
||||
)?;
|
||||
accs.bridge.last_lamports = accs.fee_collector.lamports();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -89,13 +89,12 @@ pub fn post_message(
|
|||
.verify_derivation(ctx.program_id, &msg_derivation)?;
|
||||
|
||||
// Fee handling
|
||||
let fee = transfer_fee();
|
||||
if accs
|
||||
.fee_collector
|
||||
.lamports()
|
||||
.checked_sub(accs.bridge.last_lamports)
|
||||
.ok_or(MathOverflow)?
|
||||
< fee
|
||||
< accs.bridge.config.fee
|
||||
{
|
||||
return Err(InsufficientFees.into());
|
||||
}
|
||||
|
@ -124,7 +123,3 @@ pub fn post_message(
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn transfer_fee() -> u64 {
|
||||
500
|
||||
}
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
use borsh::BorshSerialize;
|
||||
use solitaire::processors::seeded::Seeded;
|
||||
use solitaire::AccountState;
|
||||
use solana_program::{
|
||||
borsh::try_from_slice_unchecked,
|
||||
hash,
|
||||
|
@ -17,6 +15,10 @@ use solana_program::{
|
|||
system_program,
|
||||
sysvar,
|
||||
};
|
||||
use solitaire::{
|
||||
processors::seeded::Seeded,
|
||||
AccountState,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
accounts::{
|
||||
|
@ -41,25 +43,22 @@ use crate::{
|
|||
pub fn initialize(
|
||||
program_id: Pubkey,
|
||||
payer: Pubkey,
|
||||
bridge: Pubkey,
|
||||
guardian_set_index: u32,
|
||||
guardian_set: Pubkey,
|
||||
fee: u64,
|
||||
guardian_set_expiration_time: u32,
|
||||
) -> solitaire::Result<Instruction> {
|
||||
let bridge = Bridge::<'_, { AccountState::Uninitialized }>::key(None, &program_id);
|
||||
let guardian_set = GuardianSet::<'_, { AccountState::Uninitialized }>::key(
|
||||
&GuardianSetDerivationData {
|
||||
index: guardian_set_index,
|
||||
},
|
||||
&GuardianSetDerivationData { index: 0 },
|
||||
&program_id,
|
||||
);
|
||||
let fee_collector = FeeCollector::key(None, &program_id);
|
||||
|
||||
Ok(Instruction {
|
||||
program_id,
|
||||
accounts: vec![
|
||||
AccountMeta::new(bridge, false),
|
||||
AccountMeta::new(guardian_set, false),
|
||||
AccountMeta::new(fee_collector, false),
|
||||
AccountMeta::new(payer, true),
|
||||
AccountMeta::new_readonly(sysvar::rent::id(), false),
|
||||
AccountMeta::new_readonly(solana_program::system_program::id(), false),
|
||||
|
@ -75,22 +74,27 @@ pub fn initialize(
|
|||
pub fn post_message(
|
||||
program_id: Pubkey,
|
||||
payer: Pubkey,
|
||||
bridge: Pubkey,
|
||||
emitter: Pubkey,
|
||||
message: PostedMessage,
|
||||
sequence: u64,
|
||||
nonce: u32,
|
||||
payload: Vec<u8>,
|
||||
) -> solitaire::Result<Instruction> {
|
||||
let bridge = Bridge::<'_, { AccountState::Uninitialized }>::key(None, &program_id);
|
||||
let fee_collector = FeeCollector::<'_>::key(None, &program_id);
|
||||
let sequence = Sequence::<'_>::key(&SequenceDerivationData {
|
||||
let sequence = Sequence::<'_>::key(
|
||||
&SequenceDerivationData {
|
||||
emitter_key: &emitter,
|
||||
}, &program_id);
|
||||
let message = Message::<'_, { AccountState::Uninitialized }>::key(&MessageDerivationData {
|
||||
},
|
||||
&program_id,
|
||||
);
|
||||
let message = Message::<'_, { AccountState::Uninitialized }>::key(
|
||||
&MessageDerivationData {
|
||||
emitter_key: emitter.to_bytes(),
|
||||
emitter_chain: message.emitter_chain,
|
||||
nonce: message.nonce,
|
||||
payload: message.payload.clone(),
|
||||
}, &program_id);
|
||||
emitter_chain: 1,
|
||||
nonce,
|
||||
payload: payload.clone(),
|
||||
},
|
||||
&program_id,
|
||||
);
|
||||
|
||||
Ok(Instruction {
|
||||
program_id,
|
||||
|
@ -108,10 +112,10 @@ pub fn post_message(
|
|||
],
|
||||
|
||||
data: crate::instruction::Instruction::PostMessage(PostMessageData {
|
||||
nonce: 0,
|
||||
payload: vec![],
|
||||
nonce,
|
||||
payload: payload.clone(),
|
||||
})
|
||||
.try_to_vec()?
|
||||
.try_to_vec()?,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -155,7 +159,7 @@ pub fn verify_signatures(
|
|||
signers,
|
||||
initial_creation: true,
|
||||
})
|
||||
.try_to_vec()?
|
||||
.try_to_vec()?,
|
||||
})
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ use solana_program::msg;
|
|||
// package as soon as possible.
|
||||
pub mod accounts;
|
||||
pub mod api;
|
||||
pub mod client_instructions;
|
||||
pub mod types;
|
||||
pub mod vaa;
|
||||
|
||||
|
@ -25,14 +26,14 @@ pub use api::{
|
|||
PostMessageData,
|
||||
PostVAA,
|
||||
PostVAAData,
|
||||
Signature,
|
||||
UninitializedMessage,
|
||||
UpgradeContract,
|
||||
UpgradeContractData,
|
||||
UpgradeGuardianSet,
|
||||
UpgradeGuardianSetData,
|
||||
VerifySignatures,
|
||||
VerifySignaturesData,
|
||||
UninitializedMessage,
|
||||
Signature,
|
||||
};
|
||||
use types::BridgeConfig;
|
||||
|
||||
|
|
Loading…
Reference in New Issue