1

In JS you could use this and call it a day.

let randomArrayEntry = myArray[Math.floor(Math.random() * myArray.length)];

However, in Solidity everything is deterministic unless you call an outside function for a random number. Easy in theory, but the problem is that it is not currently practical with available services to choose a number matching your array length.

If my array length is 23, and the oracle spits out 1823792836199027320224 as the seed we can reduce it down to the last two numbers % 10**2. Now we have the number 24 and we now have a range from 0-99 of possible numbers. How can we then choose fairly from the array using this number without spending a bunch of gas iterating over it?

The first solution I came up with is to check for the first two numbers in a row less than 24, and then that becomes the answer. The problem is that the randomly generated number might not have any two numbers in a row less than our array length. This becomes much more likely if our number is 2 or 3. The transaction would have to revert or fetch another random number as a fallback. This seems like a poor plan.

Edit 1:

The current solution (found after) is to just take the remainder.

uint256 randomArrayEntry = (randomNumber + myArray.length + 10) % myArray.length;

You multiply the random number by 10 so you don't divide zero or less than the array length (no remainder).

5
  • You haven't quite explained what's wrong with a simple oracleOutput % 23. Dec 13, 2020 at 7:15
  • I found this after, but am still unsure if this arithmetic will lead to problems. 1400 gas is about what it is now, but it will need multiple operations of the same thing. Is dividing and using the remainder really the best way? Dec 13, 2020 at 7:58
  • I have no idea what you're trying ask (or say) in this comment. It sounds like you're adding information unrelated of what I asked, so I will ask again - what's wrong with simply taking the value returned from the oracle modulo 23??? Dec 13, 2020 at 8:00
  • You can't simply divide by 23 because the random number can be less than 23. Dec 13, 2020 at 18:27
  • 1
    I did not say divide, I said modulo! Dec 13, 2020 at 19:24

1 Answer 1

3

Good Vibes is right. Modulo and you're done.

The discussion gives me the impression that you don't fully understand how the remainder function solves it. There is no divide by zero, no need to multiply x 10. Just do:

random % array.length and you will get a number from 0 to array.length-1 which is precisely what you want.

0 % <anything> = 0
3 % 9 = 3
23 % 23 = 0
24 % 23 = 1
1212121212456453535356453121212121 % 23 = something from 0-22

It's not free, of course, but it's a simple math op. Further efforts to complicate the process will probably add expensive steps instead of improving performance.

This is as random as the random number that feeds it.

Hope it helps.

2
  • Good read, that would be affirmative. I've never ran into a problem in my life where I needed the modulo operator. I had to think about this 10 minutes, but I now understand because enumerated all the ways I thought it could fail. :-) I'm still looking for a proof to discuss why this won't favor one number. When it comes to smart contracts I'm full Art Bell/Alex Jones on conspiracies on how it will fail, and I will be sorry for not fully understanding something. Simply putting a sign in my contract that it believes in arithmetic, and that no number is illegal doesn't seem to help. :P Dec 14, 2020 at 0:55
  • 1
    The remainder idea is completely fair. The random number you start with needs to be evenly distributed which is, of course, a property of a truly random number. Dec 14, 2020 at 2:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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