Fixes for actual CPI usage and variable VAA size

This commit is contained in:
Hendrik Hofstadt 2020-08-17 21:01:52 +02:00
parent 760d5541b7
commit f8e70efb0b
6 changed files with 291 additions and 128 deletions

View File

@ -26,13 +26,14 @@ Parameters:
| Index | Name | Type | signer | writeable | empty | derived |
| ----- | -------- | ------------------- | ------ | --------- | ----- | ------- |
| 0 | sys | SystemProgram | | | | |
| 1 | token_program | SplToken | | | | |
| 2 | token_account | TokenAccount | | ✅ | | |
| 3 | bridge | BridgeConfig | | | | |
| 4 | proposal | TransferOutProposal | | ✅ | ✅ | ✅ |
| 5 | token | WrappedAsset | | ✅ | | ✅ |
| 6 | payer | Account | ✅ | | | |
| 0 | bridge_p | BridgeProgram | | | | |
| 1 | sys | SystemProgram | | | | |
| 2 | token_program | SplToken | | | | |
| 3 | token_account | TokenAccount | | ✅ | | |
| 4 | bridge | BridgeConfig | | | | |
| 5 | proposal | TransferOutProposal | | ✅ | ✅ | ✅ |
| 6 | token | WrappedAsset | | ✅ | | ✅ |
| 7 | payer | Account | ✅ | | | |
#### TransferOutNative
@ -43,14 +44,15 @@ The transfer proposal will be tracked at a new account `proposal` where a VAA wi
| Index | Name | Type | signer | writeable | empty | derived |
| ----- | --------------- | ------------------- | ------ | --------- | ----- | ------- |
| 0 | sys | SystemProgram | | | | |
| 1 | token_program | SplToken | | | | |
| 2 | token_account | TokenAccount | | ✅ | | |
| 3 | bridge | BridgeConfig | | | | |
| 4 | proposal | TransferOutProposal | | ✅ | ✅ | ✅ |
| 5 | token | Mint | | ✅ | | |
| 6 | payer | Account | ✅ | | | |
| 7 | custody_account | TokenAccount | | ✅ | opt | ✅ |
| 0 | bridge_p | BridgeProgram | | | | |
| 1 | sys | SystemProgram | | | | |
| 2 | token_program | SplToken | | | | |
| 3 | token_account | TokenAccount | | ✅ | | |
| 4 | bridge | BridgeConfig | | | | |
| 5 | proposal | TransferOutProposal | | ✅ | ✅ | ✅ |
| 6 | token | Mint | | ✅ | | |
| 7 | payer | Account | ✅ | | | |
| 8 | custody_account | TokenAccount | | ✅ | opt | ✅ |
#### EvictTransferOut
@ -58,10 +60,11 @@ Deletes a `proposal` after the `VAA_EXPIRATION_TIME` to free up space on chain.
| Index | Name | Type | signer | writeable | empty | derived |
| ----- | -------- | ------------------- | ------ | --------- | ----- | ------- |
| 0 | guardian | Account | ✅ | | | |
| 1 | clock | Sysvar | | | | ✅ |
| 2 | bridge | BridgeConfig | | | | |
| 3 | proposal | TransferOutProposal | | ✅ | | ✅ |
| 0 | bridge_p | BridgeProgram | | | | |
| 1 | guardian | Account | ✅ | | | |
| 2 | clock | Sysvar | | | | ✅ |
| 3 | bridge | BridgeConfig | | | | |
| 4 | proposal | TransferOutProposal | | ✅ | | ✅ |
#### EvictClaimedVAA
@ -69,10 +72,11 @@ Deletes a `ClaimedVAA` after the `VAA_EXPIRATION_TIME` to free up space on chain
| Index | Name | Type | signer | writeable | empty | derived |
| ----- | -------- | ------------------- | ------ | --------- | ----- | ------- |
| 0 | guardian | Account | ✅ | | | |
| 1 | clock | Sysvar | | | | ✅ |
| 2 | bridge | BridgeConfig | | | | |
| 3 | claim | ClaimedVAA | | ✅ | | ✅ |
| 0 | bridge_p | BridgeProgram | | | | |
| 1 | guardian | Account | ✅ | | | |
| 2 | clock | Sysvar | | | | ✅ |
| 3 | bridge | BridgeConfig | | | | |
| 4 | claim | ClaimedVAA | | ✅ | | ✅ |
#### CreateWrappedAsset
@ -80,12 +84,13 @@ Creates a new `WrappedAsset` to be used to create accounts and later receive tra
| Index | Name | Type | signer | writeable | empty | derived |
| ----- | -------- | ------------------- | ------ | --------- | ----- | ------- |
| 0 | sys | SystemProgram | | | | |
| 1 | token_program | SplToken | | | | |
| 2 | bridge | BridgeConfig | | | | |
| 3 | payer | Account | ✅ | | | |
| 4 | wrapped_mint | WrappedAsset | | | ✅ | ✅ |
| 5 | wrapped_meta_account | WrappedAssetMeta | | ✅ | ✅ | ✅ |
| 0 | bridge_p | BridgeProgram | | | | |
| 1 | sys | SystemProgram | | | | |
| 2 | token_program | SplToken | | | | |
| 3 | bridge | BridgeConfig | | | | |
| 4 | payer | Account | ✅ | | | |
| 5 | wrapped_mint | WrappedAsset | | | ✅ | ✅ |
| 6 | wrapped_meta_account | WrappedAssetMeta | | ✅ | ✅ | ✅ |
#### SubmitVAA
@ -97,12 +102,13 @@ All require:
| Index | Name | Type | signer | writeable | empty | derived |
| ----- | ------------ | ------------ | ------ | --------- | ----- | ------- |
| 0 | sys | SystemProgram | | | | |
| 1 | clock | Sysvar | | | | ✅ |
| 2 | bridge | BridgeConfig | | | | |
| 3 | guardian_set | GuardianSet | | | | |
| 4 | claim | ExecutedVAA | | ✅ | ✅ | ✅ |
| 5 | payer | Account | ✅ | | | |
| 0 | bridge_p | BridgeProgram | | | | |
| 1 | sys | SystemProgram | | | | |
| 2 | clock | Sysvar | | | | ✅ |
| 3 | bridge | BridgeConfig | | | | |
| 4 | guardian_set | GuardianSet | | | | |
| 5 | claim | ExecutedVAA | | ✅ | ✅ | ✅ |
| 6 | payer | Account | ✅ | | | |
followed by:
@ -110,30 +116,30 @@ followed by:
| Index | Name | Type | signer | writeable | empty | derived |
| ----- | ------------ | ------------------- | ------ | --------- | ----- | ------- |
| 6 | guardian_set_new | GuardianSet | | ✅ | ✅ | ✅ |
| 7 | guardian_set_new | GuardianSet | | ✅ | ✅ | ✅ |
##### Transfer: Ethereum (native) -> Solana (wrapped)
| Index | Name | Type | signer | writeable | empty | derived |
| ----- | ------------ | ------------ | ------ | --------- | ----- | ------- |
| 6 | token_program | SplToken | | | | |
| 7 | token | WrappedAsset | | | | ✅ |
| 8 | destination | TokenAccount | | ✅ | | |
| 7 | token_program | SplToken | | | | |
| 8 | token | WrappedAsset | | | | ✅ |
| 9 | destination | TokenAccount | | ✅ | | |
##### Transfer: Ethereum (wrapped) -> Solana (native)
| Index | Name | Type | signer | writeable | empty | derived |
| ----- | ------------ | ------------ | ------ | --------- | ----- | ------- |
| 6 | token_program | SplToken | | | | |
| 7 | token | Mint | | | | ✅ |
| 8 | destination | TokenAccount | | ✅ | opt | |
| 9 | custody_src | TokenAccount | | ✅ | | ✅ |
| 7 | token_program | SplToken | | | | |
| 8 | token | Mint | | | | ✅ |
| 9 | destination | TokenAccount | | ✅ | opt | |
| 10 | custody_src | TokenAccount | | ✅ | | ✅ |
##### Transfer: Solana (any) -> Ethereum (any)
| Index | Name | Type | signer | writeable | empty | derived |
| ----- | ------------ | ------------------- | ------ | --------- | ----- | ------- |
| 6 | out_proposal | TransferOutProposal | | ✅ | | ✅ |
| 7 | out_proposal | TransferOutProposal | | ✅ | | ✅ |
## Accounts

13
solana/Cargo.lock generated
View File

@ -2579,19 +2579,6 @@ dependencies = [
"thiserror",
]
[[package]]
name = "spl-token"
version = "1.0.8"
dependencies = [
"cbindgen",
"num-derive 0.3.1",
"num-traits",
"rand",
"remove_dir_all",
"solana-sdk",
"thiserror",
]
[[package]]
name = "spl-token"
version = "1.0.8"

View File

@ -21,14 +21,11 @@ use crate::vaa::{VAABody, VAA};
/// chain id of this chain
pub const CHAIN_ID_SOLANA: u8 = 1;
/// size of a VAA in bytes
const VAA_SIZE: usize = 200;
/// size of a foreign address in bytes
const FOREIGN_ADDRESS_SIZE: usize = 32;
/// length-prefixed serialized validator payment approval data
pub type VAAData = [u8; VAA_SIZE];
pub type VAAData = Vec<u8>;
/// X and Y point of P for guardians
pub type GuardianKey = [u8; 64];
/// address on a foreign chain
@ -129,9 +126,8 @@ impl BridgeInstruction {
TransferOut(*payload)
}
2 => {
let payload: &VAAData = unpack(input)?;
PostVAA(*payload)
let payload: VAAData = input[1..].to_vec();
PostVAA(payload)
}
5 => {
let payload: &AssetMeta = unpack(input)?;
@ -307,22 +303,10 @@ pub fn transfer_out(
pub fn post_vaa(
program_id: &Pubkey,
payer: &Pubkey,
v: &[u8],
v: VAAData,
) -> Result<Instruction, ProgramError> {
// VAA must be <= VAA_SIZE-1 to allow for the length prefix
if v.len() > VAA_SIZE - 1 {
return Err(VAATooLong.into());
}
// Convert data to length-prefixed on-chain format
let mut vaa_data: Vec<u8> = vec![];
vaa_data.push(v.len() as u8);
vaa_data.append(&mut v.to_vec());
vaa_data.resize(200, 0);
let mut vaa_chain: [u8; 200] = [0; 200];
vaa_chain.copy_from_slice(vaa_data.as_slice());
let data = BridgeInstruction::PostVAA(vaa_chain).serialize()?;
let mut data = v.clone();
data.insert(0, 2);
// Parse VAA
let vaa = VAA::deserialize(&v[..])?;
@ -350,6 +334,7 @@ pub fn post_vaa(
}
VAABody::Transfer(t) => {
if t.source_chain == CHAIN_ID_SOLANA {
println!("kot");
// Solana (any) -> Ethereum (any)
let transfer_key = Bridge::derive_transfer_id(
program_id,
@ -363,6 +348,7 @@ pub fn post_vaa(
)?;
accounts.push(AccountMeta::new(transfer_key, false))
} else if t.asset.chain == CHAIN_ID_SOLANA {
println!("kat");
// Foreign (wrapped) -> Solana (native)
let mint_key = Pubkey::new(&t.asset.address);
let custody_key = Bridge::derive_custody_id(program_id, &bridge_key, &mint_key)?;
@ -371,6 +357,7 @@ pub fn post_vaa(
accounts.push(AccountMeta::new(Pubkey::new(&t.target_address), false));
accounts.push(AccountMeta::new(custody_key, false));
} else {
println!("kit");
// Foreign (native) -> Solana (wrapped)
let wrapped_key = Bridge::derive_wrapped_asset_id(
program_id,

View File

@ -51,9 +51,7 @@ impl Bridge {
}
PostVAA(vaa_body) => {
info!("Instruction: PostVAA");
let len = vaa_body[0] as usize;
let vaa_data = &vaa_body[..len];
let vaa = VAA::deserialize(vaa_data)?;
let vaa = VAA::deserialize(&vaa_body)?;
let hash = vaa.body_hash()?;
@ -147,7 +145,6 @@ impl Bridge {
let transfer_info = next_account_info(account_info_iter)?;
let mint_info = next_account_info(account_info_iter)?;
let payer_info = next_account_info(account_info_iter)?;
let authority_info = next_account_info(account_info_iter)?;
let sender = Bridge::token_account_deserialize(sender_account_info)?;
let bridge = Bridge::bridge_deserialize(bridge_info)?;
@ -175,10 +172,10 @@ impl Bridge {
t.asset.address,
t.chain_id,
t.target,
sender.owner.to_bytes(),
sender_account_info.key.to_bytes(),
t.nonce,
);
Bridge::check_and_create_account::<TransferOutProposal>(
Bridge::check_and_create_account::<[u8; TRANSFER_OUT_PROPOSAL_SIZE]>(
program_id,
accounts,
transfer_info.key,
@ -188,11 +185,7 @@ impl Bridge {
)?;
// Load transfer account
let mut transfer_data = transfer_info.data.borrow_mut();
let transfer: &mut TransferOutProposal = Bridge::unpack_unchecked(&mut transfer_data)?;
if transfer.is_initialized {
return Err(Error::AlreadyExists.into());
}
let mut transfer: TransferOutProposal = TransferOutProposal::default();
info!("burning");
// Burn tokens
@ -200,7 +193,6 @@ impl Bridge {
program_id,
accounts,
&bridge.config.token_program,
authority_info.key,
sender_account_info.key,
t.amount,
)?;
@ -213,6 +205,11 @@ impl Bridge {
transfer.amount = t.amount;
transfer.to_chain_id = t.chain_id;
transfer.asset = t.asset;
let mut transfer_data = Self::transfer_out_proposal_serialize(&transfer)?;
transfer_info
.data
.borrow_mut()
.swap_with_slice(transfer_data.as_mut_slice());
Ok(())
}
@ -307,7 +304,7 @@ impl Bridge {
sender_account_info.key.to_bytes(),
t.nonce,
);
Bridge::check_and_create_account::<TransferOutProposal>(
Bridge::check_and_create_account::<[u8; TRANSFER_OUT_PROPOSAL_SIZE]>(
program_id,
accounts,
transfer_info.key,
@ -317,11 +314,7 @@ impl Bridge {
)?;
// Load transfer account
let mut transfer_data = transfer_info.data.borrow_mut();
let transfer: &mut TransferOutProposal = Bridge::unpack_unchecked(&mut transfer_data)?;
if transfer.is_initialized {
return Err(Error::AlreadyExists.into());
}
let mut transfer = TransferOutProposal::default();
// Check that custody account was derived correctly
let expected_custody_id =
@ -376,6 +369,12 @@ impl Bridge {
address: mint_info.key.to_bytes(),
};
let mut transfer_data = Self::transfer_out_proposal_serialize(&transfer)?;
transfer_info
.data
.borrow_mut()
.swap_with_slice(transfer_data.as_mut_slice());
Ok(())
}
@ -585,7 +584,6 @@ impl Bridge {
program_id,
accounts,
&bridge.config.token_program,
bridge_info.key,
custody_info.key,
destination_info.key,
b.amount,
@ -610,7 +608,6 @@ impl Bridge {
&bridge.config.token_program,
mint_info.key,
destination_info.key,
bridge_info.key,
b.amount,
)?;
}
@ -653,6 +650,12 @@ impl Bridge {
proposal.vaa = vaa_data;
proposal.vaa_time = vaa.timestamp;
let mut transfer_data = Self::transfer_out_proposal_serialize(&proposal)?;
proposal_info
.data
.borrow_mut()
.swap_with_slice(transfer_data.as_mut_slice());
Ok(())
}
}
@ -676,14 +679,7 @@ pub fn invoke_signed<'a>(
for account_info in account_infos.iter() {
if meta.pubkey == *account_info.key {
let mut new_account_info = account_info.clone();
for seeds in signers_seeds.iter() {
let signer =
solana_sdk::program::create_program_address(seeds, &WORMHOLE_PROGRAM_ID)
.unwrap();
if *account_info.key == signer {
new_account_info.is_signer = true;
}
}
for seeds in signers_seeds.iter() {}
new_account_infos.push(new_account_info);
}
}
@ -706,19 +702,14 @@ impl Bridge {
program_id: &Pubkey,
accounts: &[AccountInfo],
token_program_id: &Pubkey,
authority: &Pubkey,
token_account: &Pubkey,
amount: U256,
) -> Result<(), ProgramError> {
let all_signers: Vec<&Pubkey> = accounts
.iter()
.filter_map(|item| if item.is_signer { Some(item.key) } else { None })
.collect();
let ix = spl_token::instruction::burn(
token_program_id,
token_account,
authority,
all_signers.as_slice(),
&Self::derive_bridge_id(program_id)?,
&[],
amount.as_u64(),
)?;
Self::invoke_as_bridge(program_id, &ix, accounts)
@ -731,14 +722,13 @@ impl Bridge {
token_program_id: &Pubkey,
mint: &Pubkey,
destination: &Pubkey,
bridge: &Pubkey,
amount: U256,
) -> Result<(), ProgramError> {
let ix = spl_token::instruction::mint_to(
token_program_id,
mint,
destination,
bridge,
&Self::derive_bridge_id(program_id)?,
&[],
amount.as_u64(),
)?;
@ -771,7 +761,6 @@ impl Bridge {
program_id: &Pubkey,
accounts: &[AccountInfo],
token_program_id: &Pubkey,
bridge: &Pubkey,
source: &Pubkey,
destination: &Pubkey,
amount: U256,
@ -780,7 +769,7 @@ impl Bridge {
token_program_id,
source,
destination,
bridge,
&Self::derive_bridge_id(program_id)?,
&[],
amount.as_u64(),
)?;

View File

@ -1,7 +1,10 @@
//! Bridge transition types
use std::io::{Cursor, Read, Write};
use std::mem::size_of;
use std::ops::Deref;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use primitive_types::U256;
use solana_sdk::hash::Hasher;
use solana_sdk::pubkey::{PubkeyError, MAX_SEED_LEN};
@ -46,8 +49,7 @@ impl IsInitialized for GuardianSet {
}
/// proposal to transfer tokens to a foreign chain
#[repr(C)]
#[derive(Clone, Copy)]
#[derive(Default)]
pub struct TransferOutProposal {
/// amount to transfer
pub amount: U256,
@ -62,7 +64,7 @@ pub struct TransferOutProposal {
/// nonce of the transfer
pub nonce: u32,
/// vaa to unlock the tokens on the foreign chain
pub vaa: VAAData,
pub vaa: Vec<u8>,
/// time the vaa was submitted
pub vaa_time: u32,
@ -76,6 +78,7 @@ impl IsInitialized for TransferOutProposal {
}
}
pub const TRANSFER_OUT_PROPOSAL_SIZE: usize = 1139;
impl TransferOutProposal {
pub fn matches_vaa(&self, b: &BodyTransfer) -> bool {
return b.amount == self.amount
@ -203,8 +206,55 @@ impl Bridge {
pub fn transfer_out_proposal_deserialize(
info: &AccountInfo,
) -> Result<TransferOutProposal, Error> {
Ok(*Bridge::unpack(&mut info.data.borrow_mut())
.map_err(|_| Error::ExpectedTransferOutProposal)?)
let raw_data = info.data.borrow();
let mut rdr = Cursor::new(raw_data.as_ref());
let mut proposal = TransferOutProposal::default();
proposal.is_initialized = rdr.read_u8()? == 1;
let mut amount_data = [0u8; 32];
rdr.read_exact(&mut amount_data)?;
let amount = U256::from_big_endian(&amount_data[..]);
proposal.amount = amount;
proposal.nonce = rdr.read_u32::<BigEndian>()?;
proposal.vaa_time = rdr.read_u32::<BigEndian>()?;
proposal.to_chain_id = rdr.read_u8()?;
rdr.read_exact(&mut proposal.source_address)?;
rdr.read_exact(&mut proposal.foreign_address)?;
proposal.asset.chain = rdr.read_u8()?;
rdr.read_exact(&mut proposal.asset.address)?;
rdr.read_to_end(&mut proposal.vaa)?;
return Ok(proposal);
}
/// Deserializes a `TransferOutProposal`.
pub fn transfer_out_proposal_serialize(data: &TransferOutProposal) -> Result<Vec<u8>, Error> {
let mut out_data = Vec::new();
out_data.write_u8({
if data.is_initialized {
1
} else {
0
}
})?;
let mut amount_data = [0u8; 32];
data.amount.to_big_endian(&mut amount_data);
out_data.write(&amount_data)?;
out_data.write_u32::<BigEndian>(data.nonce)?;
out_data.write_u32::<BigEndian>(data.vaa_time)?;
out_data.write_u8(data.to_chain_id)?;
out_data.write(&data.source_address)?;
out_data.write(&data.foreign_address)?;
out_data.write_u8(data.asset.chain)?;
out_data.write(&data.asset.address)?;
let mut vaa_res = data.vaa.clone();
vaa_res.resize(1000, 0u8);
out_data.write(&vaa_res)?;
return Ok(out_data);
}
/// Unpacks a state from a bytes buffer while assuring that the state is initialized.

View File

@ -46,7 +46,11 @@ struct Config {
type Error = Box<dyn std::error::Error>;
type CommmandResult = Result<Option<Transaction>, Error>;
fn command_deploy_bridge(config: &Config, bridge: &Pubkey) -> CommmandResult {
fn command_deploy_bridge(
config: &Config,
bridge: &Pubkey,
initial_guardian: Vec<[u8; 20]>,
) -> CommmandResult {
println!("Deploying bridge program");
let minimum_balance_for_rent_exemption = config
@ -56,7 +60,7 @@ fn command_deploy_bridge(config: &Config, bridge: &Pubkey) -> CommmandResult {
let ix = initialize(
bridge,
&config.owner.pubkey(),
vec![[0u8; 20]],
initial_guardian,
&BridgeConfig {
vaa_expiration_time: 200000000,
token_program: spl_token::id(),
@ -104,7 +108,7 @@ fn command_lock_tokens(
chain: wrapped_meta.chain,
}
}
Err(_) => AssetMeta {
Err(e) => AssetMeta {
address: token.to_bytes(),
chain: CHAIN_ID_SOLANA,
},
@ -152,6 +156,30 @@ fn command_lock_tokens(
Ok(Some(transaction))
}
fn command_create_wrapped_asset(
config: &Config,
bridge: &Pubkey,
meta: AssetMeta,
) -> CommmandResult {
println!("Creating wrapped asset");
let minimum_balance_for_rent_exemption = config
.rpc_client
.get_minimum_balance_for_rent_exemption(size_of::<Mint>())?;
let ix = create_wrapped(bridge, &config.owner.pubkey(), meta)?;
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,
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, bridge: &Pubkey, vaa: &[u8]) -> CommmandResult {
println!("Submitting VAA");
@ -159,7 +187,7 @@ fn command_submit_vaa(config: &Config, bridge: &Pubkey, vaa: &[u8]) -> CommmandR
.rpc_client
.get_minimum_balance_for_rent_exemption(size_of::<Mint>())?;
let ix = post_vaa(bridge, &config.owner.pubkey(), vaa)?;
let ix = post_vaa(bridge, &config.owner.pubkey(), vaa.to_vec())?;
let mut transaction = Transaction::new_with_payer(&[ix], Some(&config.fee_payer.pubkey()));
@ -828,6 +856,16 @@ fn main() {
.help(
"Specify the bridge program public key"
),
)
.arg(
Arg::with_name("guardian")
.validator(is_hex)
.value_name("GUADIAN_ADDRESS")
.takes_value(true)
.index(2)
.required(true)
.help("Address of the initial guardian"),
))
.subcommand(
SubCommand::with_name("lock")
@ -915,6 +953,74 @@ fn main() {
.help("The vaa to be posted"),
)
)
.subcommand(
SubCommand::with_name("create-wrapped")
.about("Create new wrapped asset and token account")
.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("chain")
.validator(is_u8)
.value_name("CHAIN")
.takes_value(true)
.index(2)
.required(true)
.help("Chain ID of the asset"),
)
.arg(
Arg::with_name("token")
.validator(is_hex)
.value_name("TOKEN_ADDRESS")
.takes_value(true)
.index(3)
.required(true)
.help("Token address of the asset"),
)
)
.subcommand(
SubCommand::with_name("wrapped-address")
.about("Derive wrapped asset address")
.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("chain")
.validator(is_u8)
.value_name("CHAIN")
.takes_value(true)
.index(2)
.required(true)
.help("Chain ID of the asset"),
)
.arg(
Arg::with_name("token")
.validator(is_hex)
.value_name("TOKEN_ADDRESS")
.takes_value(true)
.index(3)
.required(true)
.help("Token address of the asset"),
)
)
.get_matches();
let config = {
@ -999,7 +1105,12 @@ fn main() {
}
("create-bridge", Some(arg_matches)) => {
let bridge = pubkey_of(arg_matches, "bridge").unwrap();
command_deploy_bridge(&config, &bridge)
let initial_guardian: String = value_of(arg_matches, "guardian").unwrap();
let initial_data = hex::decode(initial_guardian).unwrap();
let mut guardian = [0u8; 20];
guardian.copy_from_slice(&initial_data);
command_deploy_bridge(&config, &bridge, vec![guardian])
}
("lock", Some(arg_matches)) => {
let bridge = pubkey_of(arg_matches, "bridge").unwrap();
@ -1018,6 +1129,39 @@ fn main() {
let vaa = hex::decode(vaa_string).unwrap();
command_submit_vaa(&config, &bridge, vaa.as_slice())
}
("create-wrapped", Some(arg_matches)) => {
let bridge = pubkey_of(arg_matches, "bridge").unwrap();
let chain = value_t_or_exit!(arg_matches, "chain", u8);
let addr_string: String = value_of(arg_matches, "token").unwrap();
let addr_data = hex::decode(addr_string).unwrap();
let mut token_addr = [0u8; 32];
token_addr.copy_from_slice(addr_data.as_slice());
command_create_wrapped_asset(
&config,
&bridge,
AssetMeta {
chain,
address: token_addr,
},
)
}
("wrapped-address", Some(arg_matches)) => {
let bridge = pubkey_of(arg_matches, "bridge").unwrap();
let chain = value_t_or_exit!(arg_matches, "chain", u8);
let addr_string: String = value_of(arg_matches, "token").unwrap();
let addr_data = hex::decode(addr_string).unwrap();
let mut token_addr = [0u8; 32];
token_addr.copy_from_slice(addr_data.as_slice());
let bridge_key = Bridge::derive_bridge_id(&bridge).unwrap();
let wrapped_key =
Bridge::derive_wrapped_asset_id(&bridge, &bridge_key, chain, token_addr).unwrap();
println!("Wrapped address: {}", wrapped_key);
return;
}
_ => unreachable!(),
}
.and_then(|transaction| {