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
  • Memory
  • Calldata
  • memory vs calldata
  • Why bother using memory then?
  • More on calldata
  • Gotcha!
  • Why is calldata cheaper?
  • EXCEPTION: Layer-2
  • Links
  1. Gas Opt

Memory v calldata

PreviousSolidity OptimiserNextMemory caching vs direct storage vs pointers

Last updated 2 years ago

Memory

Used for storing variables that are only needed temporarily during the execution of a function, and cannot be accessed outside the function.

  • can be used for both declaring function parameters and within function logic

  • mutable (variable values are modifiable)

  • non-persistent (the value does not persist after the transaction has completed)

More on memory keyword:

Calldata

Calldata is a non-modifiable, non-persistent area where function arguments are stored, and behaves mostly like memory.

  • used to hold function arguments passed in from an external caller; user or another contract.

  • immutable: calldata is read-only and cannot be modified by the function.

  • calldata is cheaper than memory.

memory vs calldata

function doSome(bytes calldata data) external {}    // uses less gas

function doSome(bytes memory data) external {}
  • By declaring calldata, you can avoid the overhead of copying data into memory and reduce the amount of gas needed to execute the function. [CALLDATALOAD]

  • memory is more expensive than calldata. Because, it will copy the data from calldata into local memory, as an additional step. [CALLDATACOPY]

Why bother using memory then?

// this will not compile
function testImmutable(bytes calldata myBytes) external {
    myBytes[0] = 0xab;
}
  • We cannot modify calldata; read-only

  • If you need to modify function arguments that are stored in calldata, you must first copy them into memory.

  • So in this scenario, we are better served declaring myBytes as memory

Alternatively

// this is more gas intensive 
function testCallData(bytes calldata data) external {
        bytes memory localData = data;     //create a mutable copy in memory
        localData[0] = 0xab;
}

// this is less gas intensive
function testMemory(bytes memory data) external {
        data[0] = 0xab;
}
  • memory can be cheaper than calldata if you need to do modifications to the external data passed to the function.

  • use calldata when you are not going to modify the value of the variable passed as calldata inside the function.

If you only need to read the data, you can save some gas by storing it in calldata.

More on calldata

  • calldata is where data from external calls to functions are stored.

  • calldata contains parameters of a function as allocated by the external caller

  • msg.data of an external call is held in calldata

  • A byte of calldata costs either 4 gas (if it is zero) or 16 gas (if it is any other value).

In external calls to functions (when passing parameter string), opt to use calldata string, instead of memory string.

Gotcha!

One gotcha on this is that if you pass in a string (or bytes) from an 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 = "Calnix 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) {
   // ....do something 
 }  
 
}
  • when execution moves from line 4 to 5, it is not an external call. There is no msg.data that holds calldata to be passed into callFunction

  • so in the absence of msg.data, there is no calldata that can be accessed.

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.

EXCEPTION: Layer-2

  • In the context of a rollups/layer-2, calldata goes from the cheapest resource to the most-expensive resource.

Links

https://stackoverflow.com/questions/33839154/in-ethereum-solidity-what-is-the-purpose-of-the-memory-keyword
https://ethereum.stackexchange.com/questions/74442/when-should-i-use-calldata-and-when-should-i-use-memory/74443#74443
https://stackoverflow.com/questions/33839154/in-ethereum-solidity-what-is-the-purpose-of-the-memory-keyword
https://betterprogramming.pub/solidity-tutorial-all-about-calldata-aebbe998a5fc