Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

[contracts] Fixed the bug with transfer value for delegate call #11771

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions frame/contracts/common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ bitflags! {
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "std", serde(rename_all = "camelCase", transparent))]
pub struct ReturnFlags: u32 {
/// If all bits are zero it is successful case.
const SUCCESS = 0x0;
xgreenx marked this conversation as resolved.
Show resolved Hide resolved
/// If this bit is set all changes made by the contract execution are rolled back.
const REVERT = 0x0000_0001;
}
Expand Down
82 changes: 80 additions & 2 deletions frame/contracts/src/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -797,8 +797,10 @@ where
)?;
}

// Every call or instantiate also optionally transferres balance.
self.initial_transfer()?;
// Every non delegate call or instantiate also optionally transfers the balance.
if self.top_frame().delegate_caller.is_none() {
self.initial_transfer()?;
}
athei marked this conversation as resolved.
Show resolved Hide resolved

// Call into the wasm blob.
let output = executable
Expand Down Expand Up @@ -1560,6 +1562,82 @@ mod tests {
});
}

#[test]
fn correct_transfer_on_call() {
let origin = ALICE;
let dest = BOB;
let value = 55;

let success_ch = MockLoader::insert(Call, move |ctx, _| {
assert_eq!(ctx.ext.value_transferred(), value);
Ok(ExecReturnValue { flags: ReturnFlags::SUCCESS, data: Bytes(Vec::new()) })
xgreenx marked this conversation as resolved.
Show resolved Hide resolved
});

ExtBuilder::default().build().execute_with(|| {
let schedule = <Test as Config>::Schedule::get();
place_contract(&dest, success_ch);
set_balance(&origin, 100);
let balance = get_balance(&dest);
let mut storage_meter = storage::meter::Meter::new(&origin, Some(0), 55).unwrap();

let _ = MockStack::run_call(
origin.clone(),
dest.clone(),
&mut GasMeter::<Test>::new(GAS_LIMIT),
&mut storage_meter,
&schedule,
value,
vec![],
None,
)
.unwrap();

assert_eq!(get_balance(&origin), 100 - value);
assert_eq!(get_balance(&dest), balance + value);
});
}

#[test]
fn correct_transfer_on_delegate_call() {
let origin = ALICE;
let dest = BOB;
let value = 35;

let success_ch = MockLoader::insert(Call, move |ctx, _| {
assert_eq!(ctx.ext.value_transferred(), value);
Ok(ExecReturnValue { flags: ReturnFlags::SUCCESS, data: Bytes(Vec::new()) })
xgreenx marked this conversation as resolved.
Show resolved Hide resolved
});

let delegate_ch = MockLoader::insert(Call, move |ctx, _| {
assert_eq!(ctx.ext.value_transferred(), value);
let _ = ctx.ext.delegate_call(success_ch, Vec::new())?;
Ok(ExecReturnValue { flags: ReturnFlags::SUCCESS, data: Bytes(Vec::new()) })
xgreenx marked this conversation as resolved.
Show resolved Hide resolved
});

ExtBuilder::default().build().execute_with(|| {
let schedule = <Test as Config>::Schedule::get();
place_contract(&dest, delegate_ch);
set_balance(&origin, 100);
let balance = get_balance(&dest);
let mut storage_meter = storage::meter::Meter::new(&origin, Some(0), 55).unwrap();

let _ = MockStack::run_call(
origin.clone(),
dest.clone(),
&mut GasMeter::<Test>::new(GAS_LIMIT),
&mut storage_meter,
&schedule,
value,
vec![],
None,
)
.unwrap();

assert_eq!(get_balance(&origin), 100 - value);
assert_eq!(get_balance(&dest), balance + value);
});
}

#[test]
fn changes_are_reverted_on_failing_call() {
// This test verifies that changes are reverted on a call which fails (or equally, returns
Expand Down