> For the complete documentation index, see [llms.txt](https://whs3-doge5k.gitbook.io/smartcontract-security-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://whs3-doge5k.gitbook.io/smartcontract-security-docs/basics/3.-evm/evm-2/undefined-1/mapping.md).

# mapping

### **특징**

| 항목    | 설명                                          |
| ----- | ------------------------------------------- |
| 타입명   | mapping(Key => Value)                       |
| 크기    | 동적 (키 개수만큼 확장됨)                             |
| 패킹 가능 | ✕ (항상 단일 키-값 쌍 단위로 개별 저장됨)                  |
| 기본 저장 | keccak(키 + 슬롯) 형식으로 저장됨 (+는 두 값을 이어붙인다는 의미) |

키와 슬롯 값이 32바이트보다 짧은 경우, EVM은 이를 항상 상위 바이트에 0을 채워 32바이트로 패딩한다. 이는 keccak256 해시 입력이 항상 64바이트(32바이트 키 + 32바이트 슬롯) 형식이어야 하기 때문이다.

이때 슬롯은 매핑 변수가 컨트랙트 내에서 차지하는 슬롯 번호이며, 상태 변수의 선언 순서에 따라 0부터 순차적으로 할당된다.

***

### **예시 1**

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Example6 {
    mapping(address => uint) public balances;

    constructor() {
        balances[0x1111111111111111111111111111111111111111] = 100;
        balances[0x2222222222222222222222222222222222222222] = 200;
    }
}
```

<figure><img src="https://2462457462-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FLf9vwRuaws92ULgGvzTi%2Fuploads%2FfPkolTGqzbkRIfd5aZ5f%2Fimage.png?alt=media&amp;token=f84bdf79-eab2-4f94-9853-522e7898e4f4" alt=""><figcaption></figcaption></figure>

```
// 스토리지

address: 0x56136bdb02b44e818120154143c922d9485e19e5b7288e7176da184391d4457c
0x00000000000000000000000000000000000000000000000000000000000000c8

address: 0xf043c50fe795c69f30b8ff78b84032dc53a9d87ca283ae10a1dacfbb648e83ef
0x0000000000000000000000000000000000000000000000000000000000000064
```

mapping 값이 저장되는 주소는 keccak(x+y) 형태로 이루어진다.(x:키, y:슬롯)

#### **계산 과정**

1. x : 주소(20 바이트)를 패딩시켜 32바이트로 만든다.
2. y: 매핑 변수가 위치한 슬롯 0을 패딩시켜 32바이트로 만든다.
3. x와 y를 이어 붙여 keccak(x+y)를 계산한다.

balances\[x] 값이 저장된 슬롯은 아래와 같다.

```bash
# balance[0x1111111111111111111111111111111111111111]
python3 -c "import sys; sys.stdout.buffer.write(bytes.fromhex('0000000000000000000000001111111111111111111111111111111111111111')+bytes.fromhex('0000000000000000000000000000000000000000000000000000000000000000'))" | cast keccak
# 값: 0xf043c50fe795c69f30b8ff78b84032dc53a9d87ca283ae10a1dacfbb648e83ef
```

```bash
# balance[0x2222222222222222222222222222222222222222]
python3 -c "import sys; sys.stdout.buffer.write(bytes.fromhex('0000000000000000000000002222222222222222222222222222222222222222')+bytes.fromhex('0000000000000000000000000000000000000000000000000000000000000000'))" | cast keccak
# 값: 0x56136bdb02b44e818120154143c922d9485e19e5b7288e7176da184391d4457c
```

***

### **예시 2**

이전 예시에서 balances 매핑 변수의 슬롯은 스토리지에 나오지 않았지만, 0을 가정하고 계산하였다. 그런데 실제로는 스토리지에 값이 등록되지 않는다. 아래의 예시를 보면 알 수 있다.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Example7 {
    uint256 public a;
    mapping(address => uint) public balances;
    uint256 public b;

    constructor() {
        a = 1;
        balances[0x1111111111111111111111111111111111111111] = 100;
        b = 2;
    }
}
```

<figure><img src="https://2462457462-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FLf9vwRuaws92ULgGvzTi%2Fuploads%2FqtKLP9BMn0AiWI1akM9x%2Fimage.png?alt=media&amp;token=cd00492c-bfed-43b9-b5d2-b82de950f411" alt=""><figcaption></figcaption></figure>

```
// 스토리지

address: 0x0000000000000000000000000000000000000000000000000000000000000000
0x0000000000000000000000000000000000000000000000000000000000000001

address: 0x0000000000000000000000000000000000000000000000000000000000000002
0x0000000000000000000000000000000000000000000000000000000000000002

address: 0x8eec1c9afb183a84aac7003cf8e730bfb6385f6e43761d6425fba4265de3a9eb
0x0000000000000000000000000000000000000000000000000000000000000064
```

코드 상에서 a와 b 변수 사이에 매핑 변수가 위치해있지만, 실제로 스토리지에 값은 할당되지 않고 슬롯은 0과 2로 나타난 것을 볼 수 있다.

mapping도 슬롯을 차지하지만, 직접 값을 저장하지 않는 방식을 EVM에서 사용하고 있기 때문에 슬롯에는 값이 나타나지 않는다.

그러나 예시 2에서 볼 수 있듯이, 매핑 변수도 결국 선언 위치에 따라 keccak(x+y)에 들어갈 y 값이 달라질 수 있으므로 슬롯 계산 과정에서 필수적으로 적용해야한다.
