OneSwap Series 4 - ABI is not the invisible man

Solidity’s ABI

Gas Consumption When External Accounts Call Contracts

function multiTransfer(uint256[] calldata mixedAddrVal) public override returns (bool) {
for (uint i = 0; i < mixedAddrVal.length; i++) {
address to = address(mixedAddrVal[i]>>96);
uint256 value = mixedAddrVal[i]&(2**96-1);
_transfer(msg.sender, to, value);
}
return true;
}
function removeOrders(uint[] calldata rmList) external override lock {
uint[5] memory proxyData;
uint expectedCallDataSize = 4+32*(ProxyData.COUNT+2+rmList.length);
ProxyData.fill(proxyData, expectedCallDataSize);
for(uint i = 0; i < rmList.length; i++) {
uint rmInfo = rmList[i];
bool isBuy = uint8(rmInfo) != 0;
uint32 id = uint32(rmInfo>>8);
uint72 prevKey = uint72(rmInfo>>40);
_removeOrder(isBuy, id, prevKey, proxyData);
}
}

Keyword calldata

function try2(uint[] storage aList) external returns (uint) {
return aList[0]+aList[1];
}
// Error: Data location must be "memory" or "calldata" for parameter in external function, but "storage" was given.
function try1(uint a1, uint a2, uint a3, uint a4, uint a5, uint a6, uint a7, uint a8, uint a9) public pure returns (uint) {
return try2(a1, a2, a3, a4, a5, a6, a7, a8, a9);
}

function try2(uint a1, uint a2, uint a3, uint a4, uint a5, uint a6, uint a7, uint a8, uint a9) public pure returns (uint) {
return a1+a2+a3+a4+a5+a6+a7+a8+a9;
}
Compiler error: Stack too deep, try removing local variables.
function try3(uint[9] calldata a) public pure returns (uint) {
return a[0]+a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7]+a[8];
}

Gas Consumption of Event Parameters

Understand the ABI Before You Make a Low-level Call

bytes4 private constant _SELECTOR = bytes4(keccak256(bytes("transfer(address,uint256)")));
bytes4 private constant _SELECTOR2 = bytes4(keccak256(bytes("transferFrom(address,address,uint256)")));
bytes4 private constant _APPROVE_SELECTOR = bytes4(keccak256(bytes("approve(address,uint256)")));
function _safeTransferToMe(address token, address from, uint value) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(_SELECTOR2, from, address(this), value));
require(success && (data.length == 0 || abi.decode(data, (bool))), "LockSend: TRANSFER_TO_ME_FAILED");
}
function _safeTransfer(address token, address to, uint value) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(_SELECTOR, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), "LockSend: TRANSFER_FAILED");
}
function _safeApprove(address token, address spender, uint value) internal {
token.call(abi.encodeWithSelector(_APPROVE_SELECTOR, spender, value)); // result does not matter
}

Use Assembly to Construct Variable-length Arrays

function copyArr(uint[] calldata a) public pure returns (uint[] memory b) {
for(uint i=0; i<a.length; i++) b.push(a[i]);
return b;
}
//Error: Member "push" is not available in uint256[] memory outside of storage.
function copyArr(uint[] calldata a) public pure returns (uint[] memory b) {
b = new uint[](a.length);
for(uint i=0; i<a.length; i++) b[i] = a[i];
return b;
}
function sliceArrCalldata(uint[] calldata a) public pure returns (uint[] calldata b) {
return a[:1];
}
function sliceArrMemory(uint[] memory a) public pure returns (uint[] memory b) {
return a[:1];
}
// Get the orderbook's content, starting from id, to get no more than maxCount orders
function getOrderList(bool isBuy, uint32 id, uint32 maxCount) external override view returns (uint[] memory) {
if(id == 0) {
if(isBuy) {
id = uint32(_bookedStockAndMoneyAndFirstBuyID>>224);
} else {
id = uint32(_reserveStockAndMoneyAndFirstSellID>>224);
}
}
uint[1<<22] storage orderbook;
if(isBuy) {
orderbook = _buyOrders;
} else {
orderbook = _sellOrders;
}
//record block height at the first entry
uint order = (block.number<<24) | id;
uint addrOrig; // start of returned data
uint addrLen; // the slice's length is written at this address
uint addrStart; // the address of the first entry of returned slice
uint addrEnd; // ending address to write the next order
uint count = 0; // the slice's length
assembly {
addrOrig := mload(0x40) // There is a “free memory pointer” at address 0x40 in memory
mstore(addrOrig, 32) //the meaningful data start after offset 32
}
addrLen = addrOrig + 32;
addrStart = addrLen + 32;
addrEnd = addrStart;
while(count < maxCount) {
assembly {
mstore(addrEnd, order) //write the order
}
addrEnd += 32;
count++;
if(id == 0) {break;}
order = orderbook[id];
require(order!=0, "OneSwap: INCONSISTENT_BOOK");
id = uint32(order&_MAX_ID);
}
assembly {
mstore(addrLen, count) // record the returned slice's length
let byteCount := sub(addrEnd, addrOrig)
return(addrOrig, byteCount)
}
}

Conclusion

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store