Solidity
猜數字遊戲,不工作
我正在嘗試在智能合約上編寫一個遊戲,這是一個玩家必須輸入 5 次猜測的遊戲,並且根據它們與實際數字的接近程度,他們將獲得乙太幣作為獎品。
猜數字:
// SPDX-License-Identifier: MIT pragma solidity >= 0.7.0 < 0.9.0; contract Oracle { address owner; uint public variableRand; constructor () { owner = msg.sender; } function setRandomVariable (uint _varRand) external { require (msg.sender == owner); variableRand = _varRand; } } contract Play { // Saves the value of what you have won in the session address owner; uint public currentPrize = 0; int[] public currentGuess; uint[] public randNum; event Log(uint gas); function showPoolBalance() public view returns(uint) { return address(this).balance; } function randMod() public view returns(uint) { // grab information from the blockchain randomly to generate random numbers - we need something dynamically changing // abi.encodePacked concatonates arguments nicely return uint(keccak256(abi.encodePacked(oracle.variableRand, block.timestamp, block.difficulty, msg.sender))) % 1000; } function inputGuess(int _guess) public { // Adds the guesses to the array currentGuess.push(_guess); } function clearGuesses() public { for (uint i = 0; i < currentGuess.length; i++) { //removes all of the guesses in the array currentGuess.pop(); } } function abs(int x) private pure returns (int) { //returns the abolute value of the input return x >= 0 ? x : -x; } function game() private returns (int, uint) { for (uint i = 0; i <= 5; i++) { randNum[i] = randMod(); int absv = abs(int(randNum[i]) - currentGuess[i]); if (absv < 6 && absv > 0) { currentPrize += 2; } else if (absv == 0) { currentPrize += 4; } else if (absv < 16 && absv > 5) { currentPrize += 1; } return (absv , randNum[i]); } } function play() public payable returns (int, uint) { require(msg.value == (10 ether), "You must pay with 10 ethers!"); game(); } function chargePool() public payable { require (msg.value >= 10 ether); } function withdrawPrize() public payable { payable(msg.sender).transfer(currentPrize); } fallback () external payable { emit Log(gasleft()); } Oracle oracle; constructor (address oracleAddress) { oracle = Oracle(oracleAddress); owner = msg.sender; } }
Oracle 合約部署正確。Play 合約也將部署。猜測的數字會被正確地推入數組,但是當我想用 10 乙太呼叫 play 函式時,會出現以下錯誤:
$$ vm $$從:0x5B3…eddC4 到:Play.play() 0xf8e…9fBe8value:10000000000000000000 weidata:0x93e…84cd9logs:0hash:0xfd3…dcad2 交易到 Play.play 錯誤:VM 錯誤:還原。 revert 事務已恢復到初始狀態。注意:如果您發送值並且您發送的值應該小於您目前的餘額,則呼叫的函式應該是應付的。調試事務以獲取更多資訊。
任何幫助,將不勝感激。
我發現你的程式碼可能出了什麼問題。
這是
randNum
數組的使用。它是動態的,因此您可以使用.push()
它來添加元素,但您沒有指定大小,因此您不能使用randNum[i]
,因為這些索引可能還不存在。我將聲明更改為:uint[] public randNum = new uint[](5);
看一看:
//SPDX-License-Identifier: MIT pragma solidity ^0.8.6; contract Oracle { address public owner; uint public variableRand; constructor () { owner = msg.sender; } function setRandomVariable (uint _varRand) external { require (msg.sender == owner, "Not owner"); variableRand = _varRand; } } contract Play { // Saves the value of what you have won in the session address owner; uint public currentPrize = 0; int[] public currentGuess; uint[] public randNum = new uint[](5); Oracle oracle; constructor(Oracle _oracle) { oracle = _oracle; } event Log(uint gas); function showPoolBalance() public view returns(uint) { return address(this).balance; } function randMod() public view returns(uint) { // grab information from the blockchain randomly to generate random numbers - we need something dynamically changing // abi.encodePacked concatonates arguments nicely return uint(keccak256(abi.encodePacked(oracle.variableRand(), block.timestamp, block.difficulty, msg.sender))) % 1000; } function inputGuess(int _guess) public { // Adds the guesses to the array currentGuess.push(_guess); } function clearGuesses() public { for (uint i = 0; i < currentGuess.length; i++) { //removes all of the guesses in the array currentGuess.pop(); } } function abs(int x) private pure returns (int) { //returns the abolute value of the input return x >= 0 ? x : -x; } function game() private returns (int, uint) { for (uint i = 0; i <= 5; i++) { randNum[i] = randMod(); int absv = abs(int(randNum[i]) - currentGuess[i]); if (absv < 6 && absv > 0) { currentPrize += 2; } else if (absv == 0) { currentPrize += 4; } else if (absv < 16 && absv > 5) { currentPrize += 1; } return (absv , randNum[i]); } } function play() public payable returns (int, uint) { require(msg.value == (10 ether), "You must pay with 10 ethers!"); game(); } }
除此之外,我看到您的程式碼確實可以在許多方面得到改進。
例如,
return (absv , randNum[i]);
在循環結束時在循環內部和 any 外部做什麼if
?循環只會執行一次然後返回。也許那不是你想要的。看看這個可能對您有幫助的新實現。
看一下
playv2
andplayv3
,尤其是playv3
,它將記錄保存SenderGuess
到senderGuesses
可以幫助您調試的映射中。這是一個想法,您可以根據需要對其進行修改。//SPDX-License-Identifier: MIT pragma solidity ^0.8.6; contract Contract { event Payment(address indexed from, address indexed to, string recipientName); function sendPayment(address from, address payable to, string memory recipientName) public payable { to.transfer(msg.value); emit Payment(from, to, recipientName); } } contract Oracle { address public owner; uint public variableRand; constructor () { owner = msg.sender; } function setRandomVariable (uint _varRand) external { require (msg.sender == owner, "Not owner"); variableRand = _varRand; } } contract Play { // Saves the value of what you have won in the session address owner; uint public currentPrize = 0; int[] public currentGuess; uint[] public randNum = new uint[](5); Oracle oracle; mapping(address => SenderGuess[]) public senderGuesses; struct SenderGuess { address sender; uint256 blockNumber; uint256[] guesses; uint256 diff; uint256 prize; } constructor(Oracle _oracle) { oracle = _oracle; } event Log(uint gas); function showPoolBalance() public view returns(uint) { return address(this).balance; } function randMod() public view returns(uint) { // grab information from the blockchain randomly to generate random numbers - we need something dynamically changing // abi.encodePacked concatonates arguments nicely return uint(keccak256(abi.encodePacked(oracle.variableRand(), block.timestamp, block.difficulty, msg.sender))) % 1000; } function inputGuess(int _guess) public { // Adds the guesses to the array currentGuess.push(_guess); } function clearGuesses() public { for (uint i = 0; i < currentGuess.length; i++) { //removes all of the guesses in the array currentGuess.pop(); } } function abs(int x) private pure returns (int) { //returns the abolute value of the input return x >= 0 ? x : -x; } function game() private returns (int, uint) { for (uint i = 0; i <= 5; i++) { randNum[i] = randMod(); int absv = abs(int(randNum[i]) - currentGuess[i]); if (absv < 6 && absv > 0) { currentPrize += 2; } else if (absv == 0) { currentPrize += 4; } else if (absv < 16 && absv > 5) { currentPrize += 1; } return (absv , randNum[i]); } } function play() public payable returns (int, uint) { require(msg.value == (10 ether), "You must pay with 10 ethers!"); game(); } // v2 // This version gets closest guess and and assigns the prize multiple times. function playv2(uint256[] memory guesses) public payable returns (uint256) { require(msg.value == (10 ether), "You must pay with 10 ethers!"); require(guesses.length == 5, "You need to send 5 guesses"); uint256 _currentPrize = 10; // Getting the 'random' mod number only once because if the call to oracle.variableRand() does not return // a different "random" number, then the randMod function will return the same thing anyways. uint256 randNumber = randMod(); for(uint256 i = 0; i < guesses.length; i++) { uint256 guess = guesses[i]; uint256 min = randNumber > guess ? randNumber : guess; uint256 max = guess > randNumber ? guess : randNumber; uint256 diff = max - min; if (diff == 0) { _currentPrize += 4; } else if (diff < 6) { _currentPrize += 2; } else if (diff < 16) { _currentPrize += 1; } } return _currentPrize; } // v3 // This version gets closest guess and and assigns the prize only once. function playv3(uint256[] memory guesses) public payable { require(msg.value == (10 ether), "You must pay with 10 ethers!"); require(guesses.length == 5, "You need to send 5 guesses"); uint256 _currentPrize = 10; // Getting the 'random' mod number only once because if the call to oracle.variableRand() does not return // a different "random" number, then the randMod function will return the same thing anyways. uint256 randNumber = randMod(); uint256 smallestDiff = 16; for(uint256 i = 0; i < guesses.length; i++) { uint256 guess = guesses[i]; uint256 min = randNumber > guess ? randNumber : guess; uint256 max = guess > randNumber ? guess : randNumber; uint256 diff = max - min; if(diff < smallestDiff) { smallestDiff = diff; } } if (smallestDiff == 0) { _currentPrize += 4; } else if (smallestDiff < 6) { _currentPrize += 2; } else if (smallestDiff < 16) { _currentPrize += 1; } SenderGuess memory senderGuess = SenderGuess(msg.sender, block.number, guesses, smallestDiff, _currentPrize); senderGuesses[msg.sender].push(senderGuess); } }