Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix errors found in the TBRv3 Solana Audit #105

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open

Conversation

feli-xlabs
Copy link
Contributor

  • Allow to create accounts by hand even if they're squatted.
  • Refactor the program ownership transfer so that we only need the new owner's signature when accepting the transfer.
  • Check both the chain ID and the peer address in case of an inbound transfer

@feli-xlabs feli-xlabs marked this pull request as ready for review January 9, 2025 18:53
Copy link
Contributor

@nonergodic nonergodic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only comments/nits, nothing that's really important.

Comment on lines +476 to +485
const ixs = [];

ixs.push(this.registerPeer(signer, chain, peerAddress));
if (config.maxGasDropoffMicroToken !== 0) {
ixs.push(this.updateMaxGasDropoff(signer, chain, config.maxGasDropoffMicroToken));
}
ixs.push(this.updateBaseFee(signer, chain, config.relayerFeeMicroUsd));
ixs.push(this.setPauseForOutboundTransfers(signer, chain, config.pausedOutboundTransfers));

return Promise.all(ixs);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const ixs = [];
ixs.push(this.registerPeer(signer, chain, peerAddress));
if (config.maxGasDropoffMicroToken !== 0) {
ixs.push(this.updateMaxGasDropoff(signer, chain, config.maxGasDropoffMicroToken));
}
ixs.push(this.updateBaseFee(signer, chain, config.relayerFeeMicroUsd));
ixs.push(this.setPauseForOutboundTransfers(signer, chain, config.pausedOutboundTransfers));
return Promise.all(ixs);
return Promise.all([
this.registerPeer(signer, chain, peerAddress),
...(config.maxGasDropoffMicroToken !== 0
? [this.updateMaxGasDropoff(signer, chain, config.maxGasDropoffMicroToken))]
: []
),
this.updateBaseFee(signer, chain, config.relayerFeeMicroUsd),
this.setPauseForOutboundTransfers(signer, chain, config.pausedOutboundTransfers)
]);

nit/style suggestion originally mentioned on slack

@@ -144,6 +144,8 @@ pub fn complete_transfer(
} = *ctx.accounts.vaa.message().data();
let gas_dropoff_amount = denormalize_dropoff_to_lamports(gas_dropoff_amount);

let _x = ctx.accounts.vaa.message();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems like a leftover

@@ -116,7 +116,7 @@ pub struct OutboundTransfer<'info> {
/// CHECK: Token Bridge emitter.
pub token_bridge_emitter: UncheckedAccount<'info>,

/// CHECK: Token Bridge sequence.
/// CHECK: Token Bridge sequence. Mutable.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems redundant and also not in line with e.g. the fee_recipient comment above, which is also mutable but isn't commented as such either.

Comment on lines +60 to +89
let total_fees_mwei = (|| {
let evm_transaction_fee_mwei = config
.evm_transaction_gas
.checked_mul(u64::from(oracle_evm_prices.gas_price))?;
let evm_tx_size_fee_mwei = config
.evm_transaction_size
.checked_mul(u64::from(oracle_evm_prices.price_per_byte))?;
let dropoff_mwei = u64::from(dropoff_amount_micro).checked_mul(MWEI_PER_MICRO_ETH)?;

evm_transaction_fee_mwei
.checked_add(evm_tx_size_fee_mwei)?
.checked_add(dropoff_mwei)
})()
.ok_or(TokenBridgeRelayerError::Overflow)?;

// μusd = Mwei * μusd/Token / Mwei/Token + μusd)
let total_fees_micro_usd = u64::try_from(
u128::from(total_fees_mwei) * u128::from(oracle_evm_prices.gas_token_price) / MWEI_PER_ETH,
)
.expect("Overflow")
+ u64::from(chain_config.relayer_fee_micro_usd);
.map_err(|_| TokenBridgeRelayerError::Overflow)?
.checked_add(u64::from(chain_config.relayer_fee_micro_usd))
.ok_or(TokenBridgeRelayerError::Overflow)?;

// lamports/SOL * μusd / μusd/SOL
Ok((LAMPORTS_PER_SOL * total_fees_micro_usd) / oracle_config.sol_price)
let fee = total_fees_micro_usd
.checked_mul(LAMPORTS_PER_SOL)
.map(|sol| sol / oracle_config.sol_price)
.ok_or(TokenBridgeRelayerError::Overflow)?;

Ok(fee)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see where the motivation for the custom type with a single, simple overflow check at the end that you mentioned came from. Only a mother can love this code.

@@ -18,6 +18,9 @@ pub mod constant {

#[constant]
pub const SEED_PREFIX_TEMPORARY: &[u8] = b"tmp";

#[constant]
pub const SEED_PREFIX_UPGRADE_LOCK: &[u8] = b"upgrade lock";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub const SEED_PREFIX_UPGRADE_LOCK: &[u8] = b"upgrade lock";
pub const SEED_PREFIX_UPGRADE_LOCK: &[u8] = b"upgrade_lock";

I think we canonically use underscore instead of a plain space in seed prefixes

userTokenAccount: tokenAccount.publicKey,
transferredAmount,
gasDropoffAmount,
maxFeeLamports: 100_000_000n, // 0.1SOL max
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably use LAMPORT_PER_SOL/10 rather than use a comment (and same for the other values)

Comment on lines +106 to +108
type Tuple<T, N extends number, R extends T[] = []> = R['length'] extends N
? R
: Tuple<T, N, [T, ...R]>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should (probably) use the corresponding type from the SDKv2 instead (it's very marginal though).

Comment on lines +140 to +141
several: <N extends number>(amount: N): Tuple<Keypair, N> =>
Array.from({ length: amount }).map(Keypair.generate) as Tuple<Keypair, N>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same considerations as above.

Comment on lines +151 to +158
several: <N extends number>(
amount: number,
ethereum?: 'ethereum',
): Tuple<UniversalAddress, N> =>
Array.from({ length: amount }).map(() => this.universalAddress.generate(ethereum)) as Tuple<
UniversalAddress,
N
>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same considerations as above.

Comment on lines +131 to +132
several: <N extends number>(amount: number): Tuple<PublicKey, N> =>
Array.from({ length: amount }).map(PublicKey.unique) as Tuple<PublicKey, N>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason not to just make this an optional parameter of generate? also, N should be const here and amount seems like a suboptimal choice of name since it is more associated with tokens. count would likely be better.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants