1

I am trying to compile following code:

pragma solidity 0.5.9;
contract test{
//https://github.com/ethereum/solidity/issues/3469

address payable owner;
mapping (address => bool) pd;
uint public sS;
function lnA(address addr) public view returns(uint n) {
        // 1 in 8 chance
        n = uint(keccak256(uint(addr), sS)[0]) % 8;

    }
}

solc keccakSyntaxerr.sol

keccakSyntaxerr.sol:10:18: Error: Wrong argument count for function call: 2 arguments given but expected 1. This function requires a single bytes argument. Use abi.encodePacked(...) to obtain the pre-0.5.0 behaviour or abi.encode(...) to use ABI encoding.

n = uint(keccak256(uint(addr), sS)[0]) % 8; ^-----------------------^ keccakSyntaxerr.sol:10:13: Error: Explicit type conversion not allowed from "bytes1" to "uint256". n = uint(keccak256(uint(addr), sS)[0]) % 8; ^--------------------------------^

How to convert the above syntax into lastest Solidity versions? Somebody please guide me.

Zulfi.

1

You have to covert the arguments to a bytes. abi.encodePacked(args) is a way to do it.

pragma solidity 0.5.9;
contract test{
    //https://github.com/ethereum/solidity/issues/3469

    address payable owner;
    mapping (address => bool) pd;
    uint public sS;

    function lnA(address addr) public view returns(uint8 n) {
        // 1 in 8 chance
        n = uint8(keccak256(abi.encodePacked(addr, sS))[0]) % 8;
    }
}

I dropped uint(addr) because padding the address with extra zeroes isn't needed for the "1 in 8" objective described in the remark.

As a PSA: Be sure you fully understand the attack vectors for a game based on randomness if that is the goal. This is entirely predictable and an adversary can brute force it to get a desired result.

Hope it helps.

UPDATE

The 1 byte step doesn't add any entropy to this (there isn't any). You can:

// 1 in 8 chance
n = uint(keccak256(abi.encodePacked(addr, sS))) % 8;

Where n is a uint. That may save a little gas or be a little more readable.

| improve this answer | |
  • 1
    In previous compilers, conversion from 256-bit to 8-bit uint was implicit but now the rules require explicit conversion so the compiler can catch more errors. The [0] takes the first byte from the bytes returned from abi.encode... - 1 byte. That gets converted to uint8 which is natural because a byte is 8 bits. – Rob Hitchens Mar 25 at 3:25
  • 1
    Solidity won't let you mix up type casting between functions. You can explicitly convert with uint(uint8(3)) to pad with lots of 0 or you can uint8(uint(256)) to discover how bits will be lost. The potentially disastrous effect of losing bits is a reason why the conversion has to be explicit. Another is it helps flag errors and oversights, which is a good thing. Think about the casting you want and stick to it. Usually the largest precision you will ever need. – Rob Hitchens Mar 25 at 8:06
  • 1
    That remark is from the original question, so we assume that is part of the goal. % 8, modulo 8 is a number between 0 and 7. You keccak looks like an attempt to make even distribution. Even distribution != random. See the warning in the answer. – Rob Hitchens Apr 2 at 4:19
  • 1
    In the code you are trying to convert, it is uint8. uint and uint256 are the same. Conversions have to be explicit. Your 0-7 range of possible results will fit in a uint8. There are subtle considerations for optimization but the main thing is consistency. – Rob Hitchens Apr 10 at 22:53
  • 1
    It goes keccak =-> bytes32 which is convertable to uint256 which is convertable to uint8 (with loss of leftmost bits). – Rob Hitchens Apr 11 at 8:43

Not the answer you're looking for? Browse other questions tagged or ask your own question.