verge下最节省gas的智能合约写法

ops/2024/12/27 3:19:43/

改什么

把map改成array

比如我们现在有三种优先队列的实现.

当前mainet下最节省gas

library HeapMapping {using SafeCast for *;struct Uint256Heap {//键是节点在堆中的位置(索引)//值是该位置的父节点的索引。//通过这个映射,可以快速找到每个节点的父节点,从而维护堆的性质。     mapping(uint32 pos=> uint32 father) tree;//这个映射用于存储堆中的所有节点。键是节点的索引,值是一个 `Node` 结构体,包含该节点的值和在堆中的位置。mapping(uint32 pos=> Node) items;//表示当前堆中元素的数量。uint32 size;//这个变量用于跟踪下一个可用的索引,以便在插入新节点时使用。它通常会在插入操作中递增,以确保每个新节点都有一个唯一的索引。uint32 nextItemIdx;}struct Node {//存储节点的值。这个值是优先队列中用于比较优先级的关键数据。uint256 value;//表示该节点在堆中的位置(索引)。这个索引用于在堆中快速定位节点,并在堆的操作(如插入和删除)中进行调整。uint32 heapIndex; // value -> position}function peek(Uint256Heap storage self) internal view returns (uint256) {return self.items[self.tree[0]].value;}function pop(Uint256Heap storage self) internal returns (uint256) {unchecked {uint32 last = --self.size;uint32 rootIdx = self.tree[0];uint256 rootValue = self.items[rootIdx].value;delete self.items[rootIdx];self.tree[0] = self.tree[last];self.items[self.tree[0]].heapIndex = 0;delete self.tree[last];_siftDown(self, last, 0);return rootValue;}}function insert(Uint256Heap storage self, uint256 value) internal {uint32 pos = self.size++;uint32 idx = self.nextItemIdx++;self.tree[pos] = idx;self.items[idx] = Node({ value: value, heapIndex: pos });_siftUp(self, pos, value);}function length(Uint256Heap storage self) internal view returns (uint32) {return self.size;}function _swap(Uint256Heap storage self, uint32 i, uint32 j) private {uint32 ii = self.tree[i];uint32 jj = self.tree[j];self.tree[i] = jj;self.tree[j] = ii;self.items[ii].heapIndex = j;self.items[jj].heapIndex = i;}function _siftDown(Uint256Heap storage self, uint32 size, uint32 pos) private {uint256 left = 2 * pos + 1; // this could overflow uint32uint256 right = 2 * pos + 2; // this could overflow uint32if (right < size) {// the check guarantees that `left` and `right` are both valid uint32uint32 lIndex = uint32(left);uint32 rIndex = uint32(right);uint256 lValue = self.items[self.tree[lIndex]].value;uint256 rValue = self.items[self.tree[rIndex]].value;uint256 value = self.items[pos].value;if (lValue < value || rValue < value) {if (lValue < rValue) {_swap(self, pos, lIndex);_siftDown(self, size, lIndex);} else {_swap(self, pos, rIndex);_siftDown(self, size, rIndex);}}} else if (left < size) {// the check guarantees that `left` is a valid uint32uint32 lIndex = uint32(left);uint256 lValue = self.items[self.tree[lIndex]].value;uint256 value = self.items[pos].value;if (lValue < value) {_swap(self, pos, lIndex);_siftDown(self, size, lIndex);}}}function _siftUp(Uint256Heap storage self,uint32 pos,uint256 value) private {unchecked {while (pos > 0) {uint32 parent = (pos - 1) / 2;uint256 parentValue = self.items[self.tree[parent]].value;if (parentValue < value) break;_swap(self, pos, parent);pos = parent;}}}
}

mapping implementation

library HeapArray {using SafeCast for *;struct Uint256Heap {Uint256HeapNode[] data;}struct Uint256HeapNode {uint256 value;uint32 index; // position -> valueuint32 lookup; // value -> position}function peek(Uint256Heap storage self) internal view returns (uint256) {return _unsafeNodeAccess(self, self.data[0].index).value;}function pop(Uint256Heap storage self) internal returns (uint256) {unchecked {uint32 size = length(self);if (size == 0) Panic.panic(Panic.EMPTY_ARRAY_POP);uint32 last = size - 1;// get root location (in the data array) and valueUint256HeapNode storage rootNode = _unsafeNodeAccess(self, 0);uint32 rootIdx = rootNode.index;Uint256HeapNode storage rootData = _unsafeNodeAccess(self, rootIdx);Uint256HeapNode storage lastNode = _unsafeNodeAccess(self, last);uint256 rootDataValue = rootData.value;// if root is not the last element of the data array (that will get pop-ed), reorder the data array.if (rootIdx != last) {// get details about the value stored in the last element of the array (that will get pop-ed)uint32 lastDataIdx = lastNode.lookup;uint256 lastDataValue = lastNode.value;// copy these values to the location of the root (that is safe, and that we no longer use)rootData.value = lastDataValue;rootData.lookup = lastDataIdx;// update the tree node that used to point to that last element (value now located where the root was)_unsafeNodeAccess(self, lastDataIdx).index = rootIdx;}// get last leaf location (in the data array) and valueuint32 lastIdx = lastNode.index;uint256 lastValue = _unsafeNodeAccess(self, lastIdx).value;// move the last leaf to the root, pop last leaf ...rootNode.index = lastIdx;_unsafeNodeAccess(self, lastIdx).lookup = 0;self.data.pop();// ... and heapify_siftDown(self, last, 0, lastValue);// return root valuereturn rootDataValue;}}function insert(Uint256Heap storage self, uint256 value) internal {uint32 size = length(self);if (size == type(uint32).max) Panic.panic(Panic.RESOURCE_ERROR);self.data.push(Uint256HeapNode({index: size, lookup: size, value: value}));_siftUp(self, size, value);}function length(Uint256Heap storage self) internal view returns (uint32) {return self.data.length.toUint32();}function clear(Uint256Heap storage self) internal {Uint256HeapNode[] storage data = self.data;/// @solidity memory-safe-assemblyassembly {sstore(data.slot, 0)}}function _swap(Uint256Heap storage self, uint32 i, uint32 j) private {Uint256HeapNode storage ni = _unsafeNodeAccess(self, i);Uint256HeapNode storage nj = _unsafeNodeAccess(self, j);uint32 ii = ni.index;uint32 jj = nj.index;// update pointers to the data (swap the value)ni.index = jj;nj.index = ii;// update lookup pointers for consistency_unsafeNodeAccess(self, ii).lookup = j;_unsafeNodeAccess(self, jj).lookup = i;}function _siftDown(Uint256Heap storage self,uint32 size,uint32 pos,uint256 value) private {uint256 left = 2 * pos + 1; // this could overflow uint32uint256 right = 2 * pos + 2; // this could overflow uint32if (right < size) {// the check guarantees that `left` and `right` are both valid uint32uint32 lIndex = uint32(left);uint32 rIndex = uint32(right);uint256 lValue = _unsafeNodeAccess(self, _unsafeNodeAccess(self, lIndex).index).value;uint256 rValue = _unsafeNodeAccess(self, _unsafeNodeAccess(self, rIndex).index).value;if (lValue < value || rValue < value) {if (lValue < rValue) {_swap(self, pos, lIndex);_siftDown(self, size, lIndex, value);} else {_swap(self, pos, rIndex);_siftDown(self, size, rIndex, value);}}} else if (left < size) {// the check guarantees that `left` is a valid uint32uint32 lIndex = uint32(left);uint256 lValue = _unsafeNodeAccess(self, _unsafeNodeAccess(self, lIndex).index).value;if (lValue < value) {_swap(self, pos, lIndex);_siftDown(self, size, lIndex, value);}}}function _siftUp(Uint256Heap storage self,uint32 pos,uint256 value) private {unchecked {while (pos > 0) {uint32 parent = (pos - 1) / 2;uint256 parentValue = _unsafeNodeAccess(self, _unsafeNodeAccess(self, parent).index).value;if (parentValue < value) break;_swap(self, pos, parent);pos = parent;}}}function _unsafeNodeAccess(Uint256Heap storage self,uint32 pos) private pure returns (Uint256HeapNode storage result) {assembly ("memory-safe") {mstore(0x00, self.slot)result.slot := add(keccak256(0x00, 0x20), mul(pos, 2))}}
}

custom array implementation(verge下最节省gas)

library HeapArray2 {using SafeCast for *;struct Uint256Heap {bytes32 _placeholder_do_not_use;}struct Uint256HeapNode {uint256 value;uint32 index; // position -> valueuint32 lookup; // value -> position}struct Uint256HeapLength {uint256 value;}function peek(Uint256Heap storage self) internal view returns (uint256) {uint32 size = length(self);if (size == 0) Panic.panic(Panic.ARRAY_OUT_OF_BOUNDS);return _unsafeNodeAccess(self, _unsafeNodeAccess(self, 0).index).value;}function pop(Uint256Heap storage self) internal returns (uint256) {unchecked {uint32 size = length(self);if (size == 0) Panic.panic(Panic.EMPTY_ARRAY_POP);uint32 last = size - 1;// get root location (in the data array) and valueUint256HeapNode storage rootNode = _unsafeNodeAccess(self, 0);uint32 rootIdx = rootNode.index;Uint256HeapNode storage rootData = _unsafeNodeAccess(self, rootIdx);Uint256HeapNode storage lastNode = _unsafeNodeAccess(self, last);uint256 rootDataValue = rootData.value;// if root is not the last element of the data array (that will get pop-ed), reorder the data array.if (rootIdx != last) {// get details about the value stored in the last element of the array (that will get pop-ed)uint32 lastDataIdx = lastNode.lookup;uint256 lastDataValue = lastNode.value;// copy these values to the location of the root (that is safe, and that we no longer use)rootData.value = lastDataValue;rootData.lookup = lastDataIdx;// update the tree node that used to point to that last element (value now located where the root was)_unsafeNodeAccess(self, lastDataIdx).index = rootIdx;}// get last leaf location (in the data array) and valueuint32 lastIdx = lastNode.index;uint256 lastValue = _unsafeNodeAccess(self, lastIdx).value;// move the last leaf to the root, pop last leaf ...rootNode.index = lastIdx;_unsafeNodeAccess(self, lastIdx).lookup = 0;// self.data.pop();--_unsafeLengthAccess(self).value;// ... and heapify_siftDown(self, last, 0, lastValue);// return root valuereturn rootDataValue;}}function insert(Uint256Heap storage self, uint256 value) internal {uint32 size = length(self);if (size == type(uint32).max) Panic.panic(Panic.RESOURCE_ERROR);// self.data.push(Uint256HeapNode({index: size, lookup: size, value: value}));Uint256HeapNode storage node = _unsafeNodeAccess(self, size);node.index = size;node.lookup = size;node.value = value;++_unsafeLengthAccess(self).value;_siftUp(self, size, value);}function length(Uint256Heap storage self) internal view returns (uint32) {return _unsafeLengthAccess(self).value.toUint32();}function clear(Uint256Heap storage self) internal {_unsafeLengthAccess(self).value = 0;// Uint256HeapNode[] storage data = self.data;// /// @solidity memory-safe-assembly// assembly {//     sstore(data.slot, 0)// }}function _swap(Uint256Heap storage self, uint32 i, uint32 j) private {Uint256HeapNode storage ni = _unsafeNodeAccess(self, i);Uint256HeapNode storage nj = _unsafeNodeAccess(self, j);uint32 ii = ni.index;uint32 jj = nj.index;// update pointers to the data (swap the value)ni.index = jj;nj.index = ii;// update lookup pointers for consistency_unsafeNodeAccess(self, ii).lookup = j;_unsafeNodeAccess(self, jj).lookup = i;}function _siftDown(Uint256Heap storage self,uint32 size,uint32 pos,uint256 value) private {uint256 left = 2 * pos + 1; // this could overflow uint32uint256 right = 2 * pos + 2; // this could overflow uint32if (right < size) {// the check guarantees that `left` and `right` are both valid uint32uint32 lIndex = uint32(left);uint32 rIndex = uint32(right);uint256 lValue = _unsafeNodeAccess(self, _unsafeNodeAccess(self, lIndex).index).value;uint256 rValue = _unsafeNodeAccess(self, _unsafeNodeAccess(self, rIndex).index).value;if (lValue < value || rValue < value) {if (lValue < rValue) {_swap(self, pos, lIndex);_siftDown(self, size, lIndex, value);} else {_swap(self, pos, rIndex);_siftDown(self, size, rIndex, value);}}} else if (left < size) {// the check guarantees that `left` is a valid uint32uint32 lIndex = uint32(left);uint256 lValue = _unsafeNodeAccess(self, _unsafeNodeAccess(self, lIndex).index).value;if (lValue < value) {_swap(self, pos, lIndex);_siftDown(self, size, lIndex, value);}}}function _siftUp(Uint256Heap storage self,uint32 pos,uint256 value) private {unchecked {while (pos > 0) {uint32 parent = (pos - 1) / 2;uint256 parentValue = _unsafeNodeAccess(self, _unsafeNodeAccess(self, parent).index).value;if (parentValue < value) break;_swap(self, pos, parent);pos = parent;}}}function _unsafeNodeAccess(Uint256Heap storage self,uint32 pos) private pure returns (Uint256HeapNode storage result) {assembly ("memory-safe") {mstore(0x00, self.slot)result.slot := add(keccak256(0x00, 0x20), add(mul(pos, 2), 1))}}function _unsafeLengthAccess(Uint256Heap storage self) private pure returns (Uint256HeapLength storage result) {assembly ("memory-safe") {mstore(0x00, self.slot)result.slot := keccak256(0x00, 0x20)}}
}

使用连续的储存结构

因为warmup的逻辑不再按照slot来计算, 而是按照bucket来计算, 这个时候你需要的是尽量少的加载bucket.

比如你有approval和balance变量, 他们的mapping key都是account address

mapping(address account=>uint256) approval;
mapping(address account=>uint256) balance;

改成

struct TokenDetails{uint256 approval;uint256 balance;
}
mapping(address account=>TokenDetails) details;

使用diamond储存

为了更好的让你的储存结构保持连续来减少gas消耗, 你应该使用diamond storage储存结构. 并且这种在跨合约调用或者使用diamond proxy模式的时候, 都比较方便

pragma solidity ^0.8.20;contract Example {/// @custom:storage-location erc7201:example.mainstruct MainStorage {uint256 x;uint256 y;}// keccak256(abi.encode(uint256(keccak256("example.main")) - 1)) & ~bytes32(uint256(0xff));bytes32 private constant MAIN_STORAGE_LOCATION =0x183a6125c38840424c4a85fa12bab2ab606c4b6d0e7cc73c0c06ba5300eab500;function _getMainStorage() private pure returns (MainStorage storage $) {assembly {$.slot := MAIN_STORAGE_LOCATION}}function _getXTimesY() internal view returns (uint256) {MainStorage storage $ = _getMainStorage();return $.x * $.y;}
}

将数组的长度移动到与元素相同的“空间”中。

// SPDX-License-Identifier: MIT  
pragma solidity ^0.8.0;  contract StorageArray {  // 声明一个存储数组,第一个元素(index 0)将用于存储长度  uint256[] private arr;  // 构造函数:初始化数组,设置第一个元素为0(初始长度)  constructor() {  arr.push(0); // 在索引0处存储长度  }  // 获取数组的实际长度(不包括存储长度的元素)  function getLength() public view returns (uint256) {  return arr[0];  }  // 获取指定索引的元素(注意:实际数据从索引1开始)  function getElement(uint256 index) public view returns (uint256) {  require(index < arr[0], "Index out of bounds");  return arr[index + 1]; // +1 因为实际数据从索引1开始  }  // 向数组推入新元素  function push(uint256 value) public {  arr.push(value);    // 添加新元素  arr[0] = arr[0] + 1; // 更新长度  }  // 弹出最后一个元素  function pop() public returns (uint256) {  require(arr[0] > 0, "Array is empty");  uint256 lastElement = arr[arr.length - 1];  arr.pop();         // 移除最后一个元素  arr[0] = arr[0] - 1; // 更新长度  return lastElement;  }  // 获取完整数组(用于调试)  function getFullArray() public view returns (uint256[] memory) {  return arr;  }  
}

替换 "keccak256(.)" 成 "keccak256(...) & ~0xFF"

修改编译器设置

为什么这么改

  • 所有存储都在同一个 (verkle) trie 中
  • 在这个共享 trie 中的位置是账户和偏移量的组合(“slot number”)
  • Slots 被聚集在 buckets 中(也称为扩展)
    对于给定的账户,连续的 slots 在同一个 bucket 中(最多 256 个 slots)
  • Buckets 是“预热”的,而不是 slots
    因此,在给定的 bucket 中重用 slots 是高效的

更多相关信息,请, https://t.me/+P3Z7P_xQxbNlZWZl


http://www.ppmy.cn/ops/144907.html

相关文章

电路设计-恒流电路

一、 电路作用 恒流电路是一种能够在一定条件下&#xff0c;使输出电流保持恒定不变的电路。不管负载电阻如何变化或者输入电压如何波动&#xff0c;其输出电流都能维持在一个设定的值。常用于LED驱动和电池充电 二、典型电路 1.三极管恒流电路 图1 …

华为OD E卷(100分)36-补种未成活胡树

前言 工作了十几年&#xff0c;从普通的研发工程师一路成长为研发经理、研发总监。临近40岁&#xff0c;本想辞职后换一个相对稳定的工作环境一直干到老, 没想到离职后三个多月了还没找到工作&#xff0c;愁肠百结。为了让自己有点事情做&#xff0c;也算提高一下自己的编程能力…

Ubuntu 24.04.1 解决部分中文字符(门、径)显示错误的问题

安装的 Ubuntu 24.04 中总会看到一些中文字符显示错误&#xff0c;如下&#xff1a; 有轻微强迫症的我表示很难受。 为了解决这个问题&#xff0c;需要修改配置文件 /etc/fonts/conf.d/64-language-selector-cjk-prefer.conf 中不同语言字符显示的优先级。 该文件默认内容如下…

实践KDTS-WEB从mysql迁移到kingbasev9

数据库国产化替代数据迁移是一个复杂且关键的过程。这涉及到将原有数据库中的数据准确、完整地迁移到新的国产数据库中&#xff0c;同时确保数据的完整性和一致性。人大金仓提供了强大的数据库迁移工具&#xff08;KDTS&#xff09;对同构、异构数据库数据迁移&#xff1b; 数…

EXCEL使用宏实现筛选重复项并对该行进行填充内容的操作

EXCEL使用宏实现筛选重复项并对该行进行填充内容的操作 需求 1.一个excel中有多张不同的sheet 2.筛选出sheet1中A、B列与sheet2中A、B列中非重复行 3.在非重复行对应的D列填充内容 原始表&#xff1a;需要排出专家1wbb在自己没课的时候可以听其他人课的时间&#xff0c;在专…

无人直播源码

DY无人直播系统架构设计介绍 在DY等短视频平台的直播中&#xff0c;无人直播系统能够提供自动化、智能化的互动体验&#xff0c;既减轻了主播的工作量&#xff0c;又提升了观众的参与感。以下是一个典型的无人直播系统架构设计&#xff0c;包含全局配置、点对点互动、产品话术、…

网络安全 | 入门:理解基本概念和术语

网络安全 | 入门&#xff1a;理解基本概念和术语 前言一、什么是网络安全&#xff1f;1.1 网络安全的重要性1.2 网络安全的三大核心目标&#xff08;CIA三原则&#xff09; 二、网络安全常见术语2.1 防火墙&#xff08;Firewall&#xff09;2.2 入侵检测系统&#xff08;IDS&am…

高校教师成果管理小程序的设计与实现springboot+论文源码调试讲解

第2章 开发环境与技术 基于web的高校教师成果管理的编码实现需要搭建一定的环境和使用相应的技术&#xff0c;接下来的内容就是对基于web的高校教师成果管理用到的技术和工具进行介绍。 2.1 MYSQL数据库 本课题所开发的应用程序在数据操作方面是不可预知的&#xff0c;是经常…