Smart Contract Development
  • Introduction
    • What is a Transaction
    • Accounts and Signing
    • What is a smart contract
  • Learning Solidity
    • Introduction
    • Module 1
      • Variable Types
      • Variable Scope: State & Local variables
      • Global variables
      • Functions
        • View and Pure
        • Shadowing in Fuctions
      • Mapping
      • Require
      • Events
    • Project #1: Simple Registry
    • Module 2
      • Constructor
      • Data Location: Value & Reference
      • Interface
      • Import
        • Importing in Foundry
      • Inheritance
      • ERC-20
      • Checks-effect-interaction pattern
    • Project #2: Basic Vault
    • Module 3
      • Payable
      • Receive
      • Fallback
      • Returns
    • Project #3: ERC20+ETH Wrapper
    • Module 4
      • Immutable and Constant
      • Fixed-point Math
      • Abstract contracts
      • ERC-4626
      • Modifier + Inheritance +Ownable
      • Type
    • Project #4: Fractional Wrapper
    • Module 5
      • If-else
      • Libraries
        • TransferHelper
      • Chainlink Oracle
    • Project #5: Collateralized Vault
  • Compendium
    • Solidity Basics
      • Variable Types
      • Value Types
        • address
        • enum
      • Reference Types
        • strings
        • mappings
        • struct
        • Arrays
        • Multi-Dimensional arrays
      • Global Objects
      • Functions
        • Function types
        • Constructor Function
        • Transaction vs Call
        • Require, Revert, Assert
      • Function signature + selectors
      • Payable
        • Payable + withdraw
        • msg.value & payable functions
      • Receive
      • Fallback function (sol v 0.8)
        • Fallback function (sol v 0.6)
      • call, staticcall, delegatecall
    • Return & Events
    • Control Variable Visibility
    • Local Variables (Storage v Memory)
    • Data Location and Assignment Behaviors
    • Modifiers & Inheritance & Import
      • import styles
    • Interface & Abstract Contracts
    • ABI & Debugging
    • Libraries
    • Conditional(ternary) operators
    • Smart Contract Life-cycle
      • Pausing Smart Contracts
      • Destroying Smart Contracts
    • Merkle Trie and MPT
    • Merkle Tree Airdrop
  • Try & catch
  • Ethereum Signatures
  • EVM, Storage, Opcodes
    • EVM
    • Wei, Ether, Gas
    • Storage
    • ByteCode and Opcodes
    • Transaction costs & Execution costs
  • Reading txn input data
  • Data Representation
  • Yul
    • Yul
      • Intro
      • Basic operations
      • Storage Slots
      • Storage of Arrays and Mappings
      • Memory Operations
      • Memory: how solidity uses memory
      • Memory: Return, Require, Tuples and Keccak256
      • Memory: Logs and Events
      • Inter-contract calls
      • calldata
      • free memory pointer
    • Yul Exercises
      • read state variable
      • read mapping
      • iterate Array, Return Sum
    • memory-safe
  • Upgradable Contracts
    • Upgradability & Proxies
    • UUPS Example
    • Minimal Proxy Example
    • TPP Example
    • 🚧Diamond
      • On Storage
  • Gas Opt
    • Block Limit
    • gasLimit & min cost
    • Solidity Optimiser
    • Memory v calldata
    • Memory caching vs direct storage vs pointers
    • < vs <=
    • reverting early
    • X && Y, ||
    • constant and immutable
    • caching sload into mload
    • Syntactic Sugar
    • using unchecked w/o require
    • Compact Strings
    • Calling a view function
    • Custom errors over require
    • usage of this.
      • multiple address(this)
  • ERCs & EIPs
    • ERC-20.sol
      • Core functions
      • transfer()
      • transferFrom()
      • TLDR transfer vs transferFrom
    • Landing
      • ERC721.sol
      • EIP-721
        • LooksRare
        • Page 1
      • ERC-1271
      • EIP-2981
      • ERC-165
      • EIP-1167: Minimal Proxy Contract
    • VRFConsumerBase
    • UniswapV2Library
  • Yield Mentorship 2022
    • Projects
      • #1 Simple Registry
      • #2 Basic Vault
      • #3 ERC20+ETH Wrapper
        • setFailTransferTrue
      • #4 Fractional Wrapper
      • #5 Collateralized Vault
        • Process
        • Vault.sol
        • Testing
        • Chainlink Oracles
        • Pricing + Decimal scaling
        • Refactor for Simplicity
      • #9 Flash Loan Vault
        • Implementing ERC3156
        • Full code for lender
        • Ex-rate calculation
    • State Inheritance Testing
    • Testing w/ Mocks
    • Yield Style Guide
    • Github Actions
    • TransferHelper.sol
    • math logic + internal fn
    • Interfaces: IERC20
  • Foundry
    • Overview
    • Importing Contracts
    • Testing
      • stdError.arithmeticError
      • assume vs bound
      • Traces
      • label & console2
      • std-storage
  • Smart Contract Security
    • Damn Vulnerable Defi
      • 1. Unstoppable
      • 2. Naive receiver
      • 3. Truster
      • 4. Side Entrance
      • 5. The Rewarder
      • 6. Selfie
      • 7. Compromised
      • 8. Puppet
      • 9. Puppet V2
      • 10 - Free Rider
    • Merkle Tree: shortened proof attack
  • Fixed-Point Math
    • AMM Math
  • Solidity Patterns
    • checks-effects-interactions pattern
    • Router // batch
    • claimDelegate: stack unique owners
    • claimDelegate: cache previous user
  • Array: dup/ascending check
  • Deployment
    • Behind the Scenes
    • Interacting with External Contracts
    • Logging, Events, Solidity, Bloom Filter
  • Misc
    • Mnemonic Phrases
    • Bidul Ideas
  • Archive
    • Brownie Framework
      • Brownie basics
        • storing wallets in .env
        • Deployment to ganache
        • Interacting with contract
        • Unit Testing
        • Testnet deployment
        • Interacting w/ deployed contract
        • Brownie console
      • Brownie Advanced
        • Dependencies: import contracts
        • helpful_scripts.py
        • verify and publish
        • Forking and Mocking
        • Mocking
        • Forking
      • Testing
      • Scripts Framework
        • deploy.py
        • get_accounts
        • deploy_mocks()
        • fund_with_<token>()
      • Brownie Networks
    • Brownie Projects
      • SharedWallet
        • Multiple Beneficiaries
        • Common Code Contract
        • Adding Events
        • Renounce Ownership
        • Separate Files
      • Supply Chain
        • ItemManager()
        • Adding Events
        • Adding unique address to each item
      • Lottery
      • Aave - Lending and Borrowing
        • Approve & Deposit
        • Borrow
      • NFT
      • Advanced Collectible
        • adv_deploy() + Testing
        • Create Metadata
        • Setting the TokenURI
    • node npm
    • Ganache
    • Truffle
    • Remix
    • Installing Env
Powered by GitBook
On this page
  • TLDR
  • Example
  • Other notes on reference:
  • calldata vs memory
  • Why is calldata cheaper?
  • Additional reading
  1. Compendium

Data Location and Assignment Behaviors

Every reference type contains information on where it is stored. There are three possible options: memory, storage, and calldata. The set location is important for semantics of assignments, not only for the persistence of data:

storage: The location type where the state variables are stored on blockchain which means types that has storage location are persistent.

memory: Variables are in memory and they exists during the function call which means variables that got this location are temporary and after function execution finished, they won’t exist.

calldata: Non-modifiable, non-persistent data location where function arguments are stored, behaves mostly like memory data location and only available for external functions.

  • Assignments between storage and memory (or from calldata) always generate an independent copy.

  • Assignments from memory to memory only create references. This means that changes to one memory variable are also visible in all other memory variables that refer to the same data.

  • Assignments from storage to a local storage variable only assigns a reference.

  • All other assignments to storage always creates independent copies. Examples for this case are assignments to state variables or to members of local variables of storage struct type, even if the local variable itself is just a reference.

Example: memory to memory

contract localVar {

    function play() public pure returns(bytes memory) {
        bytes memory data;
        bytes memory meal = hex"f00df00d";

        data = meal;
        data[3] = 0xed;

        return meal;        // 0xf00df0ed
    }
}
  • modifying data, also modified meal

TLDR

  • use calldata when you only need read-only data, avoiding the cost of allocating memory or storage.

  • use memory if you want your argument to be mutable.

  • use storage if your argument will already exist in storage, to prevent copying something into storage over into memory unnecessarily.

Example

You can convert implicitly storage to memory and calldata to memory, but the reverse is not possible.

string public stor = "banana";

function test(string calldata calld) external view {
    string memory memo = "pizza";

    foo(memo, stor);
    foo(calld, stor);  // Creates a copy of calld in memory and passes as parameter
    foo(stor, stor);   // Creates a copy of stor in memory and passes as parameter
    
    foo(memo, stor);
    // foo(memo, memo); // Cannot convert from memory to storage
    // foo(memo, calld); // Cannot convert from calldata to storage
}

function foo(string memory, string storage) internal view {
    
}

/* This fails because you can only use calldata on external functions
function bar(string calldata) internal view {
    
} */

Other notes on reference:

  • Reference types don’t necessarily fit into 32bytes — 256 bits.

  • Amount of gas consumed during execution depends on data location. Creating independent copies from reference types are expensive thus it is recommended that mostly inside functions we should choose working with memory data location.

  • We must be careful in scenario when two or more different variables point same data location since any change in one variable will impact the others.

calldata vs memory

Calldata is almost the same as memory but you cannot manipulate (change) the variable. It's also more gas efficient.

When using (string memory name), a copy of the parameter is passed into delete holder[name] , as per the function below:

    function release(string calldata name) public {
        require(holder[name] == msg.sender, "Not your name!");
        delete holder[name];
        emit Release(msg.sender, name);
    }

However, using (string calldata name), the parameter would be passed directly into delete holder[name] , without making a copy, thereby saving gas.

    function release(string memory name) public {
        require(holder[name] == msg.sender, "Not your name!");
        delete holder[name];
        emit Release(msg.sender, name);
    }

Since in the above function, the function parameter name is not modified within the scope of the function, we can opt to use calldata instead.

No point in making a fresh copy in a new memory location (which is what setting it to 'memory') does.

Use memory if you want to be able to manipulate the values and calldata when the parameter remain immutable.

One gotcha on this is that if you pass in a string (or bytes) from a different internal function where you had just created that string in memory, then you can't use calldata here. Example:

contract X {
 function sendString() internal {
    string memory y = "Raja is cool";
    callFunction(y);
 }

//ERROR: because calldata is only used from external calls, 
//and y was created in memory before
 function callFunction(string calldata y) {}  
}

Non-modifiable, non-persistent data location where function arguments are stored,

Why is calldata cheaper?

calldata are only parameters of a function which is declared as external, which value is allocated by the caller, that's why it's gas cost is lower.

For this reason only parameters of an external function can be declared as calldata (no variables declared inside the function and no parameters of a function which is not external)

Additional reading

PreviousLocal Variables (Storage v Memory)NextModifiers & Inheritance & Import

Last updated 2 years ago

https://ethereum.stackexchange.com/questions/107028/in-what-cases-would-i-set-a-parameter-to-use-storage-instead-of-memory/123692#123692
https://medium.com/coinmonks/solidity-storage-vs-memory-vs-calldata-8c7e8c38bce
https://ethereum.stackexchange.com/questions/74442/when-should-i-use-calldata-and-when-should-i-use-memory
https://ethereum.stackexchange.com/questions/123761/data-location-must-be-memory-or-calldata-for-parameter-in-function-but-none
https://www.youtube.com/watch?v=wOCIhzAuhgs&list=PLO5VPQH6OWdVQwpQfw9rZ67O6Pjfo6q-p&index=34