track wrapped asset meta, test CLI with acc creation
This commit is contained in:
parent
ff0b4766ae
commit
30666fd7ff
|
@ -106,6 +106,7 @@ followed by:
|
||||||
| 6 | token_program | SplToken | | | ️ | |
|
| 6 | token_program | SplToken | | | ️ | |
|
||||||
| 7 | token | WrappedAsset | | | opt | ✅ |
|
| 7 | token | WrappedAsset | | | opt | ✅ |
|
||||||
| 8 | destination | TokenAccount | | ✅ | opt | |
|
| 8 | destination | TokenAccount | | ✅ | opt | |
|
||||||
|
| 9 | wrapped_meta_account | WrappedAssetMeta | | ✅ | opt | ✅ |
|
||||||
|
|
||||||
##### Transfer: Ethereum (wrapped) -> Solana (native)
|
##### Transfer: Ethereum (wrapped) -> Solana (native)
|
||||||
|
|
||||||
|
@ -120,8 +121,7 @@ followed by:
|
||||||
|
|
||||||
| Index | Name | Type | signer | writeable | empty | derived |
|
| Index | Name | Type | signer | writeable | empty | derived |
|
||||||
| ----- | ------------ | ------------------- | ------ | --------- | ----- | ------- |
|
| ----- | ------------ | ------------------- | ------ | --------- | ----- | ------- |
|
||||||
| 7 | out_proposal | TransferOutProposal | | ✅ | | ✅ |
|
| 6 | out_proposal | TransferOutProposal | | ✅ | | ✅ |
|
||||||
| 8 | sender | Account | ✅ | | | |
|
|
||||||
|
|
||||||
## Accounts
|
## Accounts
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ use solana_sdk::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::error::Error::VAATooLong;
|
use crate::error::Error::VAATooLong;
|
||||||
use crate::instruction::BridgeInstruction::Initialize;
|
use crate::instruction::BridgeInstruction::{Initialize, PostVAA, TransferOut};
|
||||||
use crate::state::{AssetMeta, Bridge, BridgeConfig};
|
use crate::state::{AssetMeta, Bridge, BridgeConfig};
|
||||||
use crate::syscalls::RawKey;
|
use crate::syscalls::RawKey;
|
||||||
use crate::vaa::{VAABody, VAA};
|
use crate::vaa::{VAABody, VAA};
|
||||||
|
@ -118,6 +118,16 @@ impl BridgeInstruction {
|
||||||
|
|
||||||
Initialize(*payload)
|
Initialize(*payload)
|
||||||
}
|
}
|
||||||
|
1 => {
|
||||||
|
let payload: &TransferOutPayload = unpack(input)?;
|
||||||
|
|
||||||
|
TransferOut(*payload)
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
let payload: &VAAData = unpack(input)?;
|
||||||
|
|
||||||
|
PostVAA(*payload)
|
||||||
|
}
|
||||||
_ => return Err(ProgramError::InvalidInstructionData),
|
_ => return Err(ProgramError::InvalidInstructionData),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -310,9 +320,12 @@ pub fn post_vaa(
|
||||||
t.asset.chain,
|
t.asset.chain,
|
||||||
t.asset.address,
|
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_readonly(spl_token::id(), false));
|
||||||
accounts.push(AccountMeta::new(wrapped_key, false));
|
accounts.push(AccountMeta::new(wrapped_key, false));
|
||||||
accounts.push(AccountMeta::new(Pubkey::new(&t.target_address), false));
|
accounts.push(AccountMeta::new(Pubkey::new(&t.target_address), false));
|
||||||
|
accounts.push(AccountMeta::new(wrapped_meta_key, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,6 +133,7 @@ impl Bridge {
|
||||||
accounts: &[AccountInfo],
|
accounts: &[AccountInfo],
|
||||||
t: &TransferOutPayload,
|
t: &TransferOutPayload,
|
||||||
) -> ProgramResult {
|
) -> ProgramResult {
|
||||||
|
info!("wrapped transfer out");
|
||||||
let account_info_iter = &mut accounts.iter();
|
let account_info_iter = &mut accounts.iter();
|
||||||
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
|
||||||
|
@ -145,18 +146,12 @@ 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)?;
|
|
||||||
|
|
||||||
// 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 {
|
||||||
return Err(Error::TokenMintMismatch.into());
|
return Err(Error::TokenMintMismatch.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is the mint owned by the program
|
|
||||||
if mint.owner.unwrap() != *program_id {
|
|
||||||
return Err(Error::WrongMintOwner.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that the mint is actually a wrapped asset belonging to *this* bridge instance
|
// Check that the mint is actually a wrapped asset belonging to *this* bridge instance
|
||||||
let expected_mint_address = Bridge::derive_wrapped_asset_id(
|
let expected_mint_address = Bridge::derive_wrapped_asset_id(
|
||||||
program_id,
|
program_id,
|
||||||
|
@ -218,6 +213,7 @@ impl Bridge {
|
||||||
accounts: &[AccountInfo],
|
accounts: &[AccountInfo],
|
||||||
t: &TransferOutPayload,
|
t: &TransferOutPayload,
|
||||||
) -> ProgramResult {
|
) -> ProgramResult {
|
||||||
|
info!("native transfer out");
|
||||||
let account_info_iter = &mut accounts.iter();
|
let account_info_iter = &mut accounts.iter();
|
||||||
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
|
||||||
|
@ -230,18 +226,12 @@ 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)?;
|
|
||||||
|
|
||||||
// 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 {
|
||||||
return Err(Error::TokenMintMismatch.into());
|
return Err(Error::TokenMintMismatch.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the mint is owned by the program, it's a wrapped asset
|
|
||||||
if mint.owner.unwrap() == *program_id {
|
|
||||||
return Err(Error::WrongMintOwner.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create transfer account
|
// Create transfer account
|
||||||
let transfer_seed = Bridge::derive_transfer_id_seeds(
|
let transfer_seed = Bridge::derive_transfer_id_seeds(
|
||||||
bridge_info.key,
|
bridge_info.key,
|
||||||
|
@ -293,7 +283,7 @@ impl Bridge {
|
||||||
return Err(Error::WrongTokenAccountOwner.into());
|
return Err(Error::WrongTokenAccountOwner.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transfer tokens to custody
|
// Transfer tokens to custody - This also checks that custody mint = mint
|
||||||
Bridge::token_transfer_caller(
|
Bridge::token_transfer_caller(
|
||||||
accounts,
|
accounts,
|
||||||
&bridge.config.token_program,
|
&bridge.config.token_program,
|
||||||
|
@ -500,6 +490,7 @@ impl Bridge {
|
||||||
next_account_info(account_info_iter)?; // Token program
|
next_account_info(account_info_iter)?; // Token program
|
||||||
let mint_info = next_account_info(account_info_iter)?;
|
let mint_info = next_account_info(account_info_iter)?;
|
||||||
let destination_info = next_account_info(account_info_iter)?;
|
let destination_info = next_account_info(account_info_iter)?;
|
||||||
|
let wrapped_meta_info = next_account_info(account_info_iter)?;
|
||||||
|
|
||||||
let destination = Self::token_account_deserialize(destination_info)?;
|
let destination = Self::token_account_deserialize(destination_info)?;
|
||||||
if destination.mint != *mint_info.key {
|
if destination.mint != *mint_info.key {
|
||||||
|
@ -546,6 +537,25 @@ impl Bridge {
|
||||||
payer_info.key,
|
payer_info.key,
|
||||||
&b.asset,
|
&b.asset,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
// Check and create wrapped asset meta if it is unset
|
||||||
|
let wrapped_meta_seeds =
|
||||||
|
Bridge::derive_wrapped_meta_seeds(bridge_info.key, mint_info.key);
|
||||||
|
Bridge::check_and_create_account::<WrappedAssetMeta>(
|
||||||
|
program_id,
|
||||||
|
accounts,
|
||||||
|
wrapped_meta_info.key,
|
||||||
|
payer_info.key,
|
||||||
|
&wrapped_meta_seeds,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let mut wrapped_meta_data = wrapped_meta_info.data.borrow_mut();
|
||||||
|
let wrapped_meta: &mut WrappedAssetMeta =
|
||||||
|
Bridge::unpack_unchecked(&mut wrapped_meta_data)?;
|
||||||
|
|
||||||
|
wrapped_meta.is_initialized = true;
|
||||||
|
wrapped_meta.address = b.asset.address;
|
||||||
|
wrapped_meta.chain = b.asset.chain;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bridge::wrapped_mint_to(
|
Bridge::wrapped_mint_to(
|
||||||
|
@ -735,7 +745,7 @@ impl Bridge {
|
||||||
payer: &Pubkey,
|
payer: &Pubkey,
|
||||||
) -> Result<(), ProgramError> {
|
) -> Result<(), ProgramError> {
|
||||||
Self::create_account::<Mint>(
|
Self::create_account::<Mint>(
|
||||||
program_id,
|
token_program,
|
||||||
accounts,
|
accounts,
|
||||||
mint,
|
mint,
|
||||||
payer,
|
payer,
|
||||||
|
@ -756,7 +766,7 @@ impl Bridge {
|
||||||
asset: &AssetMeta,
|
asset: &AssetMeta,
|
||||||
) -> Result<(), ProgramError> {
|
) -> Result<(), ProgramError> {
|
||||||
Self::create_account::<Mint>(
|
Self::create_account::<Mint>(
|
||||||
program_id,
|
token_program,
|
||||||
accounts,
|
accounts,
|
||||||
mint,
|
mint,
|
||||||
payer,
|
payer,
|
||||||
|
@ -806,7 +816,8 @@ impl Bridge {
|
||||||
program_id,
|
program_id,
|
||||||
);
|
);
|
||||||
let s: Vec<_> = seeds.iter().map(|item| item.as_slice()).collect();
|
let s: Vec<_> = seeds.iter().map(|item| item.as_slice()).collect();
|
||||||
invoke_signed(&ix, accounts, &[s.as_slice()])
|
//invoke_signed(&ix, accounts, &[s.as_slice()])
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -99,6 +99,25 @@ impl IsInitialized for ClaimedVAA {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// metadata tracking for wrapped assets
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||||
|
pub struct WrappedAssetMeta {
|
||||||
|
/// chain id of the native chain of this asset
|
||||||
|
pub chain: u8,
|
||||||
|
/// address of the asset on the native chain
|
||||||
|
pub address: ForeignAddress,
|
||||||
|
|
||||||
|
/// Is `true` if this structure has been initialized.
|
||||||
|
pub is_initialized: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IsInitialized for WrappedAssetMeta {
|
||||||
|
fn is_initialized(&self) -> bool {
|
||||||
|
self.is_initialized
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Metadata about an asset
|
/// Metadata about an asset
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||||
|
@ -265,6 +284,15 @@ impl Bridge {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Calculates derived seeds for a wrapped asset meta entry
|
||||||
|
pub fn derive_wrapped_meta_seeds<'a>(bridge: &Pubkey, mint: &Pubkey) -> Vec<Vec<u8>> {
|
||||||
|
vec![
|
||||||
|
"claim".as_bytes().to_vec(),
|
||||||
|
bridge.to_bytes().to_vec(),
|
||||||
|
mint.to_bytes().to_vec(),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
/// Calculates a derived address for this program
|
/// Calculates a derived address for this program
|
||||||
pub fn derive_bridge_id(program_id: &Pubkey) -> Result<Pubkey, Error> {
|
pub fn derive_bridge_id(program_id: &Pubkey) -> Result<Pubkey, Error> {
|
||||||
Self::derive_key(program_id, &Self::derive_bridge_seeds())
|
Self::derive_key(program_id, &Self::derive_bridge_seeds())
|
||||||
|
@ -288,6 +316,15 @@ impl Bridge {
|
||||||
Self::derive_key(program_id, &Self::derive_claim_seeds(bridge, hash))
|
Self::derive_key(program_id, &Self::derive_claim_seeds(bridge, hash))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Calculates a derived address for a wrapped asset meta entry
|
||||||
|
pub fn derive_wrapped_meta_id(
|
||||||
|
program_id: &Pubkey,
|
||||||
|
bridge: &Pubkey,
|
||||||
|
mint: &Pubkey,
|
||||||
|
) -> Result<Pubkey, Error> {
|
||||||
|
Self::derive_key(program_id, &Self::derive_wrapped_meta_seeds(bridge, mint))
|
||||||
|
}
|
||||||
|
|
||||||
/// Calculates a derived address for this program
|
/// Calculates a derived address for this program
|
||||||
pub fn derive_guardian_set_id(
|
pub fn derive_guardian_set_id(
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
|
|
|
@ -291,13 +291,14 @@ name = "cli"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
|
"primitive-types",
|
||||||
"solana-clap-utils",
|
"solana-clap-utils",
|
||||||
"solana-cli-config",
|
"solana-cli-config",
|
||||||
"solana-client",
|
"solana-client",
|
||||||
"solana-faucet",
|
"solana-faucet",
|
||||||
"solana-logger",
|
"solana-logger",
|
||||||
"solana-sdk",
|
"solana-sdk",
|
||||||
"spl-token 1.0.6",
|
"spl-token 1.0.3",
|
||||||
"wormhole-bridge",
|
"wormhole-bridge",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -14,5 +14,6 @@ solana-logger = { version = "1.2.17" }
|
||||||
solana-sdk = { version = "1.2.17" }
|
solana-sdk = { version = "1.2.17" }
|
||||||
solana-client = { version = "1.2.17" }
|
solana-client = { version = "1.2.17" }
|
||||||
solana-faucet = "1.2.17"
|
solana-faucet = "1.2.17"
|
||||||
spl-token = { version = "1.0.6" }
|
spl-token = { package = "spl-token", git="https://github.com/hendrikhofstadt/solana-program-library", branch="256b-primitive-types" }
|
||||||
wormhole-bridge = { path = "../bridge" }
|
wormhole-bridge = { path = "../bridge" }
|
||||||
|
primitive-types = {version ="0.7.2", default-features = false}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::fmt::Display;
|
||||||
use std::net::{IpAddr, SocketAddr};
|
use std::net::{IpAddr, SocketAddr};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
|
@ -8,13 +9,16 @@ use clap::{
|
||||||
crate_description, crate_name, crate_version, value_t, value_t_or_exit, App, AppSettings, Arg,
|
crate_description, crate_name, crate_version, value_t, value_t_or_exit, App, AppSettings, Arg,
|
||||||
SubCommand,
|
SubCommand,
|
||||||
};
|
};
|
||||||
|
use primitive_types::U256;
|
||||||
use solana_clap_utils::{
|
use solana_clap_utils::{
|
||||||
input_parsers::{keypair_of, pubkey_of},
|
input_parsers::{keypair_of, pubkey_of},
|
||||||
input_validators::{is_amount, is_keypair, is_pubkey_or_keypair, is_url},
|
input_validators::{is_amount, is_keypair, is_pubkey_or_keypair, is_url},
|
||||||
};
|
};
|
||||||
use solana_client::rpc_client::RpcClient;
|
use solana_client::rpc_client::RpcClient;
|
||||||
|
use solana_client::rpc_request::TokenAccountsFilter;
|
||||||
use solana_faucet::faucet::request_airdrop_transaction;
|
use solana_faucet::faucet::request_airdrop_transaction;
|
||||||
use solana_sdk::hash::Hash;
|
use solana_sdk::hash::Hash;
|
||||||
|
use solana_sdk::system_instruction::create_account;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
native_token::*,
|
native_token::*,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
|
@ -28,8 +32,13 @@ use spl_token::{
|
||||||
state::{Account, Mint},
|
state::{Account, Mint},
|
||||||
};
|
};
|
||||||
|
|
||||||
use spl_bridge::instruction::initialize;
|
use spl_bridge::instruction::{
|
||||||
use spl_bridge::state::BridgeConfig;
|
initialize, post_vaa, transfer_out, BridgeInstruction, ForeignAddress, TransferOutPayload,
|
||||||
|
VAAData, CHAIN_ID_SOLANA,
|
||||||
|
};
|
||||||
|
use spl_bridge::state::{
|
||||||
|
AssetMeta, Bridge, BridgeConfig, ClaimedVAA, GuardianSet, TransferOutProposal,
|
||||||
|
};
|
||||||
use spl_bridge::syscalls::RawKey;
|
use spl_bridge::syscalls::RawKey;
|
||||||
|
|
||||||
struct Config {
|
struct Config {
|
||||||
|
@ -41,50 +50,6 @@ struct Config {
|
||||||
type Error = Box<dyn std::error::Error>;
|
type Error = Box<dyn std::error::Error>;
|
||||||
type CommmandResult = Result<Option<Transaction>, Error>;
|
type CommmandResult = Result<Option<Transaction>, Error>;
|
||||||
|
|
||||||
fn requestAirdrop(config: &Config, request_sol: u64) -> CommmandResult {
|
|
||||||
let (blockhash, _fee_calculator) = config.rpc_client.get_recent_blockhash()?;
|
|
||||||
let faucet_addr = SocketAddr::new(IpAddr::from_str("127.0.0.1").unwrap(), 9900);
|
|
||||||
match {
|
|
||||||
let mut retries = 5;
|
|
||||||
loop {
|
|
||||||
let result = FaucetKeypair::new_keypair(
|
|
||||||
&faucet_addr,
|
|
||||||
&config.owner.pubkey(),
|
|
||||||
LAMPORTS_PER_SOL * request_sol,
|
|
||||||
blockhash,
|
|
||||||
);
|
|
||||||
if result.is_ok() || retries == 0 {
|
|
||||||
break result;
|
|
||||||
}
|
|
||||||
retries -= 1;
|
|
||||||
sleep(Duration::from_secs(1));
|
|
||||||
}
|
|
||||||
} {
|
|
||||||
Ok(kp) => Ok(Some(kp.airdrop_transaction())),
|
|
||||||
Err(e) => Err(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FaucetKeypair {
|
|
||||||
transaction: Transaction,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FaucetKeypair {
|
|
||||||
fn new_keypair(
|
|
||||||
faucet_addr: &SocketAddr,
|
|
||||||
to_pubkey: &Pubkey,
|
|
||||||
lamports: u64,
|
|
||||||
blockhash: Hash,
|
|
||||||
) -> Result<Self, Error> {
|
|
||||||
let transaction = request_airdrop_transaction(faucet_addr, to_pubkey, lamports, blockhash)?;
|
|
||||||
Ok(Self { transaction })
|
|
||||||
}
|
|
||||||
|
|
||||||
fn airdrop_transaction(&self) -> Transaction {
|
|
||||||
self.transaction.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_fee_payer_balance(config: &Config, required_balance: u64) -> Result<(), Error> {
|
fn check_fee_payer_balance(config: &Config, required_balance: u64) -> Result<(), Error> {
|
||||||
let balance = config.rpc_client.get_balance(&config.fee_payer.pubkey())?;
|
let balance = config.rpc_client.get_balance(&config.fee_payer.pubkey())?;
|
||||||
if balance < required_balance {
|
if balance < required_balance {
|
||||||
|
@ -115,28 +80,6 @@ fn check_owner_balance(config: &Config, required_balance: u64) -> Result<(), Err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn command_request_airdrop(config: &Config) -> CommmandResult {
|
|
||||||
let token = Keypair::new();
|
|
||||||
println!("Requesting airdrop");
|
|
||||||
|
|
||||||
let minimum_balance_for_rent_exemption = config
|
|
||||||
.rpc_client
|
|
||||||
.get_minimum_balance_for_rent_exemption(size_of::<Mint>())?;
|
|
||||||
|
|
||||||
let mut transaction: Transaction = requestAirdrop(config, 20)?.unwrap();
|
|
||||||
|
|
||||||
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, &token],
|
|
||||||
recent_blockhash,
|
|
||||||
);
|
|
||||||
Ok(Some(transaction))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn command_deploy_bridge(config: &Config) -> CommmandResult {
|
fn command_deploy_bridge(config: &Config) -> CommmandResult {
|
||||||
println!("Deploying bridge program");
|
println!("Deploying bridge program");
|
||||||
|
|
||||||
|
@ -144,20 +87,134 @@ fn command_deploy_bridge(config: &Config) -> CommmandResult {
|
||||||
.rpc_client
|
.rpc_client
|
||||||
.get_minimum_balance_for_rent_exemption(size_of::<Mint>())?;
|
.get_minimum_balance_for_rent_exemption(size_of::<Mint>())?;
|
||||||
|
|
||||||
|
let p = Pubkey::from_str("7AeSppn3AjaeYScZsnRf1ZRQvtyo4Ke5gx7PAJ3r7BFp").unwrap();
|
||||||
let ix = initialize(
|
let ix = initialize(
|
||||||
&Pubkey::from_str("5x4kJ1G4UgJc3yNsznZpxEAB2vrnJirSBxZDu1uwaqnZ").unwrap(),
|
&p,
|
||||||
&config.owner.pubkey(),
|
&config.owner.pubkey(),
|
||||||
RawKey {
|
RawKey {
|
||||||
x: [8; 32],
|
x: [8; 32],
|
||||||
y: [2; 32],
|
y_parity: true,
|
||||||
},
|
},
|
||||||
&BridgeConfig {
|
&BridgeConfig {
|
||||||
vaa_expiration_time: 200000000,
|
vaa_expiration_time: 200000000,
|
||||||
token_program: spl_token::id(),
|
token_program: spl_token::id(),
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
println!("bridge: {}, ", ix.accounts[0].pubkey.to_string());
|
println!("bridge: {}, ", ix.accounts[2].pubkey.to_string());
|
||||||
println!("payer: {}, ", ix.accounts[3].pubkey.to_string());
|
println!("payer: {}, ", ix.accounts[3].pubkey.to_string());
|
||||||
|
|
||||||
|
let mut ix_c = create_account(
|
||||||
|
&config.owner.pubkey(),
|
||||||
|
&ix.accounts[2].pubkey,
|
||||||
|
100000000,
|
||||||
|
size_of::<Bridge>() as u64,
|
||||||
|
&p,
|
||||||
|
);
|
||||||
|
ix_c.accounts[1].is_signer = false;
|
||||||
|
let mut ix_c2 = create_account(
|
||||||
|
&config.owner.pubkey(),
|
||||||
|
&ix.accounts[3].pubkey,
|
||||||
|
100000000,
|
||||||
|
size_of::<GuardianSet>() as u64,
|
||||||
|
&p,
|
||||||
|
);
|
||||||
|
ix_c2.accounts[1].is_signer = false;
|
||||||
|
let mut transaction =
|
||||||
|
Transaction::new_with_payer(&[ix_c, ix_c2, ix], Some(&config.fee_payer.pubkey()));
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn command_lock_tokens(
|
||||||
|
config: &Config,
|
||||||
|
account: Pubkey,
|
||||||
|
token: Pubkey,
|
||||||
|
amount: u64,
|
||||||
|
to_chain: u8,
|
||||||
|
target: ForeignAddress,
|
||||||
|
nonce: u32,
|
||||||
|
) -> CommmandResult {
|
||||||
|
println!("Initiating transfer to foreign chain");
|
||||||
|
|
||||||
|
let minimum_balance_for_rent_exemption = config
|
||||||
|
.rpc_client
|
||||||
|
.get_minimum_balance_for_rent_exemption(size_of::<Mint>())?;
|
||||||
|
|
||||||
|
let p = Pubkey::from_str("7AeSppn3AjaeYScZsnRf1ZRQvtyo4Ke5gx7PAJ3r7BFp").unwrap();
|
||||||
|
let ix = transfer_out(
|
||||||
|
&p,
|
||||||
|
&config.owner.pubkey(),
|
||||||
|
&account,
|
||||||
|
&token,
|
||||||
|
&TransferOutPayload {
|
||||||
|
amount: U256::from(amount),
|
||||||
|
chain_id: to_chain,
|
||||||
|
asset: AssetMeta {
|
||||||
|
address: token.to_bytes(), // TODO fetch from WASSET (if WASSET)
|
||||||
|
chain: CHAIN_ID_SOLANA, //TODO fetch from WASSET (if WASSET)
|
||||||
|
},
|
||||||
|
target,
|
||||||
|
nonce,
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
println!("custody: {}, ", ix.accounts[7].pubkey.to_string());
|
||||||
|
|
||||||
|
// Approve tokens
|
||||||
|
let mut ix_a = approve(
|
||||||
|
&spl_token::id(),
|
||||||
|
&account,
|
||||||
|
&ix.accounts[3].pubkey,
|
||||||
|
&config.owner.pubkey(),
|
||||||
|
&[],
|
||||||
|
U256::from(amount),
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO remove create calls
|
||||||
|
let mut ix_c = create_account(
|
||||||
|
&config.owner.pubkey(),
|
||||||
|
&ix.accounts[4].pubkey,
|
||||||
|
100000000,
|
||||||
|
size_of::<TransferOutProposal>() as u64,
|
||||||
|
&p,
|
||||||
|
);
|
||||||
|
ix_c.accounts[1].is_signer = false;
|
||||||
|
let mut ix_c2 = create_account(
|
||||||
|
&config.owner.pubkey(),
|
||||||
|
&ix.accounts[7].pubkey,
|
||||||
|
100000000,
|
||||||
|
size_of::<Account>() as u64,
|
||||||
|
&spl_token::id(),
|
||||||
|
);
|
||||||
|
ix_c2.accounts[1].is_signer = false;
|
||||||
|
|
||||||
|
let mut transaction =
|
||||||
|
Transaction::new_with_payer(&[ix_a, ix_c, ix_c2, ix], Some(&config.fee_payer.pubkey()));
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn command_submit_vaa(config: &Config, vaa: &[u8]) -> CommmandResult {
|
||||||
|
println!("Submitting VAA");
|
||||||
|
|
||||||
|
let minimum_balance_for_rent_exemption = config
|
||||||
|
.rpc_client
|
||||||
|
.get_minimum_balance_for_rent_exemption(size_of::<Mint>())?;
|
||||||
|
|
||||||
|
let p = Pubkey::from_str("7AeSppn3AjaeYScZsnRf1ZRQvtyo4Ke5gx7PAJ3r7BFp").unwrap();
|
||||||
|
let ix = post_vaa(&p, &config.owner.pubkey(), vaa)?;
|
||||||
|
|
||||||
let mut transaction = Transaction::new_with_payer(&[ix], Some(&config.fee_payer.pubkey()));
|
let mut transaction = Transaction::new_with_payer(&[ix], Some(&config.fee_payer.pubkey()));
|
||||||
|
|
||||||
let (recent_blockhash, fee_calculator) = config.rpc_client.get_recent_blockhash()?;
|
let (recent_blockhash, fee_calculator) = config.rpc_client.get_recent_blockhash()?;
|
||||||
|
@ -191,7 +248,7 @@ fn command_create_token(config: &Config) -> CommmandResult {
|
||||||
&token.pubkey(),
|
&token.pubkey(),
|
||||||
None,
|
None,
|
||||||
Some(&config.owner.pubkey()),
|
Some(&config.owner.pubkey()),
|
||||||
0,
|
U256::from(0),
|
||||||
9, // hard code 9 decimal places to match the sol/lamports relationship
|
9, // hard code 9 decimal places to match the sol/lamports relationship
|
||||||
)?,
|
)?,
|
||||||
],
|
],
|
||||||
|
@ -293,7 +350,37 @@ fn command_transfer(
|
||||||
&recipient,
|
&recipient,
|
||||||
&config.owner.pubkey(),
|
&config.owner.pubkey(),
|
||||||
&[],
|
&[],
|
||||||
amount,
|
U256::from(amount),
|
||||||
|
)?],
|
||||||
|
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_approve(
|
||||||
|
config: &Config,
|
||||||
|
sender: Pubkey,
|
||||||
|
ui_amount: f64,
|
||||||
|
recipient: Pubkey,
|
||||||
|
) -> CommmandResult {
|
||||||
|
println!(
|
||||||
|
"Approve {} tokens\n Sender: {}\n Recipient: {}",
|
||||||
|
ui_amount, sender, recipient
|
||||||
|
);
|
||||||
|
let amount = sol_to_lamports(ui_amount);
|
||||||
|
|
||||||
|
let mut transaction = Transaction::new_with_payer(
|
||||||
|
&[approve(
|
||||||
|
&spl_token::id(),
|
||||||
|
&sender,
|
||||||
|
&recipient,
|
||||||
|
&config.owner.pubkey(),
|
||||||
|
&[],
|
||||||
|
U256::from(amount),
|
||||||
)?],
|
)?],
|
||||||
Some(&config.fee_payer.pubkey()),
|
Some(&config.fee_payer.pubkey()),
|
||||||
);
|
);
|
||||||
|
@ -314,7 +401,7 @@ fn command_burn(config: &Config, source: Pubkey, ui_amount: f64) -> CommmandResu
|
||||||
&source,
|
&source,
|
||||||
&config.owner.pubkey(),
|
&config.owner.pubkey(),
|
||||||
&[],
|
&[],
|
||||||
amount,
|
U256::from(amount),
|
||||||
)?],
|
)?],
|
||||||
Some(&config.fee_payer.pubkey()),
|
Some(&config.fee_payer.pubkey()),
|
||||||
);
|
);
|
||||||
|
@ -344,7 +431,7 @@ fn command_mint(
|
||||||
&recipient,
|
&recipient,
|
||||||
&config.owner.pubkey(),
|
&config.owner.pubkey(),
|
||||||
&[],
|
&[],
|
||||||
amount,
|
U256::from(amount),
|
||||||
)?],
|
)?],
|
||||||
Some(&config.fee_payer.pubkey()),
|
Some(&config.fee_payer.pubkey()),
|
||||||
);
|
);
|
||||||
|
@ -414,8 +501,9 @@ fn command_unwrap(config: &Config, address: Pubkey) -> CommmandResult {
|
||||||
Ok(Some(transaction))
|
Ok(Some(transaction))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn command_balance(_config: &Config, address: Pubkey) -> CommmandResult {
|
fn command_balance(config: &Config, address: Pubkey) -> CommmandResult {
|
||||||
println!("balance {}", address);
|
let balance = config.rpc_client.get_token_account_balance(&address)?;
|
||||||
|
println!("{}", balance);
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -424,8 +512,28 @@ fn command_supply(_config: &Config, address: Pubkey) -> CommmandResult {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn command_accounts(_config: &Config, token: Option<Pubkey>) -> CommmandResult {
|
fn command_accounts(config: &Config, token: Option<Pubkey>) -> CommmandResult {
|
||||||
println!("accounts {:?}", token);
|
let accounts = config.rpc_client.get_token_accounts_by_owner(
|
||||||
|
&config.owner.pubkey(),
|
||||||
|
match token {
|
||||||
|
Some(token) => TokenAccountsFilter::Mint(token),
|
||||||
|
None => TokenAccountsFilter::ProgramId(spl_token::id()),
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
if accounts.is_empty() {
|
||||||
|
println!("None");
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Account Token Balance");
|
||||||
|
println!("-------------------------------------------------------------------------------------------------");
|
||||||
|
for (address, account) in accounts {
|
||||||
|
let balance = match config.rpc_client.get_token_account_balance(&address) {
|
||||||
|
Ok(response) => response,
|
||||||
|
Err(err) => 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("{:<44} {:<44} {}", address, account.lamports, balance);
|
||||||
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -482,7 +590,6 @@ fn main() {
|
||||||
)
|
)
|
||||||
.subcommand(SubCommand::with_name("create-token").about("Create a new token"))
|
.subcommand(SubCommand::with_name("create-token").about("Create a new token"))
|
||||||
.subcommand(SubCommand::with_name("create-bridge").about("Create a new bridge"))
|
.subcommand(SubCommand::with_name("create-bridge").about("Create a new bridge"))
|
||||||
.subcommand(SubCommand::with_name("airdrop").about("Request an airdrop"))
|
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("create-account")
|
SubCommand::with_name("create-account")
|
||||||
.about("Create a new token account")
|
.about("Create a new token account")
|
||||||
|
@ -549,6 +656,86 @@ fn main() {
|
||||||
.help("The token account address of recipient"),
|
.help("The token account address of recipient"),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
.subcommand(
|
||||||
|
SubCommand::with_name("approve")
|
||||||
|
.about("Approve token sprending")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("sender")
|
||||||
|
.validator(is_pubkey_or_keypair)
|
||||||
|
.value_name("SENDER_TOKEN_ACCOUNT_ADDRESS")
|
||||||
|
.takes_value(true)
|
||||||
|
.index(1)
|
||||||
|
.required(true)
|
||||||
|
.help("The token account address of the sender"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("amount")
|
||||||
|
.validator(is_amount)
|
||||||
|
.value_name("TOKEN_AMOUNT")
|
||||||
|
.takes_value(true)
|
||||||
|
.index(2)
|
||||||
|
.required(true)
|
||||||
|
.help("Amount to send, in tokens"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("recipient")
|
||||||
|
.validator(is_pubkey_or_keypair)
|
||||||
|
.value_name("RECIPIENT_TOKEN_ACCOUNT_ADDRESS")
|
||||||
|
.takes_value(true)
|
||||||
|
.index(3)
|
||||||
|
.required(true)
|
||||||
|
.help("The token account address of recipient"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.subcommand(
|
||||||
|
SubCommand::with_name("lock")
|
||||||
|
.about("Transfer tokens to another chain")
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("sender")
|
||||||
|
.validator(is_pubkey_or_keypair)
|
||||||
|
.value_name("SENDER_TOKEN_ACCOUNT_ADDRESS")
|
||||||
|
.takes_value(true)
|
||||||
|
.index(1)
|
||||||
|
.required(true)
|
||||||
|
.help("The token account address of the sender"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("token")
|
||||||
|
.validator(is_pubkey_or_keypair)
|
||||||
|
.value_name("TOKEN_ADDRESS")
|
||||||
|
.takes_value(true)
|
||||||
|
.index(2)
|
||||||
|
.required(true)
|
||||||
|
.help("The mint address"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("amount")
|
||||||
|
.validator(is_amount)
|
||||||
|
.value_name("AMOUNT")
|
||||||
|
.takes_value(true)
|
||||||
|
.index(3)
|
||||||
|
.required(true)
|
||||||
|
.help("Amount to transfer out"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("chain")
|
||||||
|
.validator(is_u8)
|
||||||
|
.value_name("CHAIN")
|
||||||
|
.takes_value(true)
|
||||||
|
.index(4)
|
||||||
|
.required(true)
|
||||||
|
.help("Chain to transfer to"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("nonce")
|
||||||
|
.validator(is_u8)
|
||||||
|
.value_name("NONCE")
|
||||||
|
.takes_value(true)
|
||||||
|
.index(5)
|
||||||
|
.required(true)
|
||||||
|
.help("Nonce of the transfer"),
|
||||||
|
),
|
||||||
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("burn")
|
SubCommand::with_name("burn")
|
||||||
.about("Burn tokens from an account")
|
.about("Burn tokens from an account")
|
||||||
|
@ -697,9 +884,16 @@ fn main() {
|
||||||
solana_logger::setup_with_default("solana=info");
|
solana_logger::setup_with_default("solana=info");
|
||||||
|
|
||||||
let _ = match matches.subcommand() {
|
let _ = match matches.subcommand() {
|
||||||
("airdrop", Some(_arg_matches)) => command_request_airdrop(&config),
|
|
||||||
("create-token", Some(_arg_matches)) => command_create_token(&config),
|
("create-token", Some(_arg_matches)) => command_create_token(&config),
|
||||||
("create-bridge", Some(_arg_matches)) => command_deploy_bridge(&config),
|
("create-bridge", Some(_arg_matches)) => command_deploy_bridge(&config),
|
||||||
|
("lock", Some(arg_matches)) => {
|
||||||
|
let account = pubkey_of(arg_matches, "sender").unwrap();
|
||||||
|
let amount = value_t_or_exit!(arg_matches, "amount", u64);
|
||||||
|
let nonce = value_t_or_exit!(arg_matches, "nonce", u32);
|
||||||
|
let chain = value_t_or_exit!(arg_matches, "chain", u8);
|
||||||
|
let token = pubkey_of(arg_matches, "token").unwrap();
|
||||||
|
command_lock_tokens(&config, account, token, amount, chain, [0; 32], nonce)
|
||||||
|
}
|
||||||
("create-account", Some(arg_matches)) => {
|
("create-account", Some(arg_matches)) => {
|
||||||
let token = pubkey_of(arg_matches, "token").unwrap();
|
let token = pubkey_of(arg_matches, "token").unwrap();
|
||||||
command_create_account(&config, token)
|
command_create_account(&config, token)
|
||||||
|
@ -715,6 +909,12 @@ fn main() {
|
||||||
let recipient = pubkey_of(arg_matches, "recipient").unwrap();
|
let recipient = pubkey_of(arg_matches, "recipient").unwrap();
|
||||||
command_transfer(&config, sender, amount, recipient)
|
command_transfer(&config, sender, amount, recipient)
|
||||||
}
|
}
|
||||||
|
("approve", Some(arg_matches)) => {
|
||||||
|
let sender = pubkey_of(arg_matches, "sender").unwrap();
|
||||||
|
let amount = value_t_or_exit!(arg_matches, "amount", f64);
|
||||||
|
let recipient = pubkey_of(arg_matches, "recipient").unwrap();
|
||||||
|
command_approve(&config, sender, amount, recipient)
|
||||||
|
}
|
||||||
("burn", Some(arg_matches)) => {
|
("burn", Some(arg_matches)) => {
|
||||||
let source = pubkey_of(arg_matches, "source").unwrap();
|
let source = pubkey_of(arg_matches, "source").unwrap();
|
||||||
let amount = value_t_or_exit!(arg_matches, "amount", f64);
|
let amount = value_t_or_exit!(arg_matches, "amount", f64);
|
||||||
|
@ -765,3 +965,17 @@ fn main() {
|
||||||
exit(1);
|
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
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue