string이 문자열의 크기 외에 필요로 하는 메타 정보가 무엇인지, 또 어떤 원리로 조작에 활용되는지 알아보기 위한 귀납적 시도입니다.

0. 0개의 인자

cast calldata "callThis()" 함수 식별자만 출력합니다. - 0x243da18c

1-1. 1개의 인자, uint256

cast calldata "callThis(uint256)" 1 1개의 인자에 대해 32바이트가 추가됩니다.

  • 0x860fb2c1
  • 0x0000000000000000000000000000000000000000000000000000000000000001 // uint256: 1
이후 zero-padding은 생략합니다.

1-2. 1개의 인자, string

cast calldata "callThis(string)" "z" 동일하게 1개의 인자이지만, 96바이트가 추가됩니다.

  • 0x0a976363 // 함수 식별자
  • 0x20 // byte offset: 32 > 0x01
  • // string “z”
    0x01 // 문자열 길이: 1
  • 0x7a00000000000000000000000000000000000000000000000000000000000000 // 우측 정렬된 문자열 “z”

2. 2개의 인자, string, string

cast calldata "callThis(string, string)" "z" "a"

  • 0x515e7093
  • 0x40 // byte offset: 32 * 2 > 0x01(“a”)
  • 0x80 // byte offset: 32 * 4 > 0x01(“z”)
  • // string “z”
    0x01 // 문자열 길이: 1
  • 0x7a00000000000000000000000000000000000000000000000000000000000000 // 우측 정렬된 문자열 “z”
  • // string “a”
    0x01// 문자열 길이: 1
  • 0x6100000000000000000000000000000000000000000000000000000000000000 // 우측 정렬된 문자열 “a”

3. 3개의 인자, string, uint256, string

cast calldata "callThis(string, uint256, string)" "zzz" 2 "123456781234567812345678123456780"

  • 0x2ec90e97
  • 0x60 // byte offset: 32 * 3 > 0x21(“12..80”)
  • 0x02 // uint256: 2
  • 0xa0 // byte offset: 32 * 5 > 0x03(“zzz”)
  • // string “zzz”
  • 0x03 // 문자열 길이: 3
  • 0x7a7a7a0000000000000000000000000000000000000000000000000000000000 // string “zzz”
  • // string “12..80”
    0x21 // 문자열 길이: 33
  • 0x3132333435363738313233343536373831323334353637383132333435363738 // string “12345678” * 4
  • 0x3000000000000000000000000000000000000000000000000000000000000000 // string “0”



  • 전달된 인자의 순서는 고정이다.

  • bytes, string, 정적/가변 배열 <T>[]은 모두 가변 인자이다. 이들은 실제 데이터는 먼 곳에 저장해두고, offset을 제공하여 참조 순서를 보장한다.

  • calldata를 적재하는 순간에는 뒤에 어떤 인자들이 올지 알 수 없다. 한 개의 가변 인자, 그 뒤로 수많은 정적 인자가 뒤따라 올 수도 있다. 즉, O(n)으로 calldata를 적재하려면, 모든 인자를 순서대로 읽는 순간 저장해야 한다는 뜻이다. 그렇다면 가장 합리적인 방법은 가변 인자는 유효한 메모리 영역 끝에서부터 저장하는 것이다.

    • 이렇게 뒤에서부터 offset을 계산하면, 가장 최근에 적재된 가변 인자의 offset만 저장할 수 있다면, 새로운 가변 인자의 offset은 기존 offset + 현재 size로 쉽게 계산할 수 있다.
    • 그렇지 않다면, 가변 인자 사이마다 입력되는 정적 인자의 개수를 고려하여야만 하고, calldata를 적재하는 입장이든, 적재된 data를 해석하는 입장이든 과정이 더 복잡해질 것은 자명하다.



Reference