diff --git a/srml/contract/src/exec.rs b/srml/contract/src/exec.rs index ba3e0ff9676ec..7becbdd11c5f5 100644 --- a/srml/contract/src/exec.rs +++ b/srml/contract/src/exec.rs @@ -172,6 +172,18 @@ impl<'a, T: Trait> ExecutionContext<'a, T> { } } +/// Transfer some funds from `transactor` to `dest`. +/// +/// All balance changes are performed in the `overlay`. +/// +/// This function also handles charging the fee. The fee depends +/// on whether the transfer happening because of contract creation +/// (transfering endowment), specified by `contract_create` flag, +/// or because of a transfer via `call`. +/// +/// Note, that the fee is denominated in `T::Balance` units, but +/// charged in `T::Gas` from the provided `gas_meter`. This means +/// that the actual amount charged might differ. fn transfer( gas_meter: &mut GasMeter, contract_create: bool, @@ -180,7 +192,7 @@ fn transfer( value: T::Balance, overlay: &mut OverlayAccountDb, ) -> Result<(), &'static str> { - let would_create = overlay.get_balance(transactor).is_zero(); + let would_create = overlay.get_balance(dest).is_zero(); let fee: T::Balance = if contract_create { >::contract_fee() diff --git a/srml/contract/src/tests.rs b/srml/contract/src/tests.rs index 17f5a4d88aac9..cda965312df81 100644 --- a/srml/contract/src/tests.rs +++ b/srml/contract/src/tests.rs @@ -70,6 +70,8 @@ struct ExtBuilder { existential_deposit: u64, gas_price: u64, block_gas_limit: u64, + transfer_fee: u64, + creation_fee: u64, } impl Default for ExtBuilder { fn default() -> Self { @@ -77,6 +79,8 @@ impl Default for ExtBuilder { existential_deposit: 0, gas_price: 2, block_gas_limit: 100_000_000, + transfer_fee: 0, + creation_fee: 0, } } } @@ -93,6 +97,14 @@ impl ExtBuilder { self.block_gas_limit = block_gas_limit; self } + fn transfer_fee(mut self, transfer_fee: u64) -> Self { + self.transfer_fee = transfer_fee; + self + } + fn creation_fee(mut self, creation_fee: u64) -> Self { + self.creation_fee = creation_fee; + self + } fn build(self) -> runtime_io::TestExternalities { let mut t = system::GenesisConfig::::default() .build_storage() @@ -103,8 +115,8 @@ impl ExtBuilder { transaction_base_fee: 0, transaction_byte_fee: 0, existential_deposit: self.existential_deposit, - transfer_fee: 0, - creation_fee: 0, + transfer_fee: self.transfer_fee, + creation_fee: self.creation_fee, reclaim_rebate: 0, }.build_storage() .unwrap(), @@ -195,6 +207,85 @@ fn contract_transfer() { }); } +#[test] +fn contract_transfer_takes_creation_fee() { + const CONTRACT_SHOULD_TRANSFER_VALUE: u64 = 6; + const CONTRACT_SHOULD_TRANSFER_TO: u64 = 9; + + let code_transfer = wabt::wat2wasm(CODE_TRANSFER).unwrap(); + + with_externalities(&mut ExtBuilder::default().creation_fee(105).build(), || { + >::insert(1, code_transfer.to_vec()); + + Balances::set_free_balance(&0, 100_000_000); + Balances::increase_total_stake_by(100_000_000); + Balances::set_free_balance(&1, 11); + Balances::increase_total_stake_by(11); + + assert_ok!(Contract::call(Origin::signed(0), 1, 3, 100_000, Vec::new())); + + assert_eq!( + Balances::free_balance(&0), + // 3 - value sent with the transaction + // 2 * 10 - gas used by the contract (10) multiplied by gas price (2) + // 2 * 135 - base gas fee for call (by transaction) + // 2 * 135 - base gas fee for call (by the contract) + // 104 - (rounded) fee per creation (by the contract) + 100_000_000 - 3 - (2 * 10) - (2 * 135) - (2 * 135) - 104, + ); + assert_eq!( + Balances::free_balance(&1), + 11 + 3 - CONTRACT_SHOULD_TRANSFER_VALUE, + ); + assert_eq!( + Balances::free_balance(&CONTRACT_SHOULD_TRANSFER_TO), + CONTRACT_SHOULD_TRANSFER_VALUE, + ); + }); +} + +#[test] +fn contract_transfer_takes_transfer_fee() { + const CONTRACT_SHOULD_TRANSFER_VALUE: u64 = 6; + const CONTRACT_SHOULD_TRANSFER_TO: u64 = 9; + + let code_transfer = wabt::wat2wasm(CODE_TRANSFER).unwrap(); + + with_externalities(&mut ExtBuilder::default().creation_fee(105).transfer_fee(45).build(), || { + >::insert(1, code_transfer.to_vec()); + + Balances::set_free_balance(&0, 100_000_000); + Balances::increase_total_stake_by(100_000_000); + Balances::set_free_balance(&1, 11); + Balances::increase_total_stake_by(11); + + // Create destination account here so we can check that transfer fee + // is charged (and creation fee is not). + Balances::set_free_balance(&CONTRACT_SHOULD_TRANSFER_TO, 25); + + assert_ok!(Contract::call(Origin::signed(0), 1, 3, 100_000, Vec::new())); + + assert_eq!( + Balances::free_balance(&0), + // 3 - value sent with the transaction + // 2 * 10 - gas used by the contract (10) multiplied by gas price (2) + // 2 * 135 - base gas fee for call (by transaction) + // 44 - (rounded from 45) fee per transfer (by transaction) + // 2 * 135 - base gas fee for call (by the contract) + // 44 - (rounded from 45) fee per transfer (by the contract) + 100_000_000 - 3 - (2 * 10) - (2 * 135) - 44 - (2 * 135) - 44, + ); + assert_eq!( + Balances::free_balance(&1), + 11 + 3 - CONTRACT_SHOULD_TRANSFER_VALUE, + ); + assert_eq!( + Balances::free_balance(&CONTRACT_SHOULD_TRANSFER_TO), + 25 + CONTRACT_SHOULD_TRANSFER_VALUE, + ); + }); +} + #[test] fn contract_transfer_oog() { const CONTRACT_SHOULD_TRANSFER_TO: u64 = 9;