0

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. – goodvibration Dec 13 '20 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? – ThickMiddleManager Dec 13 '20 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??? – goodvibration Dec 13 '20 at 8:00
  • You can't simply divide by 23 because the random number can be less than 23. – ThickMiddleManager Dec 13 '20 at 18:27
  • 1
    I did not say divide, I said modulo! – goodvibration Dec 13 '20 at 19:24
1

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 – ThickMiddleManager Dec 14 '20 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. – Rob Hitchens Dec 14 '20 at 2:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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