subsidize guardian transactions using fees (#82)
* subsidize guardian transactions using fees * reuse transfer function * evict signature state on inbound transfers * fix mutability issues due to copying * add fee refund * unify fee calculation * add fee documentation * Unflip tables * type annotation
This commit is contained in:
parent
ee5d07c929
commit
8510140165
|
@ -217,7 +217,80 @@ The user can then get the VAA from the `LockProposal` and submit it on the forei
|
|||
|
||||
### Fees
|
||||
|
||||
TODO \o/
|
||||
Fees exist for 2 reasons: spam prevention and guardian cost coverage.
|
||||
|
||||
Costs for guardians:
|
||||
|
||||
Assuming no hosting costs for a guardian operation (blockchain and guardian nodes), the only costs
|
||||
that need to be covered by a guardian operator are Solana transaction fees as well as rent costs for newly
|
||||
created account (used to store application information).
|
||||
|
||||
**For a transfer from Solana to a foreign chain (20 guardians; 14 quorum):**
|
||||
|
||||
Transactions required: `3 (signatures + verify) + 1 (post VAA)`
|
||||
|
||||
Accounts created: `1 ClaimedVAA + 1 SignatureState`
|
||||
|
||||
Costs:
|
||||
```
|
||||
4 TX (14 secp signatures + 4 ed25519) + ClaimedVAA (exemption rent) + SignatureState (exemption rent)
|
||||
18 * 10_000 + (36+128) * 6962 + (1337+128) * 6962
|
||||
11521098 lamports = 0.0115 SOL
|
||||
```
|
||||
|
||||
**For a transfer from a foreign chain to Solana (20 guardians; 14 quorum):**
|
||||
|
||||
Transactions required: `3 (signatures + verify) + 1 (post VAA)`
|
||||
|
||||
Accounts created: `1 ClaimedVAA + 1 SignatureState (temporary; evicted in PostVAA)`
|
||||
|
||||
Costs:
|
||||
```
|
||||
4 TX (14 secp signatures + 4 ed25519) + ClaimedVAA (exemption rent)
|
||||
18 * 10_000 + (36+128) * 6962
|
||||
1321768 lamports = 0.0013 SOL
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
In order to cover rent costs there exists a subsidy pool controlled by the bridge to cover rent payments.
|
||||
While the guardian needs to hold enough SOL to pay for the rent, it is automatically refunded by the pool,
|
||||
in case the pool has sufficient balance.
|
||||
|
||||
This subsidy pool is funded by transaction fees.
|
||||
Additionally, the subsidy pool subsidizes the transactions fees paid by the guardian submitting the VAA.
|
||||
As long as the pool has a sufficient balance, it will try to refund transaction fees to the guardian.
|
||||
|
||||
Since Wormhole does not require foreign chain users to own SOL, Wormhole can't charge subsidy fees on inbound
|
||||
transfers. Assuming a balance between inbound and outbound transfers, outbound transfers need to subsidize
|
||||
inbound Solana transfers.
|
||||
|
||||
Additionally, foreign chain contracts might start charging additional fees in the future.
|
||||
|
||||
---
|
||||
|
||||
The bridge can handle at most <TODO> transactions per second. Therefore, the fees should prevent spam
|
||||
by dynamically adjusting to load. This is particularly useful on Solana where fees are low and spamming
|
||||
would be cheap.
|
||||
|
||||
Dynamic fees should be cheap while the system is under low and medium load and high while the system is
|
||||
close or above its capacity.
|
||||
To prevent sudden fee changes, the fee system has inertia.
|
||||
|
||||
Fees scale as follows `fee = (tps/tps_max)^6`.
|
||||
The result is the fee per transfer in SOL. So at max capacity, the price per transfer is 1SOL.
|
||||
TPS is measured over a 30 second window.
|
||||
|
||||
The minimum fee is the equivalent of 2x the rent of SignatureState and ClaimedVAA to cover the cost
|
||||
of this transfer and about 10 inbound transfers.
|
||||
|
||||
---
|
||||
|
||||
The above design can currently not be implemented due to limitations in the Solana BPF VM.
|
||||
|
||||
In the current design, tx fees are refunded, rents are subsidized by the bridge and transfers out of Solana
|
||||
cost a fixed fee of 2x (ClaimedVAA rent + SignatureState rent + VAA submission fee), which will roughly
|
||||
pay for 1 outbound + ~10 inbound transfers.
|
||||
|
||||
### Config changes
|
||||
#### Guardian set changes
|
||||
|
|
|
@ -12,7 +12,7 @@ Initializes a new Bridge at `bridge`.
|
|||
| ----- | ------ | ------------ | ------ | --------- | ----- | ------- |
|
||||
| 0 | sys | SystemProgram | | | | |
|
||||
| 1 | clock | Sysvar | | | | ✅ |
|
||||
| 2 | bridge | BridgeConfig | | | ✅ | ✅ |
|
||||
| 2 | bridge | BridgeConfig | | ✅ | ✅ | ✅ |
|
||||
| 3 | guardian_set | GuardianSet | | ✅ | ✅ | ✅ |
|
||||
| 4 | payer | Account | ✅ | | | |
|
||||
|
||||
|
@ -42,14 +42,15 @@ Creates a new `WrappedAsset` to be used to create accounts and later receive tra
|
|||
|
||||
Checks secp checks (in the previous instruction) and stores results.
|
||||
|
||||
| Index | Name | Type | signer | writeable | empty | derived |
|
||||
| ----- | ------ | ------------ | ------ | --------- | ----- | ------- |
|
||||
| 0 | bridge_p | BridgeProgram | | | | |
|
||||
| 1 | sys | SystemProgram | | | | |
|
||||
| 2 | instructions | Sysvar | | | | ✅ |
|
||||
| 3 | sig_status | SignatureState | | ✅ | | |
|
||||
| 4 | guardian_set | GuardianSet | | | | ✅ |
|
||||
| 5 | payer | Account | ✅ | | | |
|
||||
| Index | Name | Type | signer | writeable | empty | derived |
|
||||
| ----- | ------ | ------------ | ------ | --------- | ----- | ------- |
|
||||
| 0 | bridge_p | BridgeProgram | | | | |
|
||||
| 1 | sys | SystemProgram | | | | |
|
||||
| 2 | instructions | Sysvar | | | | ✅ |
|
||||
| 3 | bridge_config | BridgeConfig | | ✅ | | ✅ |
|
||||
| 4 | sig_status | SignatureState | | ✅ | | |
|
||||
| 5 | guardian_set | GuardianSet | | | | ✅ |
|
||||
| 6 | payer | Account | ✅ | | | |
|
||||
|
||||
#### TransferOut
|
||||
|
||||
|
@ -65,7 +66,7 @@ Parameters:
|
|||
| 1 | sys | SystemProgram | | | | |
|
||||
| 2 | token_program | SplToken | | | | |
|
||||
| 3 | rent | Sysvar | | | | ✅ |
|
||||
| 4 | clock | Sysvar | | | ✅ | |
|
||||
| 4 | clock | Sysvar | | | ✅ | |
|
||||
| 5 | token_account | TokenAccount | | ✅ | | |
|
||||
| 6 | bridge | BridgeConfig | | | | |
|
||||
| 7 | proposal | TransferOutProposal | | ✅ | ✅ | ✅ |
|
||||
|
@ -131,10 +132,10 @@ All require:
|
|||
| 1 | sys | SystemProgram | | | | |
|
||||
| 2 | rent | Sysvar | | | | ✅ |
|
||||
| 3 | clock | Sysvar | | | | ✅ |
|
||||
| 4 | bridge | BridgeConfig | | | | |
|
||||
| 4 | bridge | BridgeConfig | | ✅ | | |
|
||||
| 5 | guardian_set | GuardianSet | | | | |
|
||||
| 6 | claim | ExecutedVAA | | ✅ | ✅ | ✅ |
|
||||
| 7 | sig_info | SigState | | | ✅ | |
|
||||
| 7 | sig_info | SigState | | ✅ | ✅ | |
|
||||
| 8 | payer | Account | ✅ | | | |
|
||||
|
||||
followed by:
|
||||
|
|
|
@ -330,7 +330,7 @@ pub fn transfer_out(
|
|||
AccountMeta::new_readonly(solana_program::sysvar::rent::id(), false),
|
||||
AccountMeta::new_readonly(solana_program::sysvar::clock::id(), false),
|
||||
AccountMeta::new(*token_account, false),
|
||||
AccountMeta::new(bridge_key, false),
|
||||
AccountMeta::new_readonly(bridge_key, false),
|
||||
AccountMeta::new(transfer_key, false),
|
||||
AccountMeta::new(*token_mint, false),
|
||||
AccountMeta::new(*payer, true),
|
||||
|
@ -368,6 +368,7 @@ pub fn verify_signatures(
|
|||
AccountMeta::new_readonly(*program_id, false),
|
||||
AccountMeta::new_readonly(solana_program::system_program::id(), false),
|
||||
AccountMeta::new_readonly(solana_program::sysvar::instructions::id(), false),
|
||||
AccountMeta::new(bridge_key, false),
|
||||
AccountMeta::new(*signature_acc, false),
|
||||
AccountMeta::new_readonly(guardian_set_key, false),
|
||||
AccountMeta::new(*payer, true),
|
||||
|
@ -494,7 +495,7 @@ pub fn create_wrapped(
|
|||
AccountMeta::new_readonly(solana_program::system_program::id(), false),
|
||||
AccountMeta::new_readonly(spl_token::id(), false),
|
||||
AccountMeta::new_readonly(solana_program::sysvar::rent::id(), false),
|
||||
AccountMeta::new(bridge_key, false),
|
||||
AccountMeta::new_readonly(bridge_key, false),
|
||||
AccountMeta::new(*payer, true),
|
||||
AccountMeta::new(wrapped_mint_key, false),
|
||||
AccountMeta::new(wrapped_meta_key, false),
|
||||
|
|
|
@ -35,6 +35,10 @@ use crate::{
|
|||
use solana_program::program_pack::Pack;
|
||||
use std::borrow::BorrowMut;
|
||||
use std::ops::Add;
|
||||
use solana_program::fee_calculator::FeeCalculator;
|
||||
|
||||
/// Tx fee of Signature checks and PostVAA (see docs for calculation)
|
||||
const VAA_TX_FEE: u64 = 18 * 10000;
|
||||
|
||||
/// SigInfo contains metadata about signers in a VerifySignature ix
|
||||
struct SigInfo {
|
||||
|
@ -123,12 +127,13 @@ impl Bridge {
|
|||
program_id,
|
||||
accounts,
|
||||
new_bridge_info.key,
|
||||
payer_info.key,
|
||||
payer_info,
|
||||
program_id,
|
||||
&bridge_seed,
|
||||
None,
|
||||
)?;
|
||||
|
||||
let mut new_account_data = new_bridge_info.try_borrow_mut_data()?;
|
||||
let mut new_account_data = new_bridge_info.try_borrow_mut_data().map_err(|_| ProgramError::AccountBorrowFailed)?;
|
||||
let mut bridge: &mut Bridge = Self::unpack_unchecked(&mut new_account_data)?;
|
||||
if bridge.is_initialized {
|
||||
return Err(Error::AlreadyExists.into());
|
||||
|
@ -140,12 +145,13 @@ impl Bridge {
|
|||
program_id,
|
||||
accounts,
|
||||
new_guardian_info.key,
|
||||
payer_info.key,
|
||||
payer_info,
|
||||
program_id,
|
||||
&guardian_seed,
|
||||
None,
|
||||
)?;
|
||||
|
||||
let mut new_guardian_data = new_guardian_info.try_borrow_mut_data()?;
|
||||
let mut new_guardian_data = new_guardian_info.try_borrow_mut_data().map_err(|_| ProgramError::AccountBorrowFailed)?;
|
||||
let mut guardian_info: &mut GuardianSet = Self::unpack_unchecked(&mut new_guardian_data)?;
|
||||
if guardian_info.is_initialized {
|
||||
return Err(Error::AlreadyExists.into());
|
||||
|
@ -197,11 +203,13 @@ impl Bridge {
|
|||
next_account_info(account_info_iter)?; // Bridge program
|
||||
next_account_info(account_info_iter)?; // System program
|
||||
let instruction_accounts = next_account_info(account_info_iter)?;
|
||||
let bridge_info = next_account_info(account_info_iter)?;
|
||||
let sig_info = next_account_info(account_info_iter)?;
|
||||
let guardian_set_info = next_account_info(account_info_iter)?;
|
||||
let payer_info = next_account_info(account_info_iter)?;
|
||||
|
||||
let guardian_set: GuardianSet = Self::guardian_set_deserialize(guardian_set_info)?;
|
||||
let guardian_data = guardian_set_info.data.try_borrow().map_err(|_| ProgramError::AccountBorrowFailed)?;
|
||||
let guardian_set: &GuardianSet = Self::unpack_immutable(&guardian_data)?;
|
||||
|
||||
let sig_infos: Vec<SigInfo> = payload
|
||||
.signers
|
||||
|
@ -232,7 +240,7 @@ impl Bridge {
|
|||
secp_ix_index as usize,
|
||||
&instruction_accounts.try_borrow_mut_data()?,
|
||||
)
|
||||
.map_err(|_| ProgramError::InvalidAccountData)?;
|
||||
.map_err(|_| ProgramError::InvalidAccountData)?;
|
||||
|
||||
// Check that the instruction is actually for the secp program
|
||||
if secp_ix.program_id != solana_program::secp256k1_program::id() {
|
||||
|
@ -310,9 +318,10 @@ impl Bridge {
|
|||
program_id,
|
||||
accounts,
|
||||
sig_info.key,
|
||||
payer_info.key,
|
||||
payer_info,
|
||||
program_id,
|
||||
&sig_seeds,
|
||||
Some(bridge_info),
|
||||
)?;
|
||||
} else if payload.initial_creation {
|
||||
return Err(Error::AlreadyExists.into());
|
||||
|
@ -377,10 +386,15 @@ impl Bridge {
|
|||
let payer_info = next_account_info(account_info_iter)?;
|
||||
|
||||
let sender = Bridge::token_account_deserialize(sender_account_info)?;
|
||||
let bridge = Bridge::bridge_deserialize(bridge_info)?;
|
||||
let bridge_data = bridge_info.data.try_borrow().map_err(|_| ProgramError::AccountBorrowFailed)?;
|
||||
let bridge: &Bridge = Self::unpack_immutable(&bridge_data)?;
|
||||
let mint = Bridge::mint_deserialize(mint_info)?;
|
||||
let clock = Clock::from_account_info(clock_info)?;
|
||||
|
||||
// Fee handling
|
||||
let fee = Self::transfer_fee();
|
||||
Self::transfer_sol(payer_info, bridge_info, fee)?;
|
||||
|
||||
// Does the token belong to the mint
|
||||
if sender.mint != *mint_info.key {
|
||||
return Err(Error::TokenMintMismatch.into());
|
||||
|
@ -412,9 +426,10 @@ impl Bridge {
|
|||
program_id,
|
||||
accounts,
|
||||
transfer_info.key,
|
||||
payer_info.key,
|
||||
payer_info,
|
||||
program_id,
|
||||
&transfer_seed,
|
||||
None,
|
||||
)?;
|
||||
|
||||
// Load transfer account
|
||||
|
@ -472,9 +487,14 @@ impl Bridge {
|
|||
|
||||
let sender = Bridge::token_account_deserialize(sender_account_info)?;
|
||||
let mint = Bridge::mint_deserialize(mint_info)?;
|
||||
let bridge = Bridge::bridge_deserialize(bridge_info)?;
|
||||
let bridge_data = bridge_info.data.try_borrow().map_err(|_| ProgramError::AccountBorrowFailed)?;
|
||||
let bridge: &Bridge = Self::unpack_immutable(&bridge_data)?;
|
||||
let clock = Clock::from_account_info(clock_info)?;
|
||||
|
||||
// Fee handling
|
||||
let fee = Self::transfer_fee();
|
||||
Self::transfer_sol(payer_info, bridge_info, fee)?;
|
||||
|
||||
// Does the token belong to the mint
|
||||
if sender.mint != *mint_info.key {
|
||||
return Err(Error::TokenMintMismatch.into());
|
||||
|
@ -494,9 +514,10 @@ impl Bridge {
|
|||
program_id,
|
||||
accounts,
|
||||
transfer_info.key,
|
||||
payer_info.key,
|
||||
payer_info,
|
||||
program_id,
|
||||
&transfer_seed,
|
||||
None,
|
||||
)?;
|
||||
|
||||
// Load transfer account
|
||||
|
@ -519,7 +540,8 @@ impl Bridge {
|
|||
bridge_info.key,
|
||||
custody_info.key,
|
||||
mint_info.key,
|
||||
payer_info.key,
|
||||
payer_info,
|
||||
None,
|
||||
)?;
|
||||
}
|
||||
|
||||
|
@ -562,6 +584,25 @@ impl Bridge {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn transfer_fee() -> u64 {
|
||||
// Pay for 2 signature state and Claimed VAA rents + 2 * guardian tx fees
|
||||
// This will pay for this transfer and ~10 inbound ones
|
||||
Rent::default().minimum_balance((size_of::<SignatureState>() + size_of::<ClaimedVAA>()) * 2) + VAA_TX_FEE * 2
|
||||
}
|
||||
|
||||
pub fn transfer_sol(
|
||||
payer_account: &AccountInfo,
|
||||
recipient_account: &AccountInfo,
|
||||
amount: u64,
|
||||
) -> ProgramResult {
|
||||
let mut payer_balance = payer_account.try_borrow_mut_lamports()?;
|
||||
**payer_balance = payer_balance.checked_sub(amount).ok_or(ProgramError::InsufficientFunds)?;
|
||||
let mut recipient_balance = recipient_account.try_borrow_mut_lamports()?;
|
||||
**recipient_balance = recipient_balance.checked_add(amount).ok_or(ProgramError::InvalidArgument)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Processes a VAA
|
||||
pub fn process_vaa(
|
||||
program_id: &Pubkey,
|
||||
|
@ -582,9 +623,11 @@ impl Bridge {
|
|||
let sig_info = next_account_info(account_info_iter)?;
|
||||
let payer_info = next_account_info(account_info_iter)?;
|
||||
|
||||
let mut bridge = Bridge::bridge_deserialize(bridge_info)?;
|
||||
let mut bridge_data = bridge_info.data.try_borrow_mut().map_err(|_| ProgramError::AccountBorrowFailed)?;
|
||||
let bridge: &mut Bridge = Self::unpack(&mut bridge_data)?;
|
||||
let clock = Clock::from_account_info(clock_info)?;
|
||||
let mut guardian_set = Bridge::guardian_set_deserialize(guardian_set_info)?;
|
||||
let mut guardian_data = guardian_set_info.data.try_borrow_mut().map_err(|_| ProgramError::AccountBorrowFailed)?;
|
||||
let guardian_set: &mut GuardianSet = Bridge::unpack(&mut guardian_data)?;
|
||||
|
||||
// Check that the guardian set is valid
|
||||
let expected_guardian_set =
|
||||
|
@ -599,9 +642,10 @@ impl Bridge {
|
|||
program_id,
|
||||
accounts,
|
||||
claim_info.key,
|
||||
payer_info.key,
|
||||
payer_info,
|
||||
program_id,
|
||||
&claim_seeds,
|
||||
Some(bridge_info),
|
||||
)?;
|
||||
|
||||
// Check that the guardian set is still active
|
||||
|
@ -635,19 +679,23 @@ impl Bridge {
|
|||
return Err(ProgramError::InvalidArgument);
|
||||
}
|
||||
|
||||
let mut evict_signatures = false;
|
||||
let payload = vaa.payload.as_ref().ok_or(Error::InvalidVAAAction)?;
|
||||
match payload {
|
||||
VAABody::UpdateGuardianSet(v) => Self::process_vaa_set_update(
|
||||
program_id,
|
||||
accounts,
|
||||
account_info_iter,
|
||||
&clock,
|
||||
bridge_info,
|
||||
payer_info,
|
||||
&mut bridge,
|
||||
&mut guardian_set,
|
||||
&v,
|
||||
),
|
||||
VAABody::UpdateGuardianSet(v) => {
|
||||
evict_signatures = true;
|
||||
Self::process_vaa_set_update(
|
||||
program_id,
|
||||
accounts,
|
||||
account_info_iter,
|
||||
&clock,
|
||||
bridge_info,
|
||||
payer_info,
|
||||
bridge,
|
||||
guardian_set,
|
||||
&v,
|
||||
)
|
||||
}
|
||||
VAABody::Transfer(v) => {
|
||||
if v.source_chain == CHAIN_ID_SOLANA {
|
||||
Self::process_vaa_transfer_post(
|
||||
|
@ -660,18 +708,30 @@ impl Bridge {
|
|||
sig_info.key,
|
||||
)
|
||||
} else {
|
||||
evict_signatures = true;
|
||||
Self::process_vaa_transfer(
|
||||
program_id,
|
||||
accounts,
|
||||
account_info_iter,
|
||||
bridge_info,
|
||||
&mut bridge,
|
||||
bridge,
|
||||
&v,
|
||||
)
|
||||
}
|
||||
}
|
||||
}?;
|
||||
|
||||
// If the signatures are not needed anymore, evict them and reclaim rent.
|
||||
// This should cover most of the costs of the guardian.
|
||||
if evict_signatures {
|
||||
Self::transfer_sol(sig_info, payer_info, sig_info.lamports())?;
|
||||
}
|
||||
|
||||
// Refund tx fee if possible
|
||||
if bridge_info.lamports().checked_sub(Self::MIN_BRIDGE_BALANCE).unwrap_or(0) >= VAA_TX_FEE {
|
||||
Self::transfer_sol(bridge_info, payer_info, VAA_TX_FEE)?;
|
||||
}
|
||||
|
||||
// Load claim account
|
||||
let mut claim_data = claim_info.try_borrow_mut_data()?;
|
||||
let claim: &mut ClaimedVAA = Bridge::unpack_unchecked(&mut claim_data)?;
|
||||
|
@ -721,9 +781,10 @@ impl Bridge {
|
|||
program_id,
|
||||
accounts,
|
||||
new_guardian_info.key,
|
||||
payer_info.key,
|
||||
payer_info,
|
||||
program_id,
|
||||
&guardian_seed,
|
||||
Some(bridge_info),
|
||||
)?;
|
||||
|
||||
let mut guardian_set_new_data = new_guardian_info.try_borrow_mut_data()?;
|
||||
|
@ -891,7 +952,8 @@ impl Bridge {
|
|||
let mint_info = next_account_info(account_info_iter)?;
|
||||
let wrapped_meta_info = next_account_info(account_info_iter)?;
|
||||
|
||||
let bridge = Bridge::bridge_deserialize(bridge_info)?;
|
||||
let bridge_data = bridge_info.data.try_borrow().map_err(|_| ProgramError::AccountBorrowFailed)?;
|
||||
let bridge: &Bridge = Self::unpack_immutable(&bridge_data)?;
|
||||
|
||||
// Foreign chain asset, mint wrapped asset
|
||||
let expected_mint_address = Bridge::derive_wrapped_asset_id(
|
||||
|
@ -912,9 +974,10 @@ impl Bridge {
|
|||
&bridge.config.token_program,
|
||||
mint_info.key,
|
||||
bridge_info.key,
|
||||
payer_info.key,
|
||||
payer_info,
|
||||
&a,
|
||||
a.decimals,
|
||||
None,
|
||||
)?;
|
||||
|
||||
// Check and create wrapped asset meta to allow reverse resolution of info
|
||||
|
@ -923,9 +986,10 @@ impl Bridge {
|
|||
program_id,
|
||||
accounts,
|
||||
wrapped_meta_info.key,
|
||||
payer_info.key,
|
||||
payer_info,
|
||||
program_id,
|
||||
&wrapped_meta_seeds,
|
||||
None,
|
||||
)?;
|
||||
|
||||
let mut wrapped_meta_data = wrapped_meta_info.try_borrow_mut_data()?;
|
||||
|
@ -1030,7 +1094,8 @@ impl Bridge {
|
|||
bridge: &Pubkey,
|
||||
account: &Pubkey,
|
||||
mint: &Pubkey,
|
||||
payer: &Pubkey,
|
||||
payer: &AccountInfo,
|
||||
subsidizer: Option<&AccountInfo>,
|
||||
) -> Result<(), ProgramError> {
|
||||
Self::check_and_create_account::<[u8; spl_token::state::Account::LEN]>(
|
||||
program_id,
|
||||
|
@ -1039,6 +1104,7 @@ impl Bridge {
|
|||
payer,
|
||||
token_program,
|
||||
&Self::derive_custody_seeds(bridge, mint),
|
||||
subsidizer,
|
||||
)?;
|
||||
info!(token_program.to_string().as_str());
|
||||
let ix = spl_token::instruction::initialize_account(
|
||||
|
@ -1057,9 +1123,10 @@ impl Bridge {
|
|||
token_program: &Pubkey,
|
||||
mint: &Pubkey,
|
||||
bridge: &Pubkey,
|
||||
payer: &Pubkey,
|
||||
payer: &AccountInfo,
|
||||
asset: &AssetMeta,
|
||||
decimals: u8,
|
||||
subsidizer: Option<&AccountInfo>,
|
||||
) -> Result<(), ProgramError> {
|
||||
Self::check_and_create_account::<[u8; spl_token::state::Mint::LEN]>(
|
||||
program_id,
|
||||
|
@ -1068,6 +1135,7 @@ impl Bridge {
|
|||
payer,
|
||||
token_program,
|
||||
&Self::derive_wrapped_asset_seeds(bridge, asset.chain, asset.decimals, asset.address),
|
||||
subsidizer,
|
||||
)?;
|
||||
let ix = spl_token::instruction::initialize_mint(
|
||||
token_program,
|
||||
|
@ -1099,14 +1167,21 @@ impl Bridge {
|
|||
invoke_signed(instruction, account_infos, &[s.as_slice()])
|
||||
}
|
||||
|
||||
/// The amount of sol that needs to be held in the BridgeConfig account in order to make it
|
||||
/// exempt of rent payments.
|
||||
const MIN_BRIDGE_BALANCE: u64 = (((solana_program::rent::ACCOUNT_STORAGE_OVERHEAD + size_of::<BridgeConfig>() as u64) *
|
||||
solana_program::rent::DEFAULT_LAMPORTS_PER_BYTE_YEAR) as f64
|
||||
* solana_program::rent::DEFAULT_EXEMPTION_THRESHOLD) as u64;
|
||||
|
||||
/// Check that a key was derived correctly and create account
|
||||
pub fn check_and_create_account<T: Sized>(
|
||||
program_id: &Pubkey,
|
||||
accounts: &[AccountInfo],
|
||||
new_account: &Pubkey,
|
||||
payer: &Pubkey,
|
||||
payer: &AccountInfo,
|
||||
owner: &Pubkey,
|
||||
seeds: &Vec<Vec<u8>>,
|
||||
subsidizer: Option<&AccountInfo>,
|
||||
) -> Result<Vec<Vec<u8>>, ProgramError> {
|
||||
info!("deriving key");
|
||||
let (expected_key, full_seeds) = Bridge::derive_key(program_id, seeds)?;
|
||||
|
@ -1114,12 +1189,28 @@ impl Bridge {
|
|||
return Err(Error::InvalidDerivedAccount.into());
|
||||
}
|
||||
|
||||
// The subsidizer refunds the rent that needs to be paid to create the account.
|
||||
// This mechanism is intended to reduce the cost of operating a guardian.
|
||||
// The subsidizer account should be of the type BridgeConfig and will only pay out
|
||||
// the subsidy if the account holds at least MIN_BRIDGE_BALANCE+rent
|
||||
match subsidizer {
|
||||
None => {}
|
||||
Some(v) => {
|
||||
let bal = v.try_lamports()?;
|
||||
let rent = Rent::default().minimum_balance(size_of::<T>());
|
||||
if bal.checked_sub(Self::MIN_BRIDGE_BALANCE).ok_or(ProgramError::InsufficientFunds)? >= rent {
|
||||
// Refund rent to payer
|
||||
Self::transfer_sol(v, payer, rent)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
info!("deploying contract");
|
||||
Self::create_account_raw::<T>(
|
||||
program_id,
|
||||
accounts,
|
||||
new_account,
|
||||
payer,
|
||||
payer.key,
|
||||
owner,
|
||||
&full_seeds,
|
||||
)?;
|
||||
|
|
|
@ -218,22 +218,6 @@ impl Bridge {
|
|||
.map_err(|_| Error::ExpectedToken)?)
|
||||
}
|
||||
|
||||
/// Deserializes a `Bridge`.
|
||||
pub fn bridge_deserialize(info: &AccountInfo) -> Result<Bridge, Error> {
|
||||
Ok(*Bridge::unpack(&mut info.data.borrow_mut()).map_err(|_| Error::ExpectedBridge)?)
|
||||
}
|
||||
|
||||
/// Deserializes a `GuardianSet`.
|
||||
pub fn guardian_set_deserialize(info: &AccountInfo) -> Result<GuardianSet, Error> {
|
||||
Ok(*Bridge::unpack(&mut info.data.borrow_mut()).map_err(|_| Error::ExpectedGuardianSet)?)
|
||||
}
|
||||
|
||||
/// Deserializes a `WrappedAssetMeta`.
|
||||
pub fn wrapped_meta_deserialize(info: &AccountInfo) -> Result<WrappedAssetMeta, Error> {
|
||||
Ok(*Bridge::unpack(&mut info.data.borrow_mut())
|
||||
.map_err(|_| Error::ExpectedWrappedAssetMeta)?)
|
||||
}
|
||||
|
||||
/// Unpacks a state from a bytes buffer while assuring that the state is initialized.
|
||||
pub fn unpack<T: IsInitialized>(input: &mut [u8]) -> Result<&mut T, ProgramError> {
|
||||
let mut_ref: &mut T = Self::unpack_unchecked(input)?;
|
||||
|
|
Loading…
Reference in New Issue