Solidity
根據循環優化刪除數組的索引
我有一個 dapp,管理員可以在其中上傳許多蛋糕(20 到 30 個)。人們可以進入網站並決定是否要購買管理員上傳的蛋糕。
所以對於智能合約,我做了一個數組
uint
來儲存管理員上傳的每id
一個。Cake
一旦買家購買了其中一個蛋糕,它將刪除數組中的那個id
。Cake
我決定刪除它以盡可能避免循環,因為在我迭代數組以將剩餘的蛋糕列表返回給買家時,它會導致氣體耗盡。在優化方面聽起來如何?下面是我的實現。有沒有更好的解決方案,或者這足以繼續嗎?
struct Cake { uint id; address buyer; } mapping (uint => Cake) public cakes; uint[] public cakeIds; function addNewCake(uint _id) public { cakes[_id] = Cake(_id, 0x0); cakeIds.push(_id); } function buyCake(uint _id) public { Cake storage cake = cakes[_id]; cake.buyer = msg.sender; removeCakeInArray(_id); } function removeCakeInArray(uint _id) private { for (uint i = 0; i <= getNumOfCakes(); i++) { if (cakeIds[i] == _id) { remove(i); } } } function remove(uint index) private { if (index >= getNumOfCakes()) return; for (uint i = index; i < getNumOfCakes() - 1; i++){ cakeIds[i] = cakeIds[i+1]; } cakeIds.length--; } function getAllUnsoldCakes() public view returns (uint[]) { uint length = getNumOfCakes(); uint[] memory ids = new uint[](length); for (uint i = 0; i < length; i++) { uint cakeId = cakeIds[i]; Cake memory cake = cakes[cakeId]; if (cake.buyer == 0x0) { ids[i] = cake.id; } } return ids; } function getNumOfCakes() public view returns (uint) { return cakeIds.length; }
這些循環,特別是在 中
remove()
,是一個危險信號:你的數組可以增長到無限長度,包括如果有人惡意增加它,你的合約將達到 gas 限制並變得不可用。僅當您可以明確限制列表的長度時,這才可行 - 例如,如果有人嘗試添加第 11 個未售出的蛋糕,revert
即交易,因為蛋糕店的貨架已滿。如果您確實需要能夠使用solidity 函式呼叫檢索未售出的蛋糕列表,則更安全的方法是使用鍊錶,因為無論列表的長度如何,都可以以相同的成本進行更新。這仍然會讓您在讀取列表時擔心 gas 成本,但如果它僅用於從節點讀取數據,而不是作為事務的一部分被呼叫,那可能沒問題。
但是,您真的需要能夠通過合約函式呼叫獲得未售出的蛋糕列表嗎?將這項工作轉移到其他地方可能會更好 - 例如,當您添加或出售蛋糕時記錄事件,然後從您的 dapp javascript 中讀取這些事件並使用它來建構您向使用者顯示的列表。