#ethereum #solidity #smartcontracts
#ethereum #solidity #смарт-контракты
Вопрос:
Итак, у меня есть этот контракт, и финансирующий может получить общую сумму конкретной кампании с помощью функции getFundsByAddress. Проблема в том, что если у кампании более 30 тысяч учредителей, контракт не сможет выполнить код, потому что ему нужно будет пройти через 30 тысяч раз, чтобы найти все правильные адреса
В Rinkeby newwork максимальный цикл, которого он может достичь, равен 30 кб, после чего возвращается 0
Как я могу разрешить такие случаи?
contract CrowdFunding {
struct Funder {
address addr;
uint amount;
}
struct Campaign {
address beneficiary;
uint numFunders;
uint amount;
mapping (uint => Funder) funders;
}
uint numCampaigns;
Campaign[] public campaigns;
function newCampaign() public returns (uint campaignID) {
campaignID = campaigns.length ;
Campaign storage c = campaigns[campaignID];
c.beneficiary = msg.sender;
}
function contribute(uint _campaignID, uint _amount) public {
Campaign storage c = campaigns[_campaignID];
c.funders[c.numFunders ] = Funder({addr: msg.sender, amount: _amount});
c.amount = 100;
}
// not tested
function getFundsByAddress() public view returns (uint[] memory) {
Campaign storage c = campaigns[0];
uint cont = c.numFunders;
uint[] memory allAmount = new uint[](TotalAmountOfUser);
uint counter = 0;
for (uint i=0; i < cont; i ) {
if (c.funders[counter].addr == msg.sender) {
allAmount[amountCont] = c.funders[counter].amount;
}
counter ;
}
return allAmount;
}
}
Ответ №1:
Я не вижу ничего особенного в числе 30K, которое могло бы это объяснить.
Ваша проблема может заключаться в том, что у транзакции либо заканчивается газ, либо она достигает предела объема газа в блоке. Если вам приходится перебирать массив и вы не можете сделать это каким-либо другим способом, вам следует рассмотреть возможность перебора массива в нескольких транзакциях (т. е. 0-9999, 10.000-19.999, …).
Однако перебор такого количества записей будет довольно дорогим с точки зрения газа, который в реальной сети стоит денег. Но если это невозможно сделать другим способом, то вышесказанное должно вам помочь.
Комментарии:
1. Есть ли какие-либо проблемы с циклированием по огромному массиву внутри функции просмотра?
2. Потенциально да. Хотя это ничего не стоит с точки зрения оплаты, функции просмотра по-прежнему используют gas внутри, чтобы избежать бесконечных циклов, поэтому наличие огромного цикла может привести к тому, что у вашей функции закончится газ и произойдет сбой. Это ничего вам не будет стоить, но сделает вашу функцию непригодной для использования. Однако, пока у функции не заканчивается топливо, все должно быть в порядке, поскольку вы не платите за функции просмотра.
Ответ №2:
Трудно угадать, что getFundsByAddress
предполагается делать, потому что код не компилируется, и цикл, похоже, ничего не делает. (Переменная цикла i
никогда не используется.)
Но если мне нужно было угадать, он должен возвращать сумму взносов, внесенных вызывающей стороной. Если это так, просто отслеживайте это общее количество по мере внесения взносов и вообще избегайте цикла:
mapping(address => uint256) public totalContributions;
function contribute(uint _campaignID, uint _amount) public {
...
// Add this line to keep track of the total.
totalContributions[msg.sender] = _amount;
}
// No need for getFundsByAddress at all because a call to `totalContributions(address)`
// (the auto-generated getter) does the trick.
// But if you want a function that returns specifically `msg.sender`'s total:
function getMyContributions() external view returns (uint256) {
return totalContributions[msg.sender];
}