2024, Feb 14
- DeFi
- What is a bonding curve ?
- AMM price assets
- Compound Finance
- What is TWAP?
- Slippage parameter
- Gas
- Loop
- Gas griefing
- Function payable
- Types of storage
- Multiplying and dividing
- EIP-1559-BASEFEE
- Cold and warm read
- Packed variable
- Gas refund
- Proxy
- Storage collision
- Function selector clash
- Upgradeable contracts constructor
- UUPS and the Transparent Upgradeable Proxy
- Delegatecall
- Token
- Security
- Transfer and send
- Front-running
- abi.encodePacked - Vulnerability
- Signature replay attack
- Commit-reveal scheme
- Rock-paper-scissors
- Solidity Misc
- ERC-165
- Measure time in Solidity
- Floating point arithmetic
- Abi.encode and abi.encodePacked?
- Uint
- Interface - Function modifiers
- Function - memory and calldata
- Free memory pointer
- Solidity style Guide
- Functions ordered
- Modifiers ordered
- Further reading
This article presents the list of medium questions with their answers related to the article Solidity Interview Questions by RareSkills.
For the levels Hard and Advanced, you can see my second and third articles.
According to the article, all questions can be answered in three sentences or less.
The answers here are more complete tha
[TOC]
n necessary in order to explain in details the topics.
DeFi
What is a bonding curve ?
— A bonding curve is a mathematical function connecting the supply of a digital asset with its value.
In summary, the price of a token change regarding the token supply
What Is a Bonding Curve and How Does It Affect Token Price?
AMM price assets
How does an AMM price assets?
An AMM used different algorithm and mathematical formula to automatically price assets.
The historical formula, used by Uniswap V1 and V2, is the constant product formula:
x * y = k
where
x: number of token 1 in the pool
y: number of token 2 in the pool
k: constant that stays constant during trades but recalculated when liquidity is provided / withdrawn
See Uniswap V2 - How Uniswap work, Back to the Basics: Uniswap, Balancer, Curve
Compound Finance
How does Compound Finance calculate utilization?
The formula for producing the utilization is:
Utilization = TotalBorrows / TotalSupply
Ref: docs.compound.finance/interest-rates/#get-utilization
What is TWAP?
— A TWAP (Time Volume Weighted Average Price) is an oracle which returns the average price of an asset over a specific period.
To compute the average price, it will perform a sum up of each price for a certain amount of block and divide it by the number of blocks used. The formula can be summarized thus:
The goal of this oracle is to make more costly a flashhloan attack / price manipulation since a flashloan attack modifies the price of an asset inside the same block.
A attacker has to manipulate the price during a long period instead that just one block.
Advantage
- Generally, more robust again short-term manipulation (typically flashloan)
Disadvantage
- You can not use a TWAP to obtain prices of an asset over a small period of time since it will reduce the cost of a price manipulation attack.
- Moreover, TWAP oracles can not use the price of an assets on an exchange to compute the price.
Reference: Halborn - WHAT ARE TWAP ORACLES?, Halborn - Why TWAP Oracles Are Key to DeFi Security, TWAP Oracles vs. Chainlink Price Feeds: A Comparative Analysis, How the TWAP Oracle in Uniswap v2 Works
Slippage parameter
What is a slippage parameter useful for?
— Slippage refers to the difference between the expected price of a trade and the actual executed price due to market volatility or liquidity issues.
It can p.ex happens on a DEX with a large order or if the transaction is targeted by a sandwich attack.
A slippage parameter in a function allows to indicate the minimum amount of tokens that you want to be returned from a swap or another operation.
Reference: What are Slippage Attacks in Decentralized Exchanges (DEXs)?
Gas
Loop
How do you write a gas-efficient for loop in Solidity?
1) Cache storage variable in a local variable2) Use ++i
instead of i++
3) Eventually*: use unchecked
to update the counter i
Solution
// Store the storage variable inside a local variableuint256 limit = storageVariableUint256;for (uint256 i; i <= limit; ) { // deactivate check overflow unchecked { // ++i instead of i++ ++i; }}
You find more optimizations in my other articles on gas optimization: Gas Optimization
Exception
*The solidity version 0.8.22 introduces an overflow check optimization that automatically generates an unchecked arithmetic increment of the counter of for loops. As a result, it is not useful to use unchecked
if the loop meets the criteria (see the release doc).
This native optimization works only for the comparaison <
and not with <=
Example
// Store the storage variable inside a local variableuint256 limit = storageVariableUint256;for (uint256 i; i < limit; ++i) {// Body}
See Solidity 0.8.22 Release Announcement
Reference:
RareSkills Book of Solidity Gas Optimization
Gas griefing
What is gas griefing?
Solution
A gas griefing is a scenario when a transaction fails maliciously due to a lack of gas.
-
It can happen when a user sends a transaction to a relayer or another smart contract with no enough gas to perform the different subcalls contains in the transaction.
-
It can happen if the smart contract entrypoint (e.g. a relayer) does not check if there is enough gas to execute the transaction and do not check the return value of the external call.
Reference:
Function payable
What is the effect on gas of making a function payable?
— It will reduce the contract bytecode size and the amount of gas required to execute the function since the compiler does not check if ethers have been send with the call (call value).
It is possible to use this trick to save gas with the constructor and admin function, but not for all functions since these specifics functions will not be called by end-user, reducing the risk to send ethers by error.
It is general not recommended to make all functions payable since it can generate unexpected behavior and it reduces the readability of the code.
Reference:
Types of storage
Describe the three types of storage gas costs.
Calldata => temporary data storage, read-only, the cheapest
memory => temporary data storage, read and write, gas cost between calldata and storage
storage => permanent data storage on the blockchain, read and write, most costly type of storage
- calldata
The cheapest storage location since it is a read-only temporary data storage used to contain the function arguments passed from an external call. Therefore, It is not possible to modify the data inside a function or to use it with an internal function.
- memory
memory
is a temporary data storage location that can be modified by a function, while calldata
is a read-only temporary data storage location used to hold function arguments passed in from an external caller.
As for calldata, it is a temporary data storage, but it is writable and therefore cost most than the calldata storage. The opcode use is mload.
- Storage
The most costly type of storage since the data are stored permanently on the blockchain. The opcode use is sload
.
Reference: When to use Storage vs. Memory vs. Calldata in Solidity
Multiplying and dividing
What is a gas efficient alternative to multiplying and dividing by a multiple of two?
We can use binary shift to do this because the shift right (shr
) and shift left (shl
) opcodes are cheaper than the opcodes used for the multiplication and division.Respectively, the binary shift opcodes cost 3 against 5 for the mul
and div
opcode.\(10 * 2 = 10 << 1\)
[10 \div 2 = 10 >> 1]
Reference: The RareSkills Book of Solidity Gas Optimization: 80+ Tips
EIP-1559-BASEFEE
How does Ethereum determine the BASEFEE in EIP-1559?
— The base fee depends of the network activity / congestion according to a fomula.
For example, if x
, the % of the block filling
x == 50% full => unchanged
x == 100% full => increase by the maxium (12.5%)
50 < x < 100% => increase by less than 12.5%
x == 0% full (empty block) => decrease by less than 12.5%
Cold and warm read
What is the difference between a cold read and a warm read?
This two terms appear inside the yellow paper regarding the gas cost.
The first time a storage variable is read is called a cold access and the second time, it is a warm access and it is cheaper.
In any case, the most cheapest solution if you have to read several time a storage variable is to cache the variable in memory, which will be cheaper than a warm read.
References:
- Cold access VS Warm access - Gas Cost Question
- coinsbench.com - Comprehensive Guide: Tips and Tricks for Gas Optimization in Solidity
Packed variable
How large a uint can be packed with an address in one slot?
A slot contains 32 bytes and an address already takes 20 bytes. Thus there are 12 bytes (= 96 bits) which are still free in this slot.
You can pack an uint96
with an address in the same slot.
Gas refund
Which operations give a partial refund of gas
— With EIP-3529, SSTORE
is the only operation that potentially provides a gas refund.
The refund is given (added into refund counter) when the storage value is set to zero from non-zero.
Reference: Appendix - Dynamic Gas Costs, How to clear storage and get incentivized by Ethereum Blockchain ?
Remark:
Initially, the operation self-destruct offered a refund of gas but this is no longer the case since the introduction of EIP-3529(London fork) which has removed this refund.
Proxy
If you want to build a contract for a proxy architecture, I made a summary of the most important points to think about: Programming proxy contracts with OpenZeppelin - Summary
Storage collision
What is a storage collision in a proxy contract?
- Storage collision between the proxy and the implementation contract
— A storage collision happens when a proxy and its implementation use the same slot to store a value. As a result, when the implementation contract writes to update its variable, it will overwrite in reality the variable used by the proxy.
A solution, used by OpenZeppelin, is to “randomize” slot positions in the proxy’s storage
See openzeppelin.com/upgrades-plugins/1.x/proxies#unstructured-storage-proxies
- Storage collision between two implementations
In this case, a new implementation overwrites a variable from the previous implementation when an upgrade is performed
Function selector clash
What is a function selector clash in a proxy and how does it happen?
A function selector clash happens when two functions have the same 4-byte identifiers.
This identifier depends on the name and arity of the function,
— In the case of a proxy, and plus particularly of a transparent proxy, a same function with the same arguments can be defined in the proxy and its implementation resulting in a function clash.
In this scenario, we have to know if the sender tries to call the function defined in the proxy or the function defines in its implementation.
The solution implemented by the OpenZeppelin team is to use a proxy admin to control the proxy.
- If the admin calls the proxy, the call is not delegate and the function called is the function defined in the proxy.
- If the call came not from the admin, the call will be delegate to the implementation contract.
See docs.openzeppelin.com/upgrades-plugins/1.x/proxies#transparent-proxies-and-function-clashes
Upgradeable contracts constructor
Why shouldn’t upgradeable contracts use the constructor?
If variables are initialied inside the constructor, the proxy has no way to see these values since :
- The constructor is not stored in the runtime bytecode, but only in the creation bytecode.
- The implementation contract is not deployed in the context of the proxy.
The solution is to use a public initialize
function to initialize the proxy with the different values for each variable.
One exception to this is for immutable variable. Since this value is stored in the contract bytecode instead of the contract storage, you can use and initialize an immutable inside the constructor of the implementation contract
See docs.openzeppelin.com/upgrades-plugins/1.x/proxies#the-constructor-caveat
UUPS and the Transparent Upgradeable Proxy
What is the difference between UUPS and the Transparent Upgradeable Proxy pattern?
Contrary to the Diamond
or the Beacon
proxy, these two interfaces use the same system to delegate call and know the implementation.
But they have a big difference in the method to upgrade the implementation.
With the Transparent
Proxy, the function to perform the upgrade is contained in the proxy, not the implementation.
While in the UUPS, the function is contained in the implementation contract.
This architecture makes the proxy cheaper, but can be more riskier because :
- If you upgrade your proxy with a new implementation where the upgrade function does not exist, the proxy can not longer be upgraded.
- Several vulnerabilities have targeted UUPS proxy where a
self destruct
function was available but not protected in the implementation contract…With the next Ethereum upgrade Dencun (2024) self destruct can no longer destroy a contract but the risk remains if others critical functions are present, see Wormhole Uninitialized Proxy Bugfix Review.
Reference: Proxy Patterns For Upgradeability Of Solidity Contracts: Transparent vs UUPS Proxies
Delegatecall
Self Destructed
a) If a contract delegatecalls an empty address or an implementation that was previously self-destructed, what happens? b) What if it is a regular call instead of a delegatecall?
Question A
— If a contract delegatecalls to a self-destructed implementation, the delegatecall will return a success.
The reason is because the address still exists, even if the storage and the funds/balance have been cleared.
If it is the zero address, I am not totally sure, but I think it returns also a success, see this question on stackoverflow.
If the call is performed to an External Owned Account (EOA), since this is not a contract, there is no code and the call will returne True
For information, DELEGATECALL
pushes 1 on the stack in case of success, and pushes 0 on the stack in case of an error.
Reference:
- Understanding Contract Delegation in Solidity: Handling Selfdestruct Scenarios
- OpenZeppelin - Proxy.sol
- Why is delegatecall returning 0 and erroring?
- Understanding delegatecall And How to Use It Safely
Question B
For a call with call
, if it is the address 0, the call will return a success. You can test this behavior with Foundry.
address _address = address(0);(bool success, bytes memory data) = _address.call{gas: 5000}(abi.encodeWithSignature("foo(string,uint256)", "call foo", 123));assertEq(success, true);
For a self destructed contract, the call will also return true
but will not do anything since the code has been removed.
Reference:
Balance
If a proxy makes a delegatecall to A, and A does address(this).balance, whose balance is returned, the proxy’s or A?
The returned balance is the proxy balance since the call is executed in the context of the proxy.
Revert
If a delegatecall is made to a function that reverts, what does the delegatecall do?
Delegatecall will return false, it does not revert.
You have to manage this case in your contract. For example, OpenZeppelin for their proxy contract reverts in case of an error
switch result// delegatecall returns 0 on error.case 0 {revert(0, returndatasize())}
References: OpenZeppelin - Proxy, Preventing unwanted delegate call
Immutable variable
If a delegatecall is made to a function that reads from an immutable variable, what will the value be?
An immutable variable is stored in the bytecode of the implementation contract and it is this value which will be read.
With a proxy, you can change/upgrade the value of an immutable variable by upgrading to a new implementation.
Token
Rebasing token
What is a rebasing token?
A rebase, or elastic, token, is a token where the supply and the user’s balance is adjusted periodically
Use case :
- Algorithmic stablecoin to maintain a peg to another asset. The number of token in circulation are adjusted through the rebase mechanism depending of the token price related to the asset peg.
Example: Ampleforth (AMPL)
- Liquid staking Token (LST) : the user’s balance is rebased to represent the revenue issue from the staking.
Example: Lido (stETH)
On UniswapV2, this type of token is a problem because the uniswap router contract does not know when a rebasing happen and it makes the pair balance unbalanced, see uniswap docs. As a result, rebasing tokens will succeed in pool creation and swapping, but liquidity providers will bear the loss of a negative rebase when their position becomes active, with no way to recover the loss, see uniswap docs
Global reference: What Is a Rebase/Elastic Token?
fee-on-transfer
What is a fee-on-transfer token?
—A “Fee on Transfer” token is a token that takes a percentage of internal commission upon transfer or trade. In other words, every time the token is transfered, a portion of the transfer amount is taken, e.g. to burn or sent to another address as a fee.
Reference: 1inch - What is a Fee on Transfer token?
On UniswapV2, to swap this type of token, you have to call a specific function which takes in consideration this fee to compute the invariant, see uniswap docs.
Token standard
ERC-777
What danger do ERC-777 tokens pose?
–ERC-777 allows a sender of a transaction specified a contract to call, which can be used to perform reentrancy attack.
From the EIP:
- The holder can “authorize” and “revoke” operators which can send tokens on their behalf
- When sending tokens, the token contract MUST call the
tokensToSend
hook of the holder if the holder registers anERC777TokensSender
implementation via ERC-1820. - The token contract MUST call the
tokensReceived
hook of the recipient if the recipient registers anERC777TokensRecipient
implementation via ERC-1820.
This contract is determined through a registry.
What are the possible attacks/danger ?
- An attacker can perform a reentrancy attack by setting a malicious contract as a hook when the attacker sends or receives tokens. For example to re-enter a
withdraw
function, which will send tokens to the attacker address. The token ERC-777 has to protect against it by adding a nonReentrantmodifier
to callbacks:_callTokensToSend
and_callTokensReceived
. See ERC-777 callback issue - If a target contract allows making arbitrary calls to any address with any data, an attacker can leverage this to set a malicious contract to call each time the target contract receives or send tokens (see One more problem with ERC-777). The attacker can choose for example to revert each time resulting in an attack dos.
Reference: A Dive With ERC-777 And Risk Mitigations, ERC777 implementation and security clarifications, ERC-777 callback issue
ERC-721
How does safeMint differ from mint in the OpenZeppelin ERC721 implementation?
The safeMint function will check if the destination contract can support ERC-721 token. If not, the call will revert
ERC-721A
What does ERC-721A do to reduce mint costs? What is the tradeoff?
Enable minting multiple NFTs for essentially the same cost of minting a single NFT.
- Optimization 1 - Removing duplicate storage from OpenZeppelin’s (OZ) ERC721Enumerable
- Optimization 2 - updating the owner’s balance once per batch mint request, instead of per minted NFT
- Optimization 3 - updating the owner data once per batch mint request, instead of per minted NFT
Reference: www.erc721a.org, www.azuki.com/erc721a
We can see these difference by comparing the function mint from OpenZeppelin and the function mint from ERC-721A. We can see for ERC-721A there is no tokenId
and a new parameter quantity
has been added in the parameters of the function.
function _mint(address to, uint256 tokenId) internal {
function _mint(address to, uint256 quantity) internal virtual
Security
Transfer and send
What is the difference between transfer and send? Why should they not be used?
- transfer
The receiving smart contract should have a fallback function defined or else the transfer call will throw an error. There is a gas limit of 2300 gas, which is enough to complete the transfer operation. Initially, this gas limit was put in place to prevent reentrancy attacks since there is no enough gas to perform this attack.
- send
It works in a similar way as to transfer call and has also a gas limit of 2300 gas . But contrary to transfer
, It returns the status as a boolean.
Recommendation
According to immunebytes.com, it is not recommended to use the function transfer
since the modifier .gas()
has been added and this function takes a hard dependency on gas costs by forwarding a fixed amount of gas i.e., 2300.
Consensys recommends also to avoid transfer
, and also send
, for the same reasons and to use call
instead.
Call
will forward all gas (if not set) and returns a boolean. It is necessary to protect the call against re-entrancy attack
Example from Solidity by Example
function sendViaCall(address payable _to) public payable { // Call returns a boolean value indicating success or failure. // This is the current recommended method to use. (bool sent, bytes memory data) = _to.call{value: msg.value}(""); require(sent, "Failed to send Ether"); }
References:
Front-running
Definition
What is frontrunning?
— A front-run happens when an attacker sends and validates by the nodes a transaction before a target transaction, for example by paying a higher gas fee.
When a transaction is waiting in the mempool to be valided, an “attacker” use the information contained in this transaction to makes its own transaction beneficial to him and pays a higher gas fee in order to validate its transaction before the first one.
Reference:
- Front-Running Attacks in Blockchain: The Complete Guide
- Front Running and Sandwich Attack Explained - QuillAudits
Sandwich attack
What is a sandwich attack?
— A sandwich attack happens when an attacker, generally a bot, sends a transaction before and after a target transaction.
In general the goal is to take advantage of price movements to make a profit to the detriment of the issuer of the target transaction.
For example, if the target transaction is a buy order of a token C. The attacker will buy tokens before the transaction, making the price to move up, then selling the bought tokens after the target transaction. Thus we have this sequence:
1) Attacker buys token X, prices moves up2) Target buy tokens to an higher price than expected3) Attacker makes a profit by selling the tokens
There are two mains methods to protect against a sandwich attack
- The slippage parameter to define a range of price
- Use flashbot to avoid the mempool.
References:
CharlesWangP - Thread, Lecture 13.7: Sandwich Attacks, Modern MEV sandwich attacks on Ethereum routers
abi.encodePacked - Vulnerability
Under what circ*mstances could abi.encodePacked create a vulnerability?
For different arguments passed, abi.encodePacked will return the same value.
The solidity documentation provides the following example:
abi.encodePacked("a", "bc") == abi.encodePacked("ab", "c")
If this return value is hashed to verify a signature or to check the arguments contents, an attacker can change the order of the arguments and still have valid data or signature associated.
In this example form, from this 1.article, an attacker present as a regular user can put its address in the privileged array.
function claimRewards(address[] calldata privileged, address[] calldata regular) external { bytes32 payoutKey = keccak256(abi.encodePacked(privileged, regular)); require(allowedPayouts[payoutKey], "Unauthorized claim"); allowedPayouts[payoutKey] = false; _payout(privileged, premiumPayout); _payout(regular, regularPayout); }
In this another example from this 2. article , if a valid signature already exists in a transaction on the blockchain, a regularUser
can call again the function with the same signature, but by putting its address in the array admins
.
function addUsers( address[] calldata admins, address[] calldata regularUsers, bytes calldata signature ) external { if (!isAdmin[msg.sender]) { // Allow calls to be relayed with an admin's signature. bytes32 hash = keccak256(abi.encodePacked(admins, regularUsers)); address signer = hash.toEthSignedMessageHash().recover(signature); require(isAdmin[signer], "Only admins can add users."); } for (uint256 i = 0; i < admins.length; i++) { isAdmin[admins[i]] = true; } for (uint256 i = 0; i < regularUsers.length; i++) { isRegularUser[regularUsers[i]] = true; } }
Reference:
- 1. New Smart Contract Weakness: Hash Collisions With Multiple Variable Length Arguments3
- 2. scsfg.io - ABI Hash Collisions
Signature replay attack
What is a signature replay attack?
A signature replay attack happens when a valid signature is used several times.
Generally, it happens because the smart contract does not verify if a signature has already been used.
See solidity-by-example.org - signature-replay
Commit-reveal scheme
What is a commit-reveal scheme and when would you use it?
In a commitment scheme, the actors involved post (= commit) a value on the blockchain. This value has the following propreties :
- It is not readable from others (hidden)
- It can be revealed later
- No one can change its value once commited.
This scheme is implemented through cryptographic algorithm and has two phases:
- A commit phase in which a value is chosen and specified;
- A reveal phase in which the value is revealed and checked.
This schema can be used to bid amounts or to play a game like Rock-paper-scissors (see next question) to keep the player’s choice secret from others players.
Example of a commit struct, where solutionHash
is compute off-chain by the sender as hash(msg.sender + solution + secret)
struct Commit { bytes32 solutionHash; uint commitTime; bool revealed;}
References:
Rock-paper-scissors
How would you design a game of rock-paper-scissors in a smart contract such that players cannot cheat?
— You can use the commit-reveal scheme (see previous question).
Each player commit its choice under the form hash(choice, salt)
Once the two players have commit their choice, the two players call the function reveal
with their salt.
The smart contract computes the hash with the salt, and can compare the choice of the two players to determine which players won.
References: obheda12 - Transaction Order Dependence (Front-running), BCAM - Commit Reveal
Solidity Misc
ERC-165
What is ERC-165 used for?
— It is used to specify and retrieve a standard ERC. It is useful to know if a contract implements an interface.
For example, it is required by the standard ERC-721 but not by ERC-20.
See eip-165
Measure time in Solidity
What keywords are provided in Solidity to measure time?
— Suffixes like seconds
, minutes
, hours
, days
and weeks
after literal numbers can be used to specify units of time where seconds are the base unit.
block.timestamp
(uint
): current block timestamp as seconds since unix epoch
The current block timestamp must be strictly larger than the timestamp of the last block.
According to the documentation, the only guarantee is that it will be somewhere between the timestamps of two consecutive blocks in the canonical chain. Nevertheless, I think this point concerned Ethereum before the merge (see next question).
References: docs.soliditylang.org - time Units, rareskills.io - Solidity Coding Standards
What changed with block.timestamp before and after proof of stake?
— The post-Merge consensus on valid blocks is pre-determined timestamps that are not modifiable.
Each slot has an expected timestamp, and a block without that exact timestamp is not valid. The block after the beacon chain genesis is expected to have a timestamp exactly 12 seconds after the genesis block. The block after it 12 seconds after, and so forth. See the spec
Reference: Miner-modifiability of block timestamp after the Merge
Floating point arithmetic
Why doesn’t Solidity support floating point arithmetic?
Ethereum blockchain is deterministic which ensures that smart contracts always produce the same output for the same input.
— With floating point number, you can have a loss of precision and a difference between node computation which is not compatible with the deterministic nature of Ethereum.
References: stackoverflow - Usage of Float Numbers in Smart contract, stackoverflow - Why are there no decimal numbers in Solidity?
Abi.encode and abi.encodePacked?
What is the difference between abi.encode and abi.encodePacked?
abi.encode
abi.encode(...) returns (bytes memory)
ABI-encodes the given arguments.
- The arguments will be passed as specified in the ABI specification
- Useful to perform a call to a contract
abi.encodePacked
abi.encodePacked(...) returns (bytes memory)
Perform packed encoding of the given arguments. Note that this encoding can be ambiguous!
The rules are the following
- types shorter than 32 bytes are concatenated directly, without padding or sign extension (contrary to
abi.encode
) - dynamic types are encoded in-place and without the length.
- array elements are padded, but still encoded in-place
Furthermore, structs as well as nested arrays are not supported.
In some cases, using abi.encodePacked
can be dangerous (see section security / abi.encodePacked - vulnerability)
Reference: solidity doc - ABI Encoding and Decoding Functions, Why are there two methods encoding arguments? “abi.encode” and “abi.encodePacked”
Uint
uint8, uint32, uint64, uint128, uint256 are all valid uint sizes. Are there others?
Yes, alluint8
to uint256
in steps of 8
(unsigned of 8 up to 256 bits)
For example, there is also uint16, uint24, uint40…
Reference: www.velvetshark.com - Max values for each uint in Solidity, from uint8 to uint256, docs.soliditylang.org - integers
Interface - Function modifiers
What function modifiers are valid for interfaces?
valid function modifiers for interfaces are
pure, view, external, override, payable
- It is not possible to use the modifiers internal or
private
. A function interface has to beexternal
. The visibility restriction can be perform on the implementation. - It is not possible to use the modifier
nonpayable
too - You can use the
pure
orview
keyword, in this case, the function implementation has to beview
orpure
. Same behavior withpayable
.
Reference: twitter.com/RareSkills_io/status/1638556209828737025 + tested with Visual Studio Code.
Function - memory and calldata
What is the difference between memory and calldata in a function argument?
See Section Gas-Type of storage
memory
is a temporary data storage location that can be modified by a function, while calldata
is a read-only temporary data storage location used to hold function arguments passed in from an external caller. A calldata
argument can not be used by an internal
function.
Free memory pointer
What is the free memory pointer and where is it stored?
It is stored in the slot 0x40
. This pointer contains the emplacement in memory which is free. Initially, it points to the address 0x80
See docs.soliditylang - Layout in Memory && 0xpranay - Solidity Internals
Solidity style Guide
Functions ordered
According to the solidity style guide, how should functions be ordered?$
Functions should be grouped according to their visibility and ordered:
- constructor
- receive function (if exists)
- fallback function (if exists)
- external
- public
- internal
- private
Within a grouping, place the view
and pure
functions last.
Example from the documentation
// SPDX-License-Identifier: GPL-3.0pragma solidity >=0.7.0 <0.9.0;contract A { constructor() { // ... } receive() external payable { // ... } fallback() external { // ... } // External functions // ... // External functions that are view // ... // External functions that are pure // ... // Public functions // ... // Internal functions // ... // Private functions // ...}
Reference: docs.soliditylang.org - order-of-functions
Modifiers ordered
According to the solidity style guide, how should function modifiers be ordered?
The modifier order for a function should be:
- Visibility
- Mutability
- Virtual
- Override
- Custom modifiers
Example:
function balance(uint from) public view override returns (uint) { return balanceOf[from];}function shutdown() public onlyOwner { selfdestruct(owner);}
Reference: docs.soliditylang.org - function-declaration
Further reading
You can find different answers to these questions in the following resources
You might also enjoy
Compound V2 Overview
Solana Programs - Basic Security with Anchor
Influential figures in Crypto and Blockchain
Zero Knowledge Proofs with Bulletproof
>