Called function suddenly needs to be payable - blockchain

I have a public uint variable denoting 'which round' it is and a function that advances rounds and does processing alongside the round advancement:
uint public round;
function completeRound() public inPaused() inRound() {
if (round == 6) {
// win
} else {
reduceByHalf();
round.add(1);
}
}
If i run this in remix, it runs 4 times and then consistently fails on the 5th, indicating that a function suddenly needs to be payable:
transact to Playingwithsmartcontracts.completeRound errored: VM error: revert. revert The transaction has been reverted to the initial state. Note: The called function should be payable if you send value and the value you send should be less than your current balance. Debug the transaction to get more information.
If I comment out round.add(1) right under where reduceByHalf is called, the code works all day long. I can click it indefinitely with no errors in Remix.
Strangely, this started as an Enum to track the rounds and that had the same exact problem. While advancing the enum, i could do it 5 times before the above failure and commenting it out made everything work.
reduceByHalf code doesnt seem to be the offender, but it is shown below in case it has a bearing on the problem:
struct Foo {
address owner;
uint mintedRound;
uint winningRound;
}
struct FooOwner {
uint[] foos;
uint totalWinningFoos;
}
uint[][5] roundFoos;
uint[][5] roundWinners;
mapping(uint => Foo) public winningFoos;
mapping(address => FooOwner) public fooOwners;
uint totalWinningFoos;
function shuffleFoos (uint256[] memory _array) internal view returns(uint[] memory){
uint[] memory clone = cloneArray(_array, _array.length);
for (uint256 i = 0; i < clone.length; i++) {
uint256 n = i + uint256(keccak256(abi.encodePacked(msg.sender, block.timestamp))) % (clone.length - i);
uint256 temp = clone[n];
clone[n] = clone[i];
clone[i] = temp;
}
return clone;
}
function cloneArray(uint256[] memory _array, uint256 _length) internal pure returns (uint256[] memory) {
uint256[] memory array = new uint256[](_length);
for (uint256 i = 0; i < _length; i++) {
array[i] = _array[i];
}
return array;
}
function reduceByHalf() internal {
uint[] memory clone = shuffleFoos(roundFoos[round]);
uint halfLength = 0;
halfLength = roundFoos[round].length.div(2);
for (uint w = 0; w < halfLength; w++) {
uint fooId = clone[w];
roundWinners[round].push(fooId);
winningFoos[round].winningRound = round;
address fooOwner = winningFoos[fooId].owner;
fooOwners[fooOwner].totalWinningFoos = fooOwners[fooOwner].totalWinningFoos.add(1);
}
totalWinningFoos = totalWinningFoos.add(halfLength);
}
As far as I know, I am not sending value, and not sure why it only thinks im sending value on transaction execution 5.
Would anyone be able to help me understand what Remix/Solidity is mad about?
I totally must not be understanding somehthing but it looks like it's something about the number 5... I can advance the round to 6, but as soon as I set the uint value to 5 is when I start seeing these problems.... so wierd....

The transaction has been reverted to the initial state.
This is the important part of the error message in your case.
Note: The called function should be payable if you send value
This is just a note, possibly because this combination often happens. But since your function and transaction doesn't send any value, it doesn't apply to your case.
round.add(1);
This (failing) snippet suggests, that there's supposed to be a library used for uint, but it's not defined. I'm gonna go with the SameMath library, because of the .add() function name and the use on uint. But in theory, it could be any library, SafeMath is just the most probable option in this context.
Mind that round.add(1); (using SafeMath) returns the value of round incremented by 1, but it doesn't store the (incremented) value anywhere. This looks like a typo and the real usage should be round = round.add(1);
Your code doesn't show any usage of the SafeMath library, but also doesn't show the Solidity version, so I'm going to divide my answer into 3 parts.
You're using Solidity 0.8+.
SameMath is not needed, because integer overflow is handled on a lower level, and you can safely replace
// even with correctly imported SafeMath, it doesn't update the stored value
round.add(1);
to
// updates the stored value
round++;
You're using Solidity 0.7 or older, and SafeMath for uint256 (not uint)
Change the definition
uint public round;
to
uint256 public round;
This way, SafeMath will be used for round and it will allow to use the function .add().
Mind that you might want to also store the incremented value, see the bold paragraph with example above.
You're using Solidity 0.7 or older, and not using SafeMath at all.
You need to import the SafeMath library and then make changes described in the point 2.

Related

what is storage slot in forge-std and how to set specific number for it?

I'm try to write smart contract based on forge-std, and writing some test by solidity looks like this:
function testOutOfToken() public {
vm.store(
address(nftToken),
bytes32(uint256(7)),
bytes32(uint256(10000))
);
vm.expectRevert(abi.encodeWithSignature("MaxSupplyReached()"));
nftToken.mintNft{value: 0.15 ether}(1);
}
the second parameter is bytes32(uint256(7)), from the document is explained like this:
// Stores a value to an address' storage slot, (who, slot, value)
function store(address,bytes32,bytes32) external;
but I still don't understand what storage slot is, if I change the 7 to some other number like 8, the test won't pass. Any idea? Thanks!
Each property of a contract can have some value, and the value is stored in a predetermined location - in a predetermined slot.
For 256bit scalar types, the slot ID is calculated simply by the property position in the code. Smaller types are packed into the same slot (used to be separate in older Solidity versions). And dynamic types are located in slots determined by a hash.
Docs: https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html
pragma solidity ^0.8;
contract MyContract {
uint256 numberA = 1; // slot 0
uint256 numberB = 1; // slot 1
uint256 numberC = 1; // slot 2
uint128 numberD = 1; // slot 3
uint128 numberE = 1; // slot 3
// length in slot 4
// values in slot ID determined by hash of the position + offset
// in this case keccak256(4) + 0, keccak256(4) + 1, and keccak256(4) + 2
uint256[] numbers;
constructor() {
numbers.push(2);
numbers.push(3);
numbers.push(4);
}
}
if I change the 7 to some other number like 8, the test won't pass
Most likely the value that your test is checking against, is stored in slot ID 7. And slot ID 8 contains some other value (or the default value of 0). Since the other slot doesn't contain the expected value, the test fails.

Solidity array's dynamic size best practices

I am coding a NFT marketplace smart contract and I came across a problem regarding arrays in solidity.
I have a function that retrieve an array of token based on an array of ids. Because the size of the Id array is dynamic, so is the size of the output array.
The function :
function fetchUnsoldMarketItems(uint256[] memory _tokenIds) public view returns (MarketItem[] memory) {
uint arrayLen = _tokenIds.length;
uint itemsCount = 0;
for (uint i = 0; i < arrayLen; i++) {
if (idToMarketItem[_tokenIds[i]].sold == false) {
itemsCount +=1;
}
}
MarketItem[] memory items = new MarketItem[](itemsCount);
for (uint i = 0; i < arrayLen; i++) {
if (idToMarketItem[_tokenIds[i]].sold == false) {
MarketItem storage currentItem = idToMarketItem[_tokenIds[i]];
items[i] = currentItem;
}
}
return items;
}
First, is there a better way to code this function and to retrieve the unsold items?
Also, if a MarketItem is being sold/bought during the execution of the function, the length of the second array will being bigger/smaller than expected. How can I prevent that ?
I am looking for any sort of advice as I am still a beginner with solidity !
It is not efficient to save to nft in your solidity code the best practices is to save them in mangodb or sql db and reteive this info from this db

Is access by pointer so expensive?

I've a Process() function that is called very heavy within my DLL (VST plugin) loaded in a DAW (Host software), such as:
for (int i = 0; i < nFrames; i++) {
// ...
for (int voiceIndex = 0; voiceIndex < PLUG_VOICES_BUFFER_SIZE; voiceIndex++) {
Voice &voice = pVoiceManager->mVoices[voiceIndex];
if (voice.mIsPlaying) {
for (int envelopeIndex = 0; envelopeIndex < ENVELOPES_CONTAINER_NUM_ENVELOPE_MANAGER; envelopeIndex++) {
Envelope &envelope = pEnvelopeManager[envelopeIndex]->mEnvelope;
envelope.Process(voice);
}
}
}
}
void Envelope::Process(Voice &voice) {
if (mIsEnabled) {
// update value
mValue[voice.mIndex] = (mBlockStartAmp[voice.mIndex] + (mBlockStep[voice.mIndex] * mBlockFraction[voice.mIndex]));
}
else {
mValue[voice.mIndex] = 0.0;
}
}
It basically takes 2% of CPU within the Host (which is nice).
Now, if I slightly change the code to this (which basically are increments and assignment):
void Envelope::Process(Voice &voice) {
if (mIsEnabled) {
// update value
mValue[voice.mIndex] = (mBlockStartAmp[voice.mIndex] + (mBlockStep[voice.mIndex] * mBlockFraction[voice.mIndex]));
// next phase
mBlockStep[voice.mIndex] += mRate;
mStep[voice.mIndex] += mRate;
}
else {
mValue[voice.mIndex] = 0.0;
}
// connectors
mOutputConnector_CV.mPolyValue[voice.mIndex] = mValue[voice.mIndex];
}
CPU go to 6/7% (note, those var don't interact with other part of codes, or at least I think so).
The only reason I can think is that access to pointer is heavy? How can I reduce this amount of CPU?
Those arrays are basic double "pointer" arrays (the most lighter C++ container):
double mValue[PLUG_VOICES_BUFFER_SIZE];
double mBlockStartAmp[PLUG_VOICES_BUFFER_SIZE];
double mBlockFraction[PLUG_VOICES_BUFFER_SIZE];
double mBlockStep[PLUG_VOICES_BUFFER_SIZE];
double mStep[PLUG_VOICES_BUFFER_SIZE];
OutputConnector mOutputConnector_CV;
Any suggestions?
You might be thinking that "pointer arrays" are the lightest containers. but CPU's don't think in terms of containers. They just read and write values through pointers.
The problem here might very well be that you know that two containers do not overlap (there are no "sub-containers"). But the CPU might not be told that by the compiler. Writing to mBlockStep might affect mBlockFraction. The compiler doesn't have run-time values, so it needs to handle the case where it does. This will mean introducing more memory reads, and less caching of values in registers.
Pack all the data items in a structure and create an array of structure. I would simply use a vector.
In Process function get the single element out of this vector, and use its parameters. At the cache-line/instruction level, all items would be (efficiently) brought into local cache (L1), as the data element (members of struct) as contiguous. Use reference or pointer of struct type to avoid copying.
Try to use integer data-types unless double is needed.
EDIT:
struct VoiceInfo
{
double mValue;
...
};
VoiceInfo voices[PLUG_VOICES_BUFFER_SIZE];
// Or vector<VoiceInfo> voices;
...
void Envelope::Process(Voice &voice)
{
// Get the object (by ref/pointer)
VoiceInfo& info = voices[voice.mIndex];
// Work with reference 'info'
...
}

bool method inside a class ''Control may reach end o non-void function''

So, i have a class called Vuelo, it has a method in which i can add a passenger to an airplane flight, i must check that the passenger id is not already in the array (the array is at first with all zeros), i must also check that there is enough space for another passenger to be added (max 10)
bool Vuelo :: agregarPasajero(int id)
{
int i = 0;
for(int iC = 0; iC < 10; iC++)
{
if (listaPasajeros[iC] == id)
{
i++;
return false;
}
}
if(i == 0)
{
if(cantidadPasajeros >= 10)
{
return false;
}
else
{
return true;
cantidadPasajeros++;
}
}
}
If i is not zero, you get to the end of the function without any kind of return statement. Since you declared the function to always return a bool, you should provide one for that case.
Now, you may know that i will never be zero at that spot, but the logic for that is fairly complex (I missed it on the first reading), and a compiler cannot be expected to realize that there is in fact no chance of control flow ever getting to the end of the function without encountering a return. In this case it's best to add a dummy return.
You can probably get away with not having a dummy return if you remove the bogus i == 0 test. i will necessarily always be zero at that point, since if it were ever increased, the function immediately returns false.
The statement cantidadPasajeros++; will never be executed since it is located after a return statement. Any halfway decent compiler also warns on that.

Halting looping till the service result is obtained in wp7

I am new to Windows Phone Development.
I am using a for loop to call webservice for each element in the list. I want to go to the next loop only after the result from service is obtained. My loop is like this:
for (int i = 0; i < count; i++)
{
selectedInfo = lstReorderItems[i];
if (string.IsNullOrEmpty(selectedInfo.ItemCostVariantValueIDs))
{
client.AddItemstoCartCompleted += client_AddItemstoCartCompleted;
client.AddItemstoCartAsync(//params);
}
else
{
client.CheckItemQuantityInCartCompleted += client_CheckItemQuantityInCartCompleted;
client.CheckItemQuantityInCartAsync(//params);
}
}
But the loop continues without waiting the result to come. So can anyone help me out?
After going through the question, what i understood is, you are making series web service call. Which in my opinion is not good, because to get final result user have to wait, until all the service call gets finished and there is chance to get failure response in mid way, which will also block final result. So go for parallel calls, to support that WebClient is designed with background options.
If it is completely necessary, then declare the looping variable as class member
Public class xxxxxx
{
int i = (startCount ) 0;
Then start the service as usual,
selectedInfo = lstReorderItems[i];
if (string.IsNullOrEmpty(selectedInfo.ItemCostVariantValueIDs))
{
client.AddItemstoCartCompleted += client_AddItemstoCartCompleted;
client.AddItemstoCartAsync(//params);
}
at the service response completed event, in your case client_AddItemstoCartCompleted call the same service after incrementing looping variable, before that you have to compare looping variable with count
as
if(i< count)
{
i++;
selectedInfo = lstReorderItems[i];
if (string.IsNullOrEmpty(selectedInfo.ItemCostVariantValueIDs))
{
client.AddItemstoCartAsync(//params);
}
}