Skip to main content

Delegate call

What it does

Checks for delegate calls to contracts passed as arguments.

Why is this bad?

Delegate calls to contracts passed as arguments can be used to change the expected behavior of the contract. If you need to change the target of a delegate call, you should use a storage variable, and make a function with proper access control to change it.

Example

    #[ink(message)]
pub fn delegate_call(&mut self, target: Hash, argument: Balance) {
let selector_bytes = [0x0, 0x0, 0x0, 0x0];
let result: T = build_call::<DefaultEnvironment>()
.delegate(target)
.exec_input(
ExecutionInput::new(Selector::new(selector_bytes))
.push_arg(argument)
)
.returns::<T>()
.invoke();
}

Use instead:

    #[ink(message)]
pub fn delegate_call(&mut self, argument: Balance) {
let selector_bytes = [0x0, 0x0, 0x0, 0x0];
let result: T = build_call::<DefaultEnvironment>()
.delegate(self.target)
.exec_input(
ExecutionInput::new(Selector::new(selector_bytes))
.push_arg(argument)
)
.returns::<T>()
.invoke();
}

#[ink::message]
pub fn set_target(&mut self, new_target: Hash) -> Result<(), Error> {
if self.admin != self.env().caller() {
Err(Error::Unauthorized)
} else {
self.target = new_target;
Ok(())
}
}

Implementation

The detector's implementation can be found at this link.