mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 12:19:24 +00:00
Fix gas estimation discrepancy when gas isn't monotonic
This commit is contained in:
@@ -241,9 +241,12 @@ impl Router {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The worst-case gas cost for a legacy transaction which executes this batch.
|
/// The worst-case gas cost for a legacy transaction which executes this batch.
|
||||||
///
|
pub fn execute_gas_and_fee(
|
||||||
/// This assumes the fee will be non-zero.
|
&self,
|
||||||
pub fn execute_gas(&self, coin: Coin, fee_per_gas: U256, outs: &OutInstructions) -> u64 {
|
coin: Coin,
|
||||||
|
fee_per_gas: U256,
|
||||||
|
outs: &OutInstructions,
|
||||||
|
) -> (u64, U256) {
|
||||||
// Unfortunately, we can't cache this in self, despite the following code being written such
|
// Unfortunately, we can't cache this in self, despite the following code being written such
|
||||||
// that a common EVM instance could be used, as revm's types aren't Send/Sync and we expect the
|
// that a common EVM instance could be used, as revm's types aren't Send/Sync and we expect the
|
||||||
// Router to be send/sync
|
// Router to be send/sync
|
||||||
@@ -252,10 +255,11 @@ impl Router {
|
|||||||
Coin::Erc20(erc20) => Some(erc20),
|
Coin::Erc20(erc20) => Some(erc20),
|
||||||
});
|
});
|
||||||
|
|
||||||
let fee = match coin {
|
let shimmed_fee = match coin {
|
||||||
Coin::Ether => {
|
Coin::Ether => {
|
||||||
// Use a fee of 1 so the fee payment is recognized as positive-value
|
// Use a fee of 1 so the fee payment is recognized as positive-value, if the fee is
|
||||||
let fee = U256::from(1);
|
// non-zero
|
||||||
|
let fee = if fee_per_gas == U256::ZERO { U256::ZERO } else { U256::ONE };
|
||||||
|
|
||||||
// Set a balance of the amount sent out to ensure we don't error on that premise
|
// Set a balance of the amount sent out to ensure we don't error on that premise
|
||||||
gas_estimator.data.ctx.modify_db(|db| {
|
gas_estimator.data.ctx.modify_db(|db| {
|
||||||
@@ -274,7 +278,7 @@ impl Router {
|
|||||||
// Use a nonce of 1
|
// Use a nonce of 1
|
||||||
ProjectivePoint::GENERATOR,
|
ProjectivePoint::GENERATOR,
|
||||||
&public_key,
|
&public_key,
|
||||||
&Self::execute_message(CHAIN_ID, 1, coin, fee, outs.clone()),
|
&Self::execute_message(CHAIN_ID, 1, coin, shimmed_fee, outs.clone()),
|
||||||
);
|
);
|
||||||
let s = Scalar::ONE + (c * private_key);
|
let s = Scalar::ONE + (c * private_key);
|
||||||
let sig = Signature::new(c, s).unwrap();
|
let sig = Signature::new(c, s).unwrap();
|
||||||
@@ -305,7 +309,7 @@ impl Router {
|
|||||||
tx.data = abi::executeCall::new((
|
tx.data = abi::executeCall::new((
|
||||||
abi::Signature::from(&sig),
|
abi::Signature::from(&sig),
|
||||||
Address::from(coin),
|
Address::from(coin),
|
||||||
fee,
|
shimmed_fee,
|
||||||
outs.0.clone(),
|
outs.0.clone(),
|
||||||
))
|
))
|
||||||
.abi_encode()
|
.abi_encode()
|
||||||
@@ -330,8 +334,14 @@ impl Router {
|
|||||||
};
|
};
|
||||||
gas += gas_estimator.into_inspector().unused_gas;
|
gas += gas_estimator.into_inspector().unused_gas;
|
||||||
|
|
||||||
// The transaction uses gas based on the amount of non-zero bytes in the calldata, which is
|
/*
|
||||||
// variable to the fee, which is variable to the gas used. This iterates until parity
|
The transaction pays an initial gas fee which is dependent on the length of the calldata and
|
||||||
|
the amount of non-zero bytes in the calldata. This is variable to the fee, which was prior
|
||||||
|
shimmed to be `1`.
|
||||||
|
|
||||||
|
Here, we calculate the actual fee, and update the initial gas fee accordingly. We then update
|
||||||
|
the fee again, until the initial gas fee stops increasing.
|
||||||
|
*/
|
||||||
let initial_gas = |fee, sig| {
|
let initial_gas = |fee, sig| {
|
||||||
let gas = calculate_initial_tx_gas(
|
let gas = calculate_initial_tx_gas(
|
||||||
SPEC_ID,
|
SPEC_ID,
|
||||||
@@ -344,26 +354,37 @@ impl Router {
|
|||||||
assert_eq!(gas.floor_gas, 0);
|
assert_eq!(gas.floor_gas, 0);
|
||||||
gas.initial_gas
|
gas.initial_gas
|
||||||
};
|
};
|
||||||
let mut current_initial_gas = initial_gas(fee, abi::Signature::from(&sig));
|
let mut current_initial_gas = initial_gas(shimmed_fee, abi::Signature::from(&sig));
|
||||||
|
// Remove the current initial gas from the transaction's gas
|
||||||
|
gas -= current_initial_gas;
|
||||||
loop {
|
loop {
|
||||||
let fee = fee_per_gas * U256::from(gas);
|
// Calculate the would-be fee
|
||||||
|
let fee = fee_per_gas * U256::from(gas + current_initial_gas);
|
||||||
|
// Calculate the would-be gas for this fee
|
||||||
let new_initial_gas =
|
let new_initial_gas =
|
||||||
initial_gas(fee, abi::Signature { c: [0xff; 32].into(), s: [0xff; 32].into() });
|
initial_gas(fee, abi::Signature { c: [0xff; 32].into(), s: [0xff; 32].into() });
|
||||||
|
// If the values are equal, or if it went down, return
|
||||||
|
/*
|
||||||
|
The gas will decrease if the new fee has more zero bytes in its encoding. Further
|
||||||
|
iterations are unhelpful as they'll simply loop infinitely for some inputs. Accordingly, we
|
||||||
|
return the current fee (which is for a very slightly higher gas rate) with the decreased
|
||||||
|
gas to ensure this algorithm terminates.
|
||||||
|
*/
|
||||||
if current_initial_gas >= new_initial_gas {
|
if current_initial_gas >= new_initial_gas {
|
||||||
return gas;
|
return (gas + new_initial_gas, fee);
|
||||||
}
|
}
|
||||||
|
// Update what the current initial gas is
|
||||||
gas += new_initial_gas - current_initial_gas;
|
|
||||||
current_initial_gas = new_initial_gas;
|
current_initial_gas = new_initial_gas;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The estimated fee for this `OutInstruction`.
|
/// The estimated gas for this `OutInstruction`.
|
||||||
///
|
///
|
||||||
/// This does not model the quadratic costs incurred when in a batch, nor other misc costs such
|
/// This does not model the quadratic costs incurred when in a batch, nor other misc costs such
|
||||||
/// as the potential to cause one less zero byte in the fee's encoding. This is intended to
|
/// as the potential to cause one less zero byte in the fee's encoding. This is intended to
|
||||||
/// produce a per-`OutInstruction` fee to deduct from each `OutInstruction`, before all
|
/// produce a per-`OutInstruction` value which can be ratioed against others to decide the fee to
|
||||||
/// `OutInstruction`s incur an amortized fee of what remains for the batch itself.
|
/// deduct from each `OutInstruction`, before all `OutInstruction`s incur an amortized fee of
|
||||||
|
/// what remains for the batch itself.
|
||||||
pub fn execute_out_instruction_gas_estimate(
|
pub fn execute_out_instruction_gas_estimate(
|
||||||
&mut self,
|
&mut self,
|
||||||
coin: Coin,
|
coin: Coin,
|
||||||
@@ -372,11 +393,12 @@ impl Router {
|
|||||||
#[allow(clippy::map_entry)] // clippy doesn't realize the multiple mutable borrows
|
#[allow(clippy::map_entry)] // clippy doesn't realize the multiple mutable borrows
|
||||||
if !self.empty_execute_gas.contains_key(&coin) {
|
if !self.empty_execute_gas.contains_key(&coin) {
|
||||||
// This can't be de-duplicated across ERC20s due to the zero bytes in the address
|
// This can't be de-duplicated across ERC20s due to the zero bytes in the address
|
||||||
let gas = self.execute_gas(coin, U256::from(0), &OutInstructions(vec![]));
|
let (gas, _fee) = self.execute_gas_and_fee(coin, U256::from(0), &OutInstructions(vec![]));
|
||||||
self.empty_execute_gas.insert(coin, gas);
|
self.empty_execute_gas.insert(coin, gas);
|
||||||
}
|
}
|
||||||
|
|
||||||
let gas = self.execute_gas(coin, U256::from(0), &OutInstructions(vec![instruction]));
|
let (gas, _fee) =
|
||||||
|
self.execute_gas_and_fee(coin, U256::from(0), &OutInstructions(vec![instruction]));
|
||||||
gas - self.empty_execute_gas[&coin]
|
gas - self.empty_execute_gas[&coin]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -540,8 +540,8 @@ async fn test_empty_execute() {
|
|||||||
test.confirm_next_serai_key().await;
|
test.confirm_next_serai_key().await;
|
||||||
|
|
||||||
{
|
{
|
||||||
let gas = test.router.execute_gas(Coin::Ether, U256::from(1), &[].as_slice().into());
|
let (gas, fee) =
|
||||||
let fee = U256::from(gas);
|
test.router.execute_gas_and_fee(Coin::Ether, U256::from(1), &[].as_slice().into());
|
||||||
|
|
||||||
let () = test
|
let () = test
|
||||||
.provider
|
.provider
|
||||||
@@ -581,8 +581,8 @@ async fn test_empty_execute() {
|
|||||||
assert_eq!(test.provider.estimate_gas(call).await.unwrap(), 21_000 + 16);
|
assert_eq!(test.provider.estimate_gas(call).await.unwrap(), 21_000 + 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
let gas = test.router.execute_gas(Coin::Erc20(token), U256::from(0), &[].as_slice().into());
|
let (gas, fee) =
|
||||||
let fee = U256::from(0);
|
test.router.execute_gas_and_fee(Coin::Erc20(token), U256::from(0), &[].as_slice().into());
|
||||||
let (_tx, gas_used) = test.execute(Coin::Erc20(token), fee, [].as_slice().into(), vec![]).await;
|
let (_tx, gas_used) = test.execute(Coin::Erc20(token), fee, [].as_slice().into(), vec![]).await;
|
||||||
const UNUSED_GAS: u64 = Router::GAS_FOR_ERC20_CALL - 16;
|
const UNUSED_GAS: u64 = Router::GAS_FOR_ERC20_CALL - 16;
|
||||||
assert_eq!(gas_used + UNUSED_GAS, gas);
|
assert_eq!(gas_used + UNUSED_GAS, gas);
|
||||||
@@ -600,8 +600,7 @@ async fn test_eth_address_out_instruction() {
|
|||||||
let out_instructions =
|
let out_instructions =
|
||||||
OutInstructions::from([(SeraiEthereumAddress::Address(rand_address), amount_out)].as_slice());
|
OutInstructions::from([(SeraiEthereumAddress::Address(rand_address), amount_out)].as_slice());
|
||||||
|
|
||||||
let gas = test.router.execute_gas(Coin::Ether, U256::from(1), &out_instructions);
|
let (gas, fee) = test.router.execute_gas_and_fee(Coin::Ether, U256::from(1), &out_instructions);
|
||||||
let fee = U256::from(gas);
|
|
||||||
|
|
||||||
let () = test
|
let () = test
|
||||||
.provider
|
.provider
|
||||||
@@ -638,8 +637,7 @@ async fn test_erc20_address_out_instruction() {
|
|||||||
let out_instructions =
|
let out_instructions =
|
||||||
OutInstructions::from([(SeraiEthereumAddress::Address(rand_address), amount_out)].as_slice());
|
OutInstructions::from([(SeraiEthereumAddress::Address(rand_address), amount_out)].as_slice());
|
||||||
|
|
||||||
let gas = test.router.execute_gas(coin, U256::from(1), &out_instructions);
|
let (gas, fee) = test.router.execute_gas_and_fee(coin, U256::from(1), &out_instructions);
|
||||||
let fee = U256::from(gas);
|
|
||||||
|
|
||||||
// Mint to the Router the necessary amount of the ERC20
|
// Mint to the Router the necessary amount of the ERC20
|
||||||
erc20.mint(&test, test.router.address(), amount_out + fee).await;
|
erc20.mint(&test, test.router.address(), amount_out + fee).await;
|
||||||
@@ -674,8 +672,7 @@ async fn test_eth_code_out_instruction() {
|
|||||||
.as_slice(),
|
.as_slice(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let gas = test.router.execute_gas(Coin::Ether, U256::from(1), &out_instructions);
|
let (gas, fee) = test.router.execute_gas_and_fee(Coin::Ether, U256::from(1), &out_instructions);
|
||||||
let fee = U256::from(gas);
|
|
||||||
let (tx, gas_used) = test.execute(Coin::Ether, fee, out_instructions, vec![true]).await;
|
let (tx, gas_used) = test.execute(Coin::Ether, fee, out_instructions, vec![true]).await;
|
||||||
|
|
||||||
// We use call-traces here to determine how much gas was allowed but unused due to the complexity
|
// We use call-traces here to determine how much gas was allowed but unused due to the complexity
|
||||||
@@ -721,8 +718,7 @@ async fn test_eth_code_out_instruction_reverts() {
|
|||||||
.as_slice(),
|
.as_slice(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let gas = test.router.execute_gas(Coin::Ether, U256::from(1), &out_instructions);
|
let (gas, fee) = test.router.execute_gas_and_fee(Coin::Ether, U256::from(1), &out_instructions);
|
||||||
let fee = U256::from(gas);
|
|
||||||
let (tx, gas_used) = test.execute(Coin::Ether, fee, out_instructions, vec![true]).await;
|
let (tx, gas_used) = test.execute(Coin::Ether, fee, out_instructions, vec![true]).await;
|
||||||
|
|
||||||
let unused_gas = test.gas_unused_by_calls(&tx).await;
|
let unused_gas = test.gas_unused_by_calls(&tx).await;
|
||||||
@@ -744,8 +740,7 @@ async fn test_erc20_code_out_instruction() {
|
|||||||
.as_slice(),
|
.as_slice(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let gas = test.router.execute_gas(coin, U256::from(1), &out_instructions);
|
let (gas, fee) = test.router.execute_gas_and_fee(coin, U256::from(1), &out_instructions);
|
||||||
let fee = U256::from(gas);
|
|
||||||
|
|
||||||
// Mint to the Router the necessary amount of the ERC20
|
// Mint to the Router the necessary amount of the ERC20
|
||||||
erc20.mint(&test, test.router.address(), amount_out + fee).await;
|
erc20.mint(&test, test.router.address(), amount_out + fee).await;
|
||||||
@@ -777,11 +772,11 @@ async fn test_result_decoding() {
|
|||||||
.as_slice(),
|
.as_slice(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let gas = test.router.execute_gas(Coin::Ether, U256::from(0), &out_instructions);
|
let (gas, fee) = test.router.execute_gas_and_fee(Coin::Ether, U256::from(0), &out_instructions);
|
||||||
|
|
||||||
// We should decode these in the correct order (not `false, true, true`)
|
// We should decode these in the correct order (not `false, true, true`)
|
||||||
let (_tx, gas_used) =
|
let (_tx, gas_used) =
|
||||||
test.execute(Coin::Ether, U256::from(0), out_instructions, vec![true, true, false]).await;
|
test.execute(Coin::Ether, fee, out_instructions, vec![true, true, false]).await;
|
||||||
// We don't check strict equality as we don't know how much gas was used by the reverted call
|
// We don't check strict equality as we don't know how much gas was used by the reverted call
|
||||||
// (even with the trace), solely that it used less than or equal to the limit
|
// (even with the trace), solely that it used less than or equal to the limit
|
||||||
assert!(gas_used <= gas);
|
assert!(gas_used <= gas);
|
||||||
@@ -817,9 +812,8 @@ async fn test_reentrancy() {
|
|||||||
.as_slice(),
|
.as_slice(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let gas = test.router.execute_gas(Coin::Ether, U256::from(0), &out_instructions);
|
let (gas, fee) = test.router.execute_gas_and_fee(Coin::Ether, U256::from(0), &out_instructions);
|
||||||
let (_tx, gas_used) =
|
let (_tx, gas_used) = test.execute(Coin::Ether, fee, out_instructions, vec![true]).await;
|
||||||
test.execute(Coin::Ether, U256::from(0), out_instructions, vec![true]).await;
|
|
||||||
// Even though this doesn't have failed `OutInstruction`s, our logic is incomplete upon any
|
// Even though this doesn't have failed `OutInstruction`s, our logic is incomplete upon any
|
||||||
// failed internal calls for some reason. That's fine, as the gas yielded is still the worst-case
|
// failed internal calls for some reason. That's fine, as the gas yielded is still the worst-case
|
||||||
// (which this isn't a counter-example to) and is validated to be the worst-case, but is peculiar
|
// (which this isn't a counter-example to) and is validated to be the worst-case, but is peculiar
|
||||||
@@ -883,8 +877,7 @@ async fn fuzz_test_out_instructions_gas() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let fee_per_gas = U256::from(1) + U256::from(OsRng.next_u64() % 10);
|
let fee_per_gas = U256::from(1) + U256::from(OsRng.next_u64() % 10);
|
||||||
let gas = test.router.execute_gas(coin, fee_per_gas, &out_instructions);
|
let (gas, fee) = test.router.execute_gas_and_fee(coin, fee_per_gas, &out_instructions);
|
||||||
let fee = U256::from(gas) * fee_per_gas;
|
|
||||||
// All of these should have succeeded
|
// All of these should have succeeded
|
||||||
let (tx, gas_used) =
|
let (tx, gas_used) =
|
||||||
test.execute(coin, fee, out_instructions.clone(), vec![true; out_instructions.0.len()]).await;
|
test.execute(coin, fee, out_instructions.clone(), vec![true; out_instructions.0.len()]).await;
|
||||||
@@ -896,3 +889,67 @@ async fn fuzz_test_out_instructions_gas() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_gas_increases_then_decreases() {
|
||||||
|
/*
|
||||||
|
This specific batch of `OutInstruction`s causes the gas to be initially calculated, and then
|
||||||
|
increase as the proper fee is written in (due to the increased amount of non-zero bytes). But
|
||||||
|
then, as the fee is updated until the final fee no longer increases the gas used, the gas
|
||||||
|
actually goes *back down*. To then derive the fee from this reduced gas causes the gas to go
|
||||||
|
back up.
|
||||||
|
|
||||||
|
A prior version of this library would return the reduced amount of gas fee in this edge case,
|
||||||
|
which only rarely appeared via the fuzz test (yet did once, yielding this). Then, it'd derive
|
||||||
|
the fee from it, and expect the realized transaction to have parity (causing a test failure as
|
||||||
|
it didn't). Now, `execute_gas` is `execute_gas_and_fee`, yielding both the gas which is
|
||||||
|
expected *and the fee for it*. This fee is guaranteed to cost the reported amount of gas,
|
||||||
|
resolving this issue.
|
||||||
|
*/
|
||||||
|
#[rustfmt::skip]
|
||||||
|
let out_instructions = vec![
|
||||||
|
(SeraiEthereumAddress::Address(**Address::from([73, 151, 53, 42, 64, 102, 196, 80, 244, 167, 149, 81, 236, 231, 65, 18, 68, 196, 173, 20])), U256::from(1u8)),
|
||||||
|
(SeraiEthereumAddress::Address(**Address::from([7, 232, 97, 33, 54, 141, 246, 45, 29, 138, 221, 30, 2, 179, 142, 165, 169, 45, 143, 126])), U256::from(1u8)),
|
||||||
|
(SeraiEthereumAddress::Contract(ContractDeployment::new(100000, vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]).unwrap()), U256::from(1u8)),
|
||||||
|
(SeraiEthereumAddress::Contract(ContractDeployment::new(100000, vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]).unwrap()), U256::from(1u8)),
|
||||||
|
(SeraiEthereumAddress::Address(**Address::from([73, 151, 53, 42, 64, 102, 196, 80, 244, 167, 149, 81, 236, 231, 65, 18, 68, 196, 173, 20])), U256::from(0u8)),
|
||||||
|
(SeraiEthereumAddress::Contract(ContractDeployment::new(100000, vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]).unwrap()), U256::from(1u8)),
|
||||||
|
(SeraiEthereumAddress::Address(**Address::from([34, 132, 167, 44, 12, 171, 57, 177, 197, 88, 60, 255, 68, 75, 2, 139, 76, 138, 78, 222])), U256::from(1u8)),
|
||||||
|
(SeraiEthereumAddress::Address(**Address::from([221, 218, 210, 119, 213, 189, 65, 118, 205, 113, 19, 11, 83, 58, 129, 203, 123, 76, 202, 99])), U256::from(0u8)),
|
||||||
|
(SeraiEthereumAddress::Contract(ContractDeployment::new(100000, vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]).unwrap()), U256::from(0u8)),
|
||||||
|
(SeraiEthereumAddress::Address(**Address::from([34, 132, 167, 44, 12, 171, 57, 177, 197, 88, 60, 255, 68, 75, 2, 139, 76, 138, 78, 222])), U256::from(0u8)),
|
||||||
|
(SeraiEthereumAddress::Address(**Address::from([82, 31, 116, 111, 9, 110, 56, 51, 122, 38, 10, 227, 36, 134, 181, 185, 255, 149, 195, 254])), U256::from(1u8)),
|
||||||
|
(SeraiEthereumAddress::Address(**Address::from([36, 68, 76, 140, 254, 107, 233, 107, 186, 85, 5, 37, 65, 201, 63, 17, 135, 244, 148, 1])), U256::from(1u8)),
|
||||||
|
(SeraiEthereumAddress::Contract(ContractDeployment::new(100000, vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]).unwrap()), U256::from(1u8)),
|
||||||
|
(SeraiEthereumAddress::Contract(ContractDeployment::new(100000, vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]).unwrap()), U256::from(0u8)),
|
||||||
|
(SeraiEthereumAddress::Address(**Address::from([34, 132, 167, 44, 12, 171, 57, 177, 197, 88, 60, 255, 68, 75, 2, 139, 76, 138, 78, 222])), U256::from(1u8)),
|
||||||
|
(SeraiEthereumAddress::Address(**Address::from([26, 54, 156, 184, 48, 50, 23, 219, 43, 54, 56, 131, 245, 126, 70, 17, 235, 56, 130, 124])), U256::from(1u8)),
|
||||||
|
(SeraiEthereumAddress::Address(**Address::from([152, 27, 239, 11, 196, 99, 61, 136, 23, 8, 58, 242, 166, 235, 106, 167, 45, 175, 69, 247])), U256::from(0u8)),
|
||||||
|
(SeraiEthereumAddress::Address(**Address::from([152, 27, 239, 11, 196, 99, 61, 136, 23, 8, 58, 242, 166, 235, 106, 167, 45, 175, 69, 247])), U256::from(0u8)),
|
||||||
|
(SeraiEthereumAddress::Contract(ContractDeployment::new(100000, vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]).unwrap()), U256::from(0u8)),
|
||||||
|
(SeraiEthereumAddress::Contract(ContractDeployment::new(100000, vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]).unwrap()), U256::from(0u8)),
|
||||||
|
(SeraiEthereumAddress::Address(**Address::from([7, 232, 97, 33, 54, 141, 246, 45, 29, 138, 221, 30, 2, 179, 142, 165, 169, 45, 143, 126])), U256::from(0u8)),
|
||||||
|
(SeraiEthereumAddress::Contract(ContractDeployment::new(100000, vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]).unwrap()), U256::from(0u8)),
|
||||||
|
(SeraiEthereumAddress::Contract(ContractDeployment::new(100000, vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]).unwrap()), U256::from(0u8)),
|
||||||
|
(SeraiEthereumAddress::Contract(ContractDeployment::new(100000, vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]).unwrap()), U256::from(1u8))
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut test = Test::new().await;
|
||||||
|
test.confirm_next_serai_key().await;
|
||||||
|
|
||||||
|
let out_instructions = OutInstructions::from(out_instructions.as_slice());
|
||||||
|
|
||||||
|
let coin = {
|
||||||
|
let erc20 = Erc20::deploy(&test).await;
|
||||||
|
erc20.mint(&test, test.router.address(), U256::from(1_000_000_000)).await;
|
||||||
|
Coin::Erc20(erc20.address())
|
||||||
|
};
|
||||||
|
|
||||||
|
let fee_per_gas = U256::from(4);
|
||||||
|
let (gas, fee) = test.router.execute_gas_and_fee(coin, fee_per_gas, &out_instructions);
|
||||||
|
assert!((U256::from(gas) * fee_per_gas) != fee);
|
||||||
|
let (tx, gas_used) =
|
||||||
|
test.execute(coin, fee, out_instructions.clone(), vec![true; out_instructions.0.len()]).await;
|
||||||
|
let unused_gas = test.gas_unused_by_calls(&tx).await;
|
||||||
|
assert_eq!(gas_used + unused_gas, gas);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user