Align behaviour of persistent message fees
Change-Id: Ic9c6c40dbac2399e0eaf3a861dff33254a828a18
This commit is contained in:
parent
0f854dc08b
commit
7784e74725
|
@ -112,7 +112,9 @@ Module [32]byte = "Core"
|
||||||
Action uint16 = 3
|
Action uint16 = 3
|
||||||
Chain uint16
|
Chain uint16
|
||||||
// Message fee in the native token
|
// Message fee in the native token
|
||||||
Fee uint64
|
Fee uint256
|
||||||
|
// Persistent message fee in the native token
|
||||||
|
FeePersistent uint256
|
||||||
```
|
```
|
||||||
|
|
||||||
TransferFees:
|
TransferFees:
|
||||||
|
@ -124,7 +126,7 @@ Module [32]byte = "Core"
|
||||||
Action uint16 = 4
|
Action uint16 = 4
|
||||||
Chain uint16
|
Chain uint16
|
||||||
// Amount being transferred (big-endian uint256)
|
// Amount being transferred (big-endian uint256)
|
||||||
Amount [32]uint8
|
Amount uint256
|
||||||
// Address of the recipient. Left-zero-padded if shorter than 32 bytes
|
// Address of the recipient. Left-zero-padded if shorter than 32 bytes
|
||||||
To [32]uint8
|
To [32]uint8
|
||||||
```
|
```
|
||||||
|
|
|
@ -79,6 +79,7 @@ fn command_deploy_bridge(
|
||||||
initial_guardians: Vec<[u8; 20]>,
|
initial_guardians: Vec<[u8; 20]>,
|
||||||
guardian_expiration: u32,
|
guardian_expiration: u32,
|
||||||
message_fee: u64,
|
message_fee: u64,
|
||||||
|
message_fee_persistent: u64,
|
||||||
) -> CommmandResult {
|
) -> CommmandResult {
|
||||||
println!("Initializing Wormhole bridge {}", bridge);
|
println!("Initializing Wormhole bridge {}", bridge);
|
||||||
|
|
||||||
|
@ -90,6 +91,7 @@ fn command_deploy_bridge(
|
||||||
*bridge,
|
*bridge,
|
||||||
config.owner.pubkey(),
|
config.owner.pubkey(),
|
||||||
message_fee,
|
message_fee,
|
||||||
|
message_fee_persistent,
|
||||||
guardian_expiration,
|
guardian_expiration,
|
||||||
initial_guardians.as_slice(),
|
initial_guardians.as_slice(),
|
||||||
)
|
)
|
||||||
|
@ -122,12 +124,19 @@ fn command_post_message(
|
||||||
None, bridge,
|
None, bridge,
|
||||||
))?;
|
))?;
|
||||||
let bridge_config = BridgeData::try_from_slice(bridge_config_account.data.as_slice())?;
|
let bridge_config = BridgeData::try_from_slice(bridge_config_account.data.as_slice())?;
|
||||||
println!("Message fee: {} lamports", bridge_config.config.fee);
|
let fee = {
|
||||||
|
if persist {
|
||||||
|
bridge_config.config.fee_persistent
|
||||||
|
} else {
|
||||||
|
bridge_config.config.fee
|
||||||
|
}
|
||||||
|
};
|
||||||
|
println!("Message fee: {} lamports", fee);
|
||||||
|
|
||||||
let transfer_ix = transfer(
|
let transfer_ix = transfer(
|
||||||
&config.owner.pubkey(),
|
&config.owner.pubkey(),
|
||||||
&FeeCollector::key(None, bridge),
|
&FeeCollector::key(None, bridge),
|
||||||
bridge_config.config.fee,
|
fee,
|
||||||
);
|
);
|
||||||
let (_, ix) = bridge::instructions::post_message(
|
let (_, ix) = bridge::instructions::post_message(
|
||||||
*bridge,
|
*bridge,
|
||||||
|
@ -237,6 +246,15 @@ fn main() {
|
||||||
.index(4)
|
.index(4)
|
||||||
.required(true)
|
.required(true)
|
||||||
.help("Initial message posting fee"),
|
.help("Initial message posting fee"),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("message_fee_persistent")
|
||||||
|
.validator(is_u64)
|
||||||
|
.value_name("MESSAGE_FEE_PERSISTENT")
|
||||||
|
.takes_value(true)
|
||||||
|
.index(5)
|
||||||
|
.required(true)
|
||||||
|
.help("Initial persistent message posting fee"),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
|
@ -315,6 +333,7 @@ fn main() {
|
||||||
let guardian_expiration: u32 =
|
let guardian_expiration: u32 =
|
||||||
value_of(arg_matches, "guardian_set_expiration").unwrap();
|
value_of(arg_matches, "guardian_set_expiration").unwrap();
|
||||||
let msg_fee: u64 = value_of(arg_matches, "message_fee").unwrap();
|
let msg_fee: u64 = value_of(arg_matches, "message_fee").unwrap();
|
||||||
|
let msg_fee_persistent: u64 = value_of(arg_matches, "message_fee_persistent").unwrap();
|
||||||
|
|
||||||
let mut guardian = [0u8; 20];
|
let mut guardian = [0u8; 20];
|
||||||
guardian.copy_from_slice(&initial_data);
|
guardian.copy_from_slice(&initial_data);
|
||||||
|
@ -324,6 +343,7 @@ fn main() {
|
||||||
vec![guardian],
|
vec![guardian],
|
||||||
guardian_expiration,
|
guardian_expiration,
|
||||||
msg_fee,
|
msg_fee,
|
||||||
|
msg_fee_persistent,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
("post-message", Some(arg_matches)) => {
|
("post-message", Some(arg_matches)) => {
|
||||||
|
|
|
@ -170,8 +170,8 @@ pub struct SetFeesData {}
|
||||||
pub fn set_fees(ctx: &ExecutionContext, accs: &mut SetFees, _data: SetFeesData) -> Result<()> {
|
pub fn set_fees(ctx: &ExecutionContext, accs: &mut SetFees, _data: SetFeesData) -> Result<()> {
|
||||||
accs.vaa.claim(ctx, accs.payer.key)?;
|
accs.vaa.claim(ctx, accs.payer.key)?;
|
||||||
|
|
||||||
// Set expiration time for the old set
|
accs.bridge.config.fee = accs.vaa.fee.as_u64();
|
||||||
accs.bridge.config.fee = accs.vaa.fee;
|
accs.bridge.config.fee_persistent = accs.vaa.persisted_fee.as_u64();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,9 @@ pub struct InitializeData {
|
||||||
/// Amount of lamports that needs to be paid to the protocol to post a message
|
/// Amount of lamports that needs to be paid to the protocol to post a message
|
||||||
pub fee: u64,
|
pub fee: u64,
|
||||||
|
|
||||||
|
/// Amount of lamports that needs to be paid to the protocol to post a message
|
||||||
|
pub fee_persistent: u64,
|
||||||
|
|
||||||
/// Initial Guardian Set
|
/// Initial Guardian Set
|
||||||
pub initial_guardians: Vec<[u8; 20]>,
|
pub initial_guardians: Vec<[u8; 20]>,
|
||||||
}
|
}
|
||||||
|
@ -71,6 +74,7 @@ pub fn initialize(
|
||||||
accs.bridge.config = BridgeConfig {
|
accs.bridge.config = BridgeConfig {
|
||||||
guardian_set_expiration_time: data.guardian_set_expiration_time,
|
guardian_set_expiration_time: data.guardian_set_expiration_time,
|
||||||
fee: data.fee,
|
fee: data.fee,
|
||||||
|
fee_persistent: data.fee_persistent,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initialize the fee collector account so it's rent exempt and will keep funds
|
// Initialize the fee collector account so it's rent exempt and will keep funds
|
||||||
|
|
|
@ -96,6 +96,13 @@ pub fn post_message(
|
||||||
accs.message
|
accs.message
|
||||||
.verify_derivation(ctx.program_id, &msg_derivation)?;
|
.verify_derivation(ctx.program_id, &msg_derivation)?;
|
||||||
|
|
||||||
|
let fee = {
|
||||||
|
if data.persist {
|
||||||
|
accs.bridge.config.fee_persistent
|
||||||
|
} else {
|
||||||
|
accs.bridge.config.fee
|
||||||
|
}
|
||||||
|
};
|
||||||
// Fee handling, checking previously known balance allows us to not care who is the payer of
|
// Fee handling, checking previously known balance allows us to not care who is the payer of
|
||||||
// this submission.
|
// this submission.
|
||||||
if accs
|
if accs
|
||||||
|
@ -103,11 +110,11 @@ pub fn post_message(
|
||||||
.lamports()
|
.lamports()
|
||||||
.checked_sub(accs.bridge.last_lamports)
|
.checked_sub(accs.bridge.last_lamports)
|
||||||
.ok_or(MathOverflow)?
|
.ok_or(MathOverflow)?
|
||||||
< accs.bridge.config.fee
|
< fee
|
||||||
{
|
{
|
||||||
trace!(
|
trace!(
|
||||||
"Expected fee not found: fee, last_lamports, collector: {} {} {}",
|
"Expected fee not found: fee, last_lamports, collector: {} {} {}",
|
||||||
accs.bridge.config.fee,
|
fee,
|
||||||
accs.bridge.last_lamports,
|
accs.bridge.last_lamports,
|
||||||
accs.fee_collector.lamports(),
|
accs.fee_collector.lamports(),
|
||||||
);
|
);
|
||||||
|
|
|
@ -29,9 +29,9 @@ use byteorder::{
|
||||||
};
|
};
|
||||||
use sha3::Digest;
|
use sha3::Digest;
|
||||||
use solana_program::{
|
use solana_program::{
|
||||||
|
msg,
|
||||||
program_error::ProgramError,
|
program_error::ProgramError,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
msg,
|
|
||||||
};
|
};
|
||||||
use solitaire::{
|
use solitaire::{
|
||||||
processors::seeded::Seeded,
|
processors::seeded::Seeded,
|
||||||
|
|
|
@ -44,6 +44,7 @@ pub fn initialize(
|
||||||
program_id: Pubkey,
|
program_id: Pubkey,
|
||||||
payer: Pubkey,
|
payer: Pubkey,
|
||||||
fee: u64,
|
fee: u64,
|
||||||
|
fee_persistent: u64,
|
||||||
guardian_set_expiration_time: u32,
|
guardian_set_expiration_time: u32,
|
||||||
initial_guardians: &[[u8; 20]],
|
initial_guardians: &[[u8; 20]],
|
||||||
) -> solitaire::Result<Instruction> {
|
) -> solitaire::Result<Instruction> {
|
||||||
|
@ -67,6 +68,7 @@ pub fn initialize(
|
||||||
data: crate::instruction::Instruction::Initialize(InitializeData {
|
data: crate::instruction::Instruction::Initialize(InitializeData {
|
||||||
initial_guardians: initial_guardians.to_vec(),
|
initial_guardians: initial_guardians.to_vec(),
|
||||||
fee,
|
fee,
|
||||||
|
fee_persistent,
|
||||||
guardian_set_expiration_time,
|
guardian_set_expiration_time,
|
||||||
})
|
})
|
||||||
.try_to_vec()?,
|
.try_to_vec()?,
|
||||||
|
|
|
@ -74,6 +74,8 @@ pub struct BridgeConfig {
|
||||||
|
|
||||||
/// Amount of lamports that needs to be paid to the protocol to post a message
|
/// Amount of lamports that needs to be paid to the protocol to post a message
|
||||||
pub fee: u64,
|
pub fee: u64,
|
||||||
|
/// Amount of lamports that needs to be paid to the protocol to post a persistent message
|
||||||
|
pub fee_persistent: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, BorshSerialize, BorshDeserialize)]
|
#[derive(Default, BorshSerialize, BorshDeserialize)]
|
||||||
|
@ -220,34 +222,6 @@ impl Owned for ClaimData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GovernancePayloadUpgrade {
|
|
||||||
// Address of the new Implementation
|
|
||||||
pub new_contract: Pubkey,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DeserializePayload for GovernancePayloadUpgrade
|
|
||||||
where
|
|
||||||
Self: DeserializeGovernancePayload,
|
|
||||||
{
|
|
||||||
fn deserialize(buf: &mut &[u8]) -> Result<Self, SolitaireError> {
|
|
||||||
let mut c = Cursor::new(buf);
|
|
||||||
|
|
||||||
Self::check_governance_header(&mut c)?;
|
|
||||||
|
|
||||||
let mut addr = [0u8; 32];
|
|
||||||
c.read_exact(&mut addr)?;
|
|
||||||
|
|
||||||
Ok(GovernancePayloadUpgrade {
|
|
||||||
new_contract: Pubkey::new(&addr[..]),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DeserializeGovernancePayload for GovernancePayloadUpgrade {
|
|
||||||
const MODULE: &'static str = "CORE";
|
|
||||||
const ACTION: u8 = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct GovernancePayloadGuardianSetChange {
|
pub struct GovernancePayloadGuardianSetChange {
|
||||||
// New GuardianSetIndex
|
// New GuardianSetIndex
|
||||||
pub new_guardian_set_index: u32,
|
pub new_guardian_set_index: u32,
|
||||||
|
@ -262,7 +236,7 @@ impl SerializePayload for GovernancePayloadGuardianSetChange {
|
||||||
v.write_u32::<BigEndian>(self.new_guardian_set_index)?;
|
v.write_u32::<BigEndian>(self.new_guardian_set_index)?;
|
||||||
v.write_u8(self.new_guardian_set.len() as u8)?;
|
v.write_u8(self.new_guardian_set.len() as u8)?;
|
||||||
for key in self.new_guardian_set.iter() {
|
for key in self.new_guardian_set.iter() {
|
||||||
v.write(key);
|
v.write(key)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -297,15 +271,52 @@ impl DeserializeGovernancePayload for GovernancePayloadGuardianSetChange {
|
||||||
const ACTION: u8 = 1;
|
const ACTION: u8 = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct GovernancePayloadUpgrade {
|
||||||
|
// Address of the new Implementation
|
||||||
|
pub new_contract: Pubkey,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DeserializePayload for GovernancePayloadUpgrade
|
||||||
|
where
|
||||||
|
Self: DeserializeGovernancePayload,
|
||||||
|
{
|
||||||
|
fn deserialize(buf: &mut &[u8]) -> Result<Self, SolitaireError> {
|
||||||
|
let mut c = Cursor::new(buf);
|
||||||
|
|
||||||
|
Self::check_governance_header(&mut c)?;
|
||||||
|
|
||||||
|
let mut addr = [0u8; 32];
|
||||||
|
c.read_exact(&mut addr)?;
|
||||||
|
|
||||||
|
Ok(GovernancePayloadUpgrade {
|
||||||
|
new_contract: Pubkey::new(&addr[..]),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DeserializeGovernancePayload for GovernancePayloadUpgrade {
|
||||||
|
const MODULE: &'static str = "CORE";
|
||||||
|
const ACTION: u8 = 2;
|
||||||
|
}
|
||||||
|
|
||||||
pub struct GovernancePayloadSetMessageFee {
|
pub struct GovernancePayloadSetMessageFee {
|
||||||
// New fee in lamports
|
// New fee in lamports
|
||||||
pub fee: u64,
|
pub fee: U256,
|
||||||
|
// New fee for persisted messages in lamports
|
||||||
|
pub persisted_fee: U256,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SerializePayload for GovernancePayloadSetMessageFee {
|
impl SerializePayload for GovernancePayloadSetMessageFee {
|
||||||
fn serialize<W: Write>(&self, v: &mut W) -> std::result::Result<(), SolitaireError> {
|
fn serialize<W: Write>(&self, v: &mut W) -> std::result::Result<(), SolitaireError> {
|
||||||
use byteorder::WriteBytesExt;
|
use byteorder::WriteBytesExt;
|
||||||
v.write_u64::<BigEndian>(self.fee)?;
|
let mut fee_data = [0u8; 32];
|
||||||
|
self.fee.to_big_endian(&mut fee_data);
|
||||||
|
v.write(&fee_data[..])?;
|
||||||
|
|
||||||
|
let mut fee_persistent_data = [0u8; 32];
|
||||||
|
self.persisted_fee.to_big_endian(&mut fee_persistent_data);
|
||||||
|
v.write(&fee_persistent_data[..])?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -317,8 +328,18 @@ where
|
||||||
fn deserialize(buf: &mut &[u8]) -> Result<Self, SolitaireError> {
|
fn deserialize(buf: &mut &[u8]) -> Result<Self, SolitaireError> {
|
||||||
let mut c = Cursor::new(buf);
|
let mut c = Cursor::new(buf);
|
||||||
|
|
||||||
let fee = c.read_u64::<BigEndian>()?;
|
let mut fee_data: [u8; 32] = [0; 32];
|
||||||
Ok(GovernancePayloadSetMessageFee { fee })
|
c.read_exact(&mut fee_data)?;
|
||||||
|
let fee = U256::from_big_endian(&fee_data);
|
||||||
|
|
||||||
|
let mut fee_persisted_data: [u8; 32] = [0; 32];
|
||||||
|
c.read_exact(&mut fee_persisted_data)?;
|
||||||
|
let fee_persisted = U256::from_big_endian(&fee_persisted_data);
|
||||||
|
|
||||||
|
Ok(GovernancePayloadSetMessageFee {
|
||||||
|
fee,
|
||||||
|
persisted_fee: fee_persisted,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,8 @@ use secp256k1::{
|
||||||
};
|
};
|
||||||
use sha3::Digest;
|
use sha3::Digest;
|
||||||
use solana_client::{
|
use solana_client::{
|
||||||
rpc_client::RpcClient,
|
|
||||||
client_error::ClientError,
|
client_error::ClientError,
|
||||||
|
rpc_client::RpcClient,
|
||||||
rpc_config::RpcSendTransactionConfig,
|
rpc_config::RpcSendTransactionConfig,
|
||||||
};
|
};
|
||||||
use solana_program::{
|
use solana_program::{
|
||||||
|
@ -42,8 +42,8 @@ use solana_sdk::{
|
||||||
signature::{
|
signature::{
|
||||||
read_keypair_file,
|
read_keypair_file,
|
||||||
Keypair,
|
Keypair,
|
||||||
Signer,
|
|
||||||
Signature,
|
Signature,
|
||||||
|
Signer,
|
||||||
},
|
},
|
||||||
transaction::Transaction,
|
transaction::Transaction,
|
||||||
};
|
};
|
||||||
|
@ -103,16 +103,15 @@ fn execute(
|
||||||
let mut transaction = Transaction::new_with_payer(instructions, Some(&payer.pubkey()));
|
let mut transaction = Transaction::new_with_payer(instructions, Some(&payer.pubkey()));
|
||||||
let recent_blockhash = client.get_recent_blockhash().unwrap().0;
|
let recent_blockhash = client.get_recent_blockhash().unwrap().0;
|
||||||
transaction.sign(&signers.to_vec(), recent_blockhash);
|
transaction.sign(&signers.to_vec(), recent_blockhash);
|
||||||
client
|
client.send_and_confirm_transaction_with_spinner_and_config(
|
||||||
.send_and_confirm_transaction_with_spinner_and_config(
|
&transaction,
|
||||||
&transaction,
|
CommitmentConfig::processed(),
|
||||||
CommitmentConfig::processed(),
|
RpcSendTransactionConfig {
|
||||||
RpcSendTransactionConfig {
|
skip_preflight: true,
|
||||||
skip_preflight: true,
|
preflight_commitment: None,
|
||||||
preflight_commitment: None,
|
encoding: None,
|
||||||
encoding: None,
|
},
|
||||||
},
|
)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mod helpers {
|
mod helpers {
|
||||||
|
@ -133,7 +132,10 @@ mod helpers {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetch account data, the loop is there to re-attempt until data is available.
|
/// Fetch account data, the loop is there to re-attempt until data is available.
|
||||||
pub fn get_account_data<T: BorshDeserialize>(client: &RpcClient, account: &Pubkey) -> Option<T> {
|
pub fn get_account_data<T: BorshDeserialize>(
|
||||||
|
client: &RpcClient,
|
||||||
|
account: &Pubkey,
|
||||||
|
) -> Option<T> {
|
||||||
for _ in 0..5 {
|
for _ in 0..5 {
|
||||||
if let Ok(account) = client.get_account(account) {
|
if let Ok(account) = client.get_account(account) {
|
||||||
return Some(T::try_from_slice(&account.data).unwrap());
|
return Some(T::try_from_slice(&account.data).unwrap());
|
||||||
|
@ -219,7 +221,12 @@ mod helpers {
|
||||||
(vaa, body, body_hash)
|
(vaa, body, body_hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn transfer(client: &RpcClient, from: &Keypair, to: &Pubkey, lamports: u64) -> Result<Signature, ClientError> {
|
pub fn transfer(
|
||||||
|
client: &RpcClient,
|
||||||
|
from: &Keypair,
|
||||||
|
to: &Pubkey,
|
||||||
|
lamports: u64,
|
||||||
|
) -> Result<Signature, ClientError> {
|
||||||
execute(
|
execute(
|
||||||
client,
|
client,
|
||||||
from,
|
from,
|
||||||
|
@ -257,14 +264,21 @@ mod helpers {
|
||||||
nonce: u32,
|
nonce: u32,
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
fee: u64,
|
fee: u64,
|
||||||
|
persist: bool,
|
||||||
) -> Result<Pubkey, ClientError> {
|
) -> Result<Pubkey, ClientError> {
|
||||||
// Transfer money into the fee collector as it needs a balance/must exist.
|
// Transfer money into the fee collector as it needs a balance/must exist.
|
||||||
let fee_collector = FeeCollector::<'_>::key(None, program);
|
let fee_collector = FeeCollector::<'_>::key(None, program);
|
||||||
|
|
||||||
// Capture the resulting message, later functions will need this.
|
// Capture the resulting message, later functions will need this.
|
||||||
let (message_key, instruction) =
|
let (message_key, instruction) = instructions::post_message(
|
||||||
instructions::post_message(*program, payer.pubkey(), emitter.pubkey(), nonce, data)
|
*program,
|
||||||
.unwrap();
|
payer.pubkey(),
|
||||||
|
emitter.pubkey(),
|
||||||
|
nonce,
|
||||||
|
data,
|
||||||
|
persist,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
execute(
|
execute(
|
||||||
client,
|
client,
|
||||||
|
@ -317,7 +331,12 @@ mod helpers {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn post_vaa(client: &RpcClient, program: &Pubkey, payer: &Keypair, vaa: PostVAAData) -> Result<Signature, ClientError> {
|
pub fn post_vaa(
|
||||||
|
client: &RpcClient,
|
||||||
|
program: &Pubkey,
|
||||||
|
payer: &Keypair,
|
||||||
|
vaa: PostVAAData,
|
||||||
|
) -> Result<Signature, ClientError> {
|
||||||
execute(
|
execute(
|
||||||
client,
|
client,
|
||||||
payer,
|
payer,
|
||||||
|
|
|
@ -1,7 +1,17 @@
|
||||||
#![allow(warnings)]
|
#![allow(warnings)]
|
||||||
|
|
||||||
use borsh::BorshSerialize;
|
use borsh::BorshSerialize;
|
||||||
use secp256k1::Message;
|
use byteorder::{
|
||||||
|
BigEndian,
|
||||||
|
WriteBytesExt,
|
||||||
|
};
|
||||||
|
use hex_literal::hex;
|
||||||
|
use secp256k1::{
|
||||||
|
Message,
|
||||||
|
PublicKey,
|
||||||
|
SecretKey,
|
||||||
|
};
|
||||||
|
use sha3::Digest;
|
||||||
use solana_client::rpc_client::RpcClient;
|
use solana_client::rpc_client::RpcClient;
|
||||||
use solana_program::{
|
use solana_program::{
|
||||||
borsh::try_from_slice_unchecked,
|
borsh::try_from_slice_unchecked,
|
||||||
|
@ -27,27 +37,17 @@ use solana_sdk::{
|
||||||
},
|
},
|
||||||
transaction::Transaction,
|
transaction::Transaction,
|
||||||
};
|
};
|
||||||
use byteorder::{
|
|
||||||
BigEndian,
|
|
||||||
WriteBytesExt,
|
|
||||||
};
|
|
||||||
use std::{
|
use std::{
|
||||||
convert::TryInto,
|
convert::TryInto,
|
||||||
io::{
|
io::{
|
||||||
Cursor,
|
Cursor,
|
||||||
Write,
|
Write,
|
||||||
},
|
},
|
||||||
|
time::{
|
||||||
|
Duration,
|
||||||
|
SystemTime,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use std::time::{
|
|
||||||
Duration,
|
|
||||||
SystemTime,
|
|
||||||
};
|
|
||||||
use hex_literal::hex;
|
|
||||||
use secp256k1::{
|
|
||||||
PublicKey,
|
|
||||||
SecretKey,
|
|
||||||
};
|
|
||||||
use sha3::Digest;
|
|
||||||
|
|
||||||
use bridge::{
|
use bridge::{
|
||||||
accounts::GuardianSetDerivationData,
|
accounts::GuardianSetDerivationData,
|
||||||
|
@ -66,14 +66,15 @@ use bridge::{
|
||||||
SerializePayload,
|
SerializePayload,
|
||||||
Signature,
|
Signature,
|
||||||
};
|
};
|
||||||
|
use primitive_types::U256;
|
||||||
|
|
||||||
mod common;
|
mod common;
|
||||||
|
|
||||||
const GOV_KEY: [u8; 64] = [
|
const GOV_KEY: [u8; 64] = [
|
||||||
240, 133, 120, 113, 30, 67, 38, 184, 197, 72, 234, 99, 241, 21, 58, 225, 41, 157, 171, 44,
|
240, 133, 120, 113, 30, 67, 38, 184, 197, 72, 234, 99, 241, 21, 58, 225, 41, 157, 171, 44, 196,
|
||||||
196, 163, 134, 236, 92, 148, 110, 68, 127, 114, 177, 0, 173, 253, 199, 9, 242, 142, 201,
|
163, 134, 236, 92, 148, 110, 68, 127, 114, 177, 0, 173, 253, 199, 9, 242, 142, 201, 174, 108,
|
||||||
174, 108, 197, 18, 102, 115, 0, 31, 205, 127, 188, 191, 56, 171, 228, 20, 247, 149, 170,
|
197, 18, 102, 115, 0, 31, 205, 127, 188, 191, 56, 171, 228, 20, 247, 149, 170, 141, 231, 147,
|
||||||
141, 231, 147, 88, 97, 199,
|
88, 97, 199,
|
||||||
];
|
];
|
||||||
|
|
||||||
struct Context {
|
struct Context {
|
||||||
|
@ -109,7 +110,17 @@ fn test_bridge_messages(context: &mut Context) {
|
||||||
let emitter = Keypair::new();
|
let emitter = Keypair::new();
|
||||||
|
|
||||||
// Post the message, publishing the data for guardian consumption.
|
// Post the message, publishing the data for guardian consumption.
|
||||||
let message_key = common::post_message(client, program, payer, &emitter, nonce, message.clone(), 10_000).unwrap();
|
let message_key = common::post_message(
|
||||||
|
client,
|
||||||
|
program,
|
||||||
|
payer,
|
||||||
|
&emitter,
|
||||||
|
nonce,
|
||||||
|
message.clone(),
|
||||||
|
10_000,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// Emulate Guardian behaviour, verifying the data and publishing signatures/VAA.
|
// Emulate Guardian behaviour, verifying the data and publishing signatures/VAA.
|
||||||
let (vaa, body, body_hash) = common::generate_vaa(&emitter, message.clone(), nonce, 0);
|
let (vaa, body, body_hash) = common::generate_vaa(&emitter, message.clone(), nonce, 0);
|
||||||
|
@ -127,7 +138,17 @@ fn test_guardian_set_change(context: &mut Context) {
|
||||||
let emitter = Keypair::from_bytes(&GOV_KEY).unwrap();
|
let emitter = Keypair::from_bytes(&GOV_KEY).unwrap();
|
||||||
|
|
||||||
// Post the message, publishing the data for guardian consumption.
|
// Post the message, publishing the data for guardian consumption.
|
||||||
let message_key = common::post_message(client, program, payer, &emitter, nonce, message.clone(), 10_000).unwrap();
|
let message_key = common::post_message(
|
||||||
|
client,
|
||||||
|
program,
|
||||||
|
payer,
|
||||||
|
&emitter,
|
||||||
|
nonce,
|
||||||
|
message.clone(),
|
||||||
|
10_000,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// Emulate Guardian behaviour, verifying the data and publishing signatures/VAA.
|
// Emulate Guardian behaviour, verifying the data and publishing signatures/VAA.
|
||||||
let (vaa, body, body_hash) = common::generate_vaa(&emitter, message.clone(), nonce, 0);
|
let (vaa, body, body_hash) = common::generate_vaa(&emitter, message.clone(), nonce, 0);
|
||||||
|
@ -141,9 +162,21 @@ fn test_guardian_set_change(context: &mut Context) {
|
||||||
let message = GovernancePayloadGuardianSetChange {
|
let message = GovernancePayloadGuardianSetChange {
|
||||||
new_guardian_set_index: 1,
|
new_guardian_set_index: 1,
|
||||||
new_guardian_set: new_public_keys.clone(),
|
new_guardian_set: new_public_keys.clone(),
|
||||||
}.try_to_vec().unwrap();
|
}
|
||||||
|
.try_to_vec()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let message_key = common::post_message(client, program, payer, &emitter, nonce, message.clone(), 10_000).unwrap();
|
let message_key = common::post_message(
|
||||||
|
client,
|
||||||
|
program,
|
||||||
|
payer,
|
||||||
|
&emitter,
|
||||||
|
nonce,
|
||||||
|
message.clone(),
|
||||||
|
10_000,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
let (vaa, body, body_hash) = common::generate_vaa(&emitter, message.clone(), nonce, 0);
|
let (vaa, body, body_hash) = common::generate_vaa(&emitter, message.clone(), nonce, 0);
|
||||||
common::verify_signatures(client, program, payer, body, body_hash, &context.secret, 0).unwrap();
|
common::verify_signatures(client, program, payer, body, body_hash, &context.secret, 0).unwrap();
|
||||||
common::post_vaa(client, program, payer, vaa).unwrap();
|
common::post_vaa(client, program, payer, vaa).unwrap();
|
||||||
|
@ -157,11 +190,22 @@ fn test_guardian_set_change(context: &mut Context) {
|
||||||
0,
|
0,
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
).unwrap();
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// Submit the message a second time with a new nonce.
|
// Submit the message a second time with a new nonce.
|
||||||
let nonce = 12399;
|
let nonce = 12399;
|
||||||
let message_key = common::post_message(client, program, payer, &emitter, nonce, message.clone(), 10_000).unwrap();
|
let message_key = common::post_message(
|
||||||
|
client,
|
||||||
|
program,
|
||||||
|
payer,
|
||||||
|
&emitter,
|
||||||
|
nonce,
|
||||||
|
message.clone(),
|
||||||
|
10_000,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
context.public = new_public_keys;
|
context.public = new_public_keys;
|
||||||
context.secret = new_secret_keys;
|
context.secret = new_secret_keys;
|
||||||
|
@ -183,9 +227,21 @@ fn test_guardian_set_change_fails(context: &mut Context) {
|
||||||
let message = GovernancePayloadGuardianSetChange {
|
let message = GovernancePayloadGuardianSetChange {
|
||||||
new_guardian_set_index: 2,
|
new_guardian_set_index: 2,
|
||||||
new_guardian_set: new_public_keys.clone(),
|
new_guardian_set: new_public_keys.clone(),
|
||||||
}.try_to_vec().unwrap();
|
}
|
||||||
|
.try_to_vec()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let message_key = common::post_message(client, program, payer, &emitter, nonce, message.clone(), 10_000).unwrap();
|
let message_key = common::post_message(
|
||||||
|
client,
|
||||||
|
program,
|
||||||
|
payer,
|
||||||
|
&emitter,
|
||||||
|
nonce,
|
||||||
|
message.clone(),
|
||||||
|
10_000,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
let (vaa, body, body_hash) = common::generate_vaa(&emitter, message.clone(), nonce, 1);
|
let (vaa, body, body_hash) = common::generate_vaa(&emitter, message.clone(), nonce, 1);
|
||||||
|
|
||||||
assert!(common::upgrade_guardian_set(
|
assert!(common::upgrade_guardian_set(
|
||||||
|
@ -197,7 +253,8 @@ fn test_guardian_set_change_fails(context: &mut Context) {
|
||||||
1,
|
1,
|
||||||
2,
|
2,
|
||||||
0,
|
0,
|
||||||
).is_err());
|
)
|
||||||
|
.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_set_fees(context: &mut Context) {
|
fn test_set_fees(context: &mut Context) {
|
||||||
|
@ -206,9 +263,12 @@ fn test_set_fees(context: &mut Context) {
|
||||||
let emitter = Keypair::from_bytes(&GOV_KEY).unwrap();
|
let emitter = Keypair::from_bytes(&GOV_KEY).unwrap();
|
||||||
|
|
||||||
let nonce = 12401;
|
let nonce = 12401;
|
||||||
let message = GovernancePayloadSetMessageFee { fee: 100 }
|
let message = GovernancePayloadSetMessageFee {
|
||||||
.try_to_vec()
|
fee: U256::from(100),
|
||||||
.unwrap();
|
persisted_fee: U256::from(100),
|
||||||
|
}
|
||||||
|
.try_to_vec()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let message_key = common::post_message(
|
let message_key = common::post_message(
|
||||||
client,
|
client,
|
||||||
|
@ -218,6 +278,7 @@ fn test_set_fees(context: &mut Context) {
|
||||||
nonce,
|
nonce,
|
||||||
message.clone(),
|
message.clone(),
|
||||||
10_000,
|
10_000,
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let (vaa, body, body_hash) = common::generate_vaa(&emitter, message.clone(), nonce, 1);
|
let (vaa, body, body_hash) = common::generate_vaa(&emitter, message.clone(), nonce, 1);
|
||||||
|
@ -229,13 +290,31 @@ fn test_set_fees(context: &mut Context) {
|
||||||
let emitter = Keypair::new();
|
let emitter = Keypair::new();
|
||||||
let nonce = 12402;
|
let nonce = 12402;
|
||||||
let message = b"Fail to Pay".to_vec();
|
let message = b"Fail to Pay".to_vec();
|
||||||
assert!(
|
assert!(common::post_message(
|
||||||
common::post_message(client, program, payer, &emitter, nonce, message.clone(), 50).is_err()
|
client,
|
||||||
);
|
program,
|
||||||
|
payer,
|
||||||
|
&emitter,
|
||||||
|
nonce,
|
||||||
|
message.clone(),
|
||||||
|
50,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
.is_err());
|
||||||
|
|
||||||
// And succeeds with the new.
|
// And succeeds with the new.
|
||||||
let emitter = Keypair::new();
|
let emitter = Keypair::new();
|
||||||
let nonce = 12402;
|
let nonce = 12402;
|
||||||
let message = b"Fail to Pay".to_vec();
|
let message = b"Fail to Pay".to_vec();
|
||||||
common::post_message(client, program, payer, &emitter, nonce, message.clone(), 100).unwrap();
|
common::post_message(
|
||||||
|
client,
|
||||||
|
program,
|
||||||
|
payer,
|
||||||
|
&emitter,
|
||||||
|
nonce,
|
||||||
|
message.clone(),
|
||||||
|
100,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ spl-token mint "$token" 10000000000 "$account"
|
||||||
|
|
||||||
# Create the bridge contract at a known address
|
# Create the bridge contract at a known address
|
||||||
# OK to fail on subsequent attempts (already created).
|
# OK to fail on subsequent attempts (already created).
|
||||||
retry client create-bridge "$bridge_address" "$initial_guardian" 86400 100
|
retry client create-bridge "$bridge_address" "$initial_guardian" 86400 100 200
|
||||||
|
|
||||||
# Let k8s startup probe succeed
|
# Let k8s startup probe succeed
|
||||||
nc -l -p 2000
|
nc -l -p 2000
|
||||||
|
|
|
@ -12,7 +12,7 @@ use crate::msg::{
|
||||||
use crate::state::{
|
use crate::state::{
|
||||||
config, config_read, guardian_set_get, guardian_set_set, sequence_read, sequence_set,
|
config, config_read, guardian_set_get, guardian_set_set, sequence_read, sequence_set,
|
||||||
vaa_archive_check, ConfigInfo, GovernancePacket, GuardianAddress, GuardianSetInfo,
|
vaa_archive_check, ConfigInfo, GovernancePacket, GuardianAddress, GuardianSetInfo,
|
||||||
GuardianSetUpgrade, ParsedVAA, TransferFee,
|
GuardianSetUpgrade, ParsedVAA, SetFee, TransferFee,
|
||||||
};
|
};
|
||||||
|
|
||||||
use k256::ecdsa::recoverable::Id as RecoverableId;
|
use k256::ecdsa::recoverable::Id as RecoverableId;
|
||||||
|
@ -30,7 +30,7 @@ const CHAIN_ID: u16 = 3;
|
||||||
|
|
||||||
// Lock assets fee amount and denomination
|
// Lock assets fee amount and denomination
|
||||||
const FEE_AMOUNT: u128 = 10000;
|
const FEE_AMOUNT: u128 = 10000;
|
||||||
const FEE_DENOMINATION: &str = "uluna";
|
pub const FEE_DENOMINATION: &str = "uluna";
|
||||||
|
|
||||||
pub fn init<S: Storage, A: Api, Q: Querier>(
|
pub fn init<S: Storage, A: Api, Q: Querier>(
|
||||||
deps: &mut Extern<S, A, Q>,
|
deps: &mut Extern<S, A, Q>,
|
||||||
|
@ -44,6 +44,7 @@ pub fn init<S: Storage, A: Api, Q: Querier>(
|
||||||
guardian_set_index: 0,
|
guardian_set_index: 0,
|
||||||
guardian_set_expirity: msg.guardian_set_expirity,
|
guardian_set_expirity: msg.guardian_set_expirity,
|
||||||
fee: Coin::new(FEE_AMOUNT, FEE_DENOMINATION), // 0.01 Luna (or 10000 uluna) fee by default
|
fee: Coin::new(FEE_AMOUNT, FEE_DENOMINATION), // 0.01 Luna (or 10000 uluna) fee by default
|
||||||
|
fee_persisted: Coin::new(FEE_AMOUNT, FEE_DENOMINATION), // 0.01 Luna (or 10000 uluna) fee by default
|
||||||
};
|
};
|
||||||
config(&mut deps.storage).save(&state)?;
|
config(&mut deps.storage).save(&state)?;
|
||||||
|
|
||||||
|
@ -103,9 +104,10 @@ fn handle_governance_payload<S: Storage, A: Api, Q: Querier>(
|
||||||
}
|
}
|
||||||
|
|
||||||
match gov_packet.action {
|
match gov_packet.action {
|
||||||
// 0 is reserved for upgrade / migration
|
// 1 is reserved for upgrade / migration
|
||||||
1u8 => vaa_update_guardian_set(deps, env, &gov_packet.payload),
|
2u8 => vaa_update_guardian_set(deps, env, &gov_packet.payload),
|
||||||
2u8 => handle_transfer_fee(deps, env, &gov_packet.payload),
|
3u8 => handle_set_fee(deps, env, &gov_packet.payload),
|
||||||
|
4u8 => handle_transfer_fee(deps, env, &gov_packet.payload),
|
||||||
_ => ContractError::InvalidVAAAction.std_err(),
|
_ => ContractError::InvalidVAAAction.std_err(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -230,6 +232,32 @@ fn vaa_update_guardian_set<S: Storage, A: Api, Q: Querier>(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn handle_set_fee<S: Storage, A: Api, Q: Querier>(
|
||||||
|
deps: &mut Extern<S, A, Q>,
|
||||||
|
env: Env,
|
||||||
|
data: &Vec<u8>,
|
||||||
|
) -> StdResult<HandleResponse> {
|
||||||
|
let set_fee_msg = SetFee::deserialize(&data)?;
|
||||||
|
|
||||||
|
// Save new fees
|
||||||
|
let mut state = config_read(&mut deps.storage).load()?;
|
||||||
|
state.fee = set_fee_msg.fee;
|
||||||
|
state.fee_persisted = set_fee_msg.fee_persistent;
|
||||||
|
config(&mut deps.storage).save(&state)?;
|
||||||
|
|
||||||
|
Ok(HandleResponse {
|
||||||
|
messages: vec![],
|
||||||
|
log: vec![
|
||||||
|
log("action", "fee_change"),
|
||||||
|
log("new_fee.amount", state.fee.amount),
|
||||||
|
log("new_fee.denom", state.fee.denom),
|
||||||
|
log("new_persisted_fee.amount", state.fee_persisted.amount),
|
||||||
|
log("new_persisted_fee.denom", state.fee_persisted.denom),
|
||||||
|
],
|
||||||
|
data: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn handle_transfer_fee<S: Storage, A: Api, Q: Querier>(
|
pub fn handle_transfer_fee<S: Storage, A: Api, Q: Querier>(
|
||||||
deps: &mut Extern<S, A, Q>,
|
deps: &mut Extern<S, A, Q>,
|
||||||
env: Env,
|
env: Env,
|
||||||
|
@ -256,9 +284,16 @@ fn handle_post_message<S: Storage, A: Api, Q: Querier>(
|
||||||
persist: bool,
|
persist: bool,
|
||||||
) -> StdResult<HandleResponse> {
|
) -> StdResult<HandleResponse> {
|
||||||
let state = config_read(&deps.storage).load()?;
|
let state = config_read(&deps.storage).load()?;
|
||||||
|
let fee = {
|
||||||
|
if persist {
|
||||||
|
state.fee
|
||||||
|
} else {
|
||||||
|
state.fee_persisted
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Check fee
|
// Check fee
|
||||||
if !has_coins(env.message.sent_funds.as_ref(), &state.fee) {
|
if !has_coins(env.message.sent_funds.as_ref(), &fee) {
|
||||||
return ContractError::FeeTooLow.std_err();
|
return ContractError::FeeTooLow.std_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use schemars::JsonSchema;
|
use schemars::{JsonSchema, Set};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use cosmwasm_std::{Binary, CanonicalAddr, Coin, HumanAddr, StdResult, Storage, Uint128};
|
use cosmwasm_std::{Binary, CanonicalAddr, Coin, HumanAddr, StdResult, Storage, Uint128};
|
||||||
|
@ -31,8 +31,10 @@ pub struct ConfigInfo {
|
||||||
pub gov_chain: u16,
|
pub gov_chain: u16,
|
||||||
pub gov_address: Vec<u8>,
|
pub gov_address: Vec<u8>,
|
||||||
|
|
||||||
// Asset locking fee
|
// Message sending fee
|
||||||
pub fee: Coin,
|
pub fee: Coin,
|
||||||
|
// Persisted message sending fee
|
||||||
|
pub fee_persisted: Coin,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validator Action Approval(VAA) data
|
// Validator Action Approval(VAA) data
|
||||||
|
@ -144,6 +146,7 @@ pub struct GuardianAddress {
|
||||||
pub bytes: Binary, // 20-byte addresses
|
pub bytes: Binary, // 20-byte addresses
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use crate::contract::FEE_DENOMINATION;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use hex;
|
use hex;
|
||||||
|
|
||||||
|
@ -263,7 +266,7 @@ impl GovernancePacket {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// action 1
|
// action 2
|
||||||
pub struct GuardianSetUpgrade {
|
pub struct GuardianSetUpgrade {
|
||||||
pub new_guardian_set_index: u32,
|
pub new_guardian_set_index: u32,
|
||||||
pub new_guardian_set: GuardianSetInfo,
|
pub new_guardian_set: GuardianSetInfo,
|
||||||
|
@ -303,7 +306,34 @@ impl GuardianSetUpgrade {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// action 2
|
// action 3
|
||||||
|
pub struct SetFee {
|
||||||
|
pub fee: Coin,
|
||||||
|
pub fee_persistent: Coin,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SetFee {
|
||||||
|
pub fn deserialize(data: &Vec<u8>) -> StdResult<Self> {
|
||||||
|
let data = data.as_slice();
|
||||||
|
|
||||||
|
let (_, amount) = data.get_u256(0);
|
||||||
|
let (_, amount_persistent) = data.get_u256(32);
|
||||||
|
let fee = Coin {
|
||||||
|
denom: String::from(FEE_DENOMINATION),
|
||||||
|
amount: Uint128(amount),
|
||||||
|
};
|
||||||
|
let fee_persistent = Coin {
|
||||||
|
denom: String::from(FEE_DENOMINATION),
|
||||||
|
amount: Uint128(amount_persistent),
|
||||||
|
};
|
||||||
|
Ok(SetFee {
|
||||||
|
fee,
|
||||||
|
fee_persistent,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// action 4
|
||||||
pub struct TransferFee {
|
pub struct TransferFee {
|
||||||
pub amount: Coin,
|
pub amount: Coin,
|
||||||
pub recipient: CanonicalAddr,
|
pub recipient: CanonicalAddr,
|
||||||
|
@ -314,12 +344,11 @@ impl TransferFee {
|
||||||
let data = data.as_slice();
|
let data = data.as_slice();
|
||||||
let recipient = data.get_address(0);
|
let recipient = data.get_address(0);
|
||||||
|
|
||||||
let amount = Uint128(data.get_u128_be(32));
|
let (_, amount) = data.get_u256(32);
|
||||||
let denom = match String::from_utf8(data[48..].to_vec()) {
|
let amount = Coin {
|
||||||
Ok(s) => s,
|
denom: String::from(FEE_DENOMINATION),
|
||||||
Err(_) => return ContractError::InvalidVAA.std_err(),
|
amount: Uint128(amount),
|
||||||
};
|
};
|
||||||
let amount = Coin { denom, amount };
|
|
||||||
Ok(TransferFee { amount, recipient })
|
Ok(TransferFee { amount, recipient })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue