Skip to content

[Bug]: Calling a Function on No-code Address Should Return Empty Vec<u8>, and Not Revert #115

@0xNeshi

Description

@0xNeshi

What happened?

Adding this unit test to SafeErc20:

    #[storage]
    #[entrypoint]
    struct SafeErc20Example {
        safe_erc20: SafeErc20,
    }

    #[public]
    #[implements(ISafeErc20<Error = Error>)]
    impl SafeErc20Example {}

    #[public]
    impl ISafeErc20 for SafeErc20Example {
        type Error = Error;

        fn safe_transfer(
            &mut self,
            token: Address,
            to: Address,
            value: U256,
        ) -> Result<(), Self::Error> {
            self.safe_erc20.safe_transfer(token, to, value)
        }

        fn safe_transfer_from(
            &mut self,
            token: Address,
            from: Address,
            to: Address,
            value: U256,
        ) -> Result<(), Self::Error> {
            self.safe_erc20.safe_transfer_from(token, from, to, value)
        }

        fn try_safe_transfer(
            &mut self,
            token: Address,
            to: Address,
            value: U256,
        ) -> bool {
            self.safe_erc20.try_safe_transfer(token, to, value)
        }

        fn try_safe_transfer_from(
            &mut self,
            token: Address,
            from: Address,
            to: Address,
            value: U256,
        ) -> bool {
            self.safe_erc20.try_safe_transfer_from(token, from, to, value)
        }

        fn safe_increase_allowance(
            &mut self,
            token: Address,
            spender: Address,
            value: U256,
        ) -> Result<(), Self::Error> {
            self.safe_erc20.safe_increase_allowance(token, spender, value)
        }

        fn safe_decrease_allowance(
            &mut self,
            token: Address,
            spender: Address,
            requested_decrease: U256,
        ) -> Result<(), Self::Error> {
            self.safe_erc20.safe_decrease_allowance(
                token,
                spender,
                requested_decrease,
            )
        }

        fn force_approve(
            &mut self,
            token: Address,
            spender: Address,
            value: U256,
        ) -> Result<(), Self::Error> {
            self.safe_erc20.force_approve(token, spender, value)
        }

        fn transfer_and_call_relaxed(
            &mut self,
            token: Address,
            to: Address,
            value: U256,
            data: Bytes,
        ) -> Result<(), Self::Error> {
            self.safe_erc20.transfer_and_call_relaxed(token, to, value, data)
        }

        fn transfer_from_and_call_relaxed(
            &mut self,
            token: Address,
            from: Address,
            to: Address,
            value: U256,
            data: Bytes,
        ) -> Result<(), Self::Error> {
            self.safe_erc20
                .transfer_from_and_call_relaxed(token, from, to, value, data)
        }

        fn approve_and_call_relaxed(
            &mut self,
            token: Address,
            spender: Address,
            value: U256,
            data: Bytes,
        ) -> Result<(), Self::Error> {
            self.safe_erc20
                .approve_and_call_relaxed(token, spender, value, data)
        }
    }

        #[motsu::test]
        fn reverts_on_transfer(
            contract: Contract<SafeErc20Example>,
            has_no_code: Address,
            alice: Address,
        ) {
            let result = contract.sender(alice).safe_transfer(
                has_no_code,
                alice,
                U256::ZERO,
            );
            assert!(matches!(
                result,
                Err(Error::SafeErc20FailedOperation(SafeErc20FailedOperation {
                    token
                })) if token == has_no_code
            ));
        }

and running it results in:

thread 'token::erc20::utils::safe_erc20::tests::address_with_no_code::reverts_on_transfer' panicked at /home/nenad/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/motsu-0.10.0/src/storage_access.rs:52:21:
contract should be initialised first
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

thread 'token::erc20::utils::safe_erc20::tests::address_with_no_code::reverts_on_transfer' panicked at library/core/src/panicking.rs:225:5:
panic in a function that cannot unwind
stack backtrace:
...

This does not align with actual blockchain behavior, where invoking a function on an address with no code results in a successful tx with empty result.

platform

  • linux
  • windows
  • macos

Expected behavior

"Uninitialized" contracts should be treated as EOAs. Invoking functions against them should behave the same as it does in an actual blockchain environment, which means: successful tx + empty result.

The above test should succeed.

Contribution Guidelines

  • I agree to follow this project's Contribution Guidelines

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    Projects

    Status

    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions