본문으로 건너뛰기
0%

Yul에서 packed storage 변수를 읽고 쓰는 방법

Mark Jonathas headshot

작성자 Mark Jonathas

2023년 8월 1일에 게시됨2분 읽기

Yul스마트 컨트랙트 내부에서 어셈블리 언어 형태를 작성하는 데 사용할 수 있는 중간 수준 프로그래밍 언어입니다. Yul의 문법과 storage 동작 방식을 배운 뒤에는, Yul에서 packed storage 변수를 읽고 쓰는 방법을 이해하는 것이 중요합니다.

Yul에서 packed storage 변수를 읽고 쓰는 방법

var5를 4로 바꾸고 싶다고 가정해봅시다. var5가 slot 3에 위치한다는 것을 알고 있으므로, 다음과 같이 시도해볼 수 있습니다:

getValInHex(3)을 사용해보면 slot 3이 다음과 같이 다시 쓰여진 것을 볼 수 있습니다:

0x0000000000000000000000000000000000000000000000000000000000000004

문제는 이제 var4가 0으로 다시 쓰여졌다는 것입니다. 이번 섹션에서는 packed 변수를 읽고 쓰는 방법을 다루는데, 그 전에 Yul 문법에 대해 조금 더 알아볼 필요가 있습니다.

이런 연산들이 익숙하지 않아도 걱정할 필요 없습니다. 지금부터 예제와 함께 하나씩 살펴보겠습니다.

먼저 and()부터 시작해보겠습니다. 두 개의 bytes32 값을 and() 연산자에 적용해서 어떤 결과가 나오는지 확인해보겠습니다.

출력 결과를 보면 다음과 같습니다:

0x0000000000000000000000009acc1d6aa9b846083e8a497a661853aae07f0f00

and()가 하는 일은 양쪽 입력의 각 비트를 보고 값을 비교하는 것이기 때문입니다.

두 비트가 모두 1이면 (이진수 관점에서: 활성 또는 비활성) 해당 비트를 그대로 유지합니다.

그렇지 않으면 0으로 설정됩니다.

이번에는 or()의 코드를 봅시다.

이번 출력 결과는 다음과 같습니다:

0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff

이는 둘 중 하나의 비트라도 활성화되어 있는지 확인하기 때문입니다.

mask 변수를 다음과 같이 바꾸면 어떻게 되는지 살펴봅시다:

0x00ffffffffffffffffffffff0000000000000000000000000000000000000000

보시다시피 출력 결과가 다음과 같이 바뀝니다:

0x00ffffffffffffffffffffff9acc1d6aa9b846083e8a497a661853aae07f0f00

첫 바이트가 0x00인 것에 주목하세요. 두 입력 모두 첫 바이트에 활성화된 비트가 없기 때문입니다.

xor()는 조금 다릅니다. 한쪽 비트는 활성(1)이고 다른 쪽 비트는 비활성(0)이어야 합니다.

다음은 코드 예시입니다:

출력 결과는 다음과 같습니다:

0xffffffffffffffffffffffff0000000000000000000000000000000000000000

핵심 차이는 0x00과 0xff가 정렬된 위치에서만 출력의 활성 비트가 나타난다는 점입니다.

shl()과 shr()은 서로 매우 비슷하게 동작합니다. 둘 다 입력된 비트 수만큼 입력값을 시프트합니다. shl()은 왼쪽으로, shr()은 오른쪽으로 시프트합니다.

코드를 살펴봅시다!

출력:

ans1: 0x0000ffff00000000000000000000000000000000000000000000000000000000

ans2: 0x00000000000000000000000000000000000000000000000000000000ffff0000

먼저 ans1을 살펴보겠습니다. 16비트(2바이트)만큼 shr()를 수행합니다. 보시다시피 마지막 두 바이트가 0xffff에서 0x0000으로 바뀌고, 첫 두 바이트는 오른쪽으로 두 바이트만큼 시프트됩니다. 이를 알면 ans2는 자명합니다. 비트가 오른쪽 대신 왼쪽으로 시프트되는 것뿐입니다.

var5에 쓰기 전에, 먼저 var4와 var5를 읽는 함수를 작성해봅시다.

출력 결과는 예상대로 1과 2입니다.

var4를 가져오려면 다음과 같이 값을 설정하는 mask만 사용하면 됩니다:

0x0000000000000000000000000000000000000000000000000000000000000001

그런 다음 1과 같은 uint128을 반환합니다.

var5를 읽을 때는 var4를 오른쪽으로 시프트해서 걷어내야 합니다.

그러면 다음과 같이 남고, 이를 반환할 수 있습니다:

0x0000000000000000000000000000000000000000000000000000000000000002

storage slot 하나에 두 개 이상의 변수가 packed되어 있는 값을 읽으려면 시프트와 마스크를 함께 사용해야 하는 경우도 있다는 점에 유의해야 합니다.

자, 이제 드디어 var5의 값을 4로 바꿀 준비가 되었습니다!

첫 단계는 storage slot 3을 로드하는 것입니다.

다음으로 mask를 만들어야 합니다.

var4를 읽을 때와 마찬가지로, 값을 다음과 같이 분리하고자 합니다:

0x0000000000000000000000000000000000000000000000000000000000000001

다음 단계는 새 값이 var5의 slot 위치에 맞게 다음과 같은 형태가 되도록 포맷하는 것입니다:

0x0000000000000000000000000000000400000000000000000000000000000000

var5를 읽을 때와 달리, 이번에는 값을 왼쪽으로 시프트합니다.

마지막으로 or()를 사용해 값을 32바이트 hexadecimal로 결합하고, 그 값을 slot 3에 저장합니다.

getValInHex(3)을 호출해서 작업을 확인할 수 있습니다.

이는 다음과 같은 결과를 반환하며, 이는 우리가 예상한 값입니다:

0x0000000000000000000000000000000400000000000000000000000000000001

좋습니다. 이제 packed storage slot을 읽고 쓰는 방법을 알게 되었습니다! 다음으로 Yul에서 Memory, Storage, Smart Contract Calls가 어떻게 동작하는지 알아보세요.

Background gradient

블록체인 매직을 만드세요

Alchemy는 가장 강력한 Web3 개발자 제품 및 도구를 리소스, 커뮤니티, 그리고 전설적인 지원과 결합합니다.