Skip to content

Instantly share code, notes, and snippets.

@oneleo
Created December 22, 2025 16:10
Show Gist options
  • Select an option

  • Save oneleo/c9afe599a3903b812d4c9aff6b4ae820 to your computer and use it in GitHub Desktop.

Select an option

Save oneleo/c9afe599a3903b812d4c9aff6b4ae820 to your computer and use it in GitHub Desktop.
ENS Universal Resolver 查詢㳘程

Universal Resolver 查詢㳘程

參考: Universal Resolver https://docs.ens.domains/resolvers/universal/

Offchain / L2 Resolvers https://docs.ens.domains/resolvers/ccip-read#offchain-vs-l2-resolvers


前置 function

註:不使用純 Bash/Zsh 原因

  • 本質是 文字導向
  • 對 \x00(NULL byte)處理極差
  • 變數裡 不能安全保存 binary data
  • 換一台機器 → encoding 就歪了

> code ~/.zshrc

# Encode DNS/ENS name to DNS wire-format hex (RFC 1035)
function dnsEncode() {
  if [[ -z "$1" ]]; then
    echo "Usage: dnsencode <name>"
    return 1
  fi

  python3 - <<EOF "$1"
import sys
name = sys.argv[1]
out = b''
for label in name.split('.'):
    out += bytes([len(label)]) + label.encode()
out += b'\x00'
print('0x' + out.hex())
EOF
}

解析

前置

### 開新視窗讓 ~/.zshrc 生效
> FORWARD_NAME="aruun.eth" \
&& COIN_TYPE="$(( 0x80000000 + 10 ))" \
&& REVERSE_ADDRESS_LOWERCASE="0x2625b4da309819cb8b5eb8aee13c86ad0e097ef6" \
&& ALCHEMY_KEY="XXX" \
&& NAME_NODE_HASH=$(cast namehash ${FORWARD_NAME}) \
&& NAME_DNS_ENCODE=$(dnsEncode ${FORWARD_NAME}) \
&& RPC_URL="https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_KEY}" \
&& UNIVERSAL_RESOLVER="0xeEeEEEeE14D718C2B47D9923Deab1335E144EeEe"

### 常用函數
> RESOLVER_ADDR_FN="addr(bytes32 node, uint256 coinType) returns (bytes addressBytes)" \
&& UNIVERSAL_RESOLVER_RESOLVE_FN="resolve(bytes name, bytes data) returns (bytes result, address resolver)" \
&& UNIVERSAL_RESOLVER_REVERSE_FN="reverse(bytes lookupAddress, uint256 coinType) returns (string primary, address resolver, address reverseResolver)" \
&& UNIVERSAL_RESOLVER_CCIP_READ_CALLBACK_FN="ccipReadCallback(bytes response, bytes extraData)" \
&& RESOLVER_OFFCHAIN_LOOKUP_ERR="OffchainLookup(address sender, string[] urls, bytes callData, bytes4 callbackFunction, bytes extraData)"


正向解析

### 建立查詢 addr() 的 calldata
> ADDR_DATA=$(cast calldata ${RESOLVER_ADDR_FN} ${NAME_NODE_HASH} ${COIN_TYPE}) && echo ${ADDR_DATA}

0xf1cb7e061641f824d4ccacc9bb1b01927b7be79f0d6a054d87a265b4e0dd5a27060ee2ab000000000000000000000000000000000000000000000000000000008000000a

### 查詢 addr()
> CALL_RESOLVE_RESULT=$(cast call --rpc-url ${RPC_URL} ${UNIVERSAL_RESOLVER} ${UNIVERSAL_RESOLVER_RESOLVE_FN} ${NAME_DNS_ENCODE} ${ADDR_DATA}) \
&& ADDR_RESULT_RAW=$(echo "$CALL_RESOLVE_RESULT" | sed -n '1p') \
&& ADDR_RESOLVER=$(echo "$CALL_RESOLVE_RESULT" | sed -n '2p') \
&& echo ${ADDR_RESULT_RAW} \
&& echo ${ADDR_RESOLVER}

0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000142625b4da309819cb8b5eb8aee13c86ad0e097ef6000000000000000000000000
0x231b0Ee14048e9dCcD1d247744d114a4EB5E8E63

### 解析 addressBytes
### 註:預設解析成 returns 參數,若要解析成 methodId + params 參數,則需加 --input flag
> cast decode-abi ${RESOLVER_ADDR_FN} ${ADDR_RESULT_RAW}

0x2625b4da309819cb8b5eb8aee13c86ad0e097ef6

反向解析(CoinType = 60 時)

### 反向查詢
> CALL_REVERSE_RESULT=$(cast call --rpc-url ${RPC_URL} ${UNIVERSAL_RESOLVER} ${UNIVERSAL_RESOLVER_REVERSE_FN} ${REVERSE_ADDRESS_LOWERCASE} ${COIN_TYPE}) \
&& NAME_PRIMARY=$(echo "$CALL_REVERSE_RESULT" | sed -n '1p') \
&& NAME_RESOLVER=$(echo "$CALL_REVERSE_RESULT" | sed -n '2p') \
&& NAME_REVERSE_RESOLVER=$(echo "$CALL_REVERSE_RESULT" | sed -n '3p') \
&& echo ${NAME_PRIMARY} \
&& echo ${NAME_RESOLVER} \
&& echo ${NAME_REVERSE_RESOLVER}

"aruun.eth"
0x231b0Ee14048e9dCcD1d247744d114a4EB5E8E63
0xF29100983E058B709F3D539b0c765937B804AC15

反向解析(CoinType = 0x80000000 + XX 時,會觸發 CCIP-Read Gateway 的 OffchainLookup() Revert)

### 反向查詢並得到 OffchainLookup() 錯誤(這是已知錯誤,Gateway 資訊會被夾帶在錯誤中)
> OFFCHAIN_LOOKUP_ERR_RAW=$(cast call --rpc-url ${RPC_URL} ${UNIVERSAL_RESOLVER} ${UNIVERSAL_RESOLVER_REVERSE_FN} ${REVERSE_ADDRESS_LOWERCASE} ${COIN_TYPE} 2>&1 | sed -n 's/.*data: "\(0x[0-9a-fA-F]*\)".*/\1/p')

### 解析 OffchainLookup() 錯誤
> OFFCHAIN_LOOKUP_ERR=$(cast decode-calldata ${RESOLVER_OFFCHAIN_LOOKUP_ERR} ${OFFCHAIN_LOOKUP_ERR_RAW}) \
&& LOOKUP_SENDER=$(echo "$OFFCHAIN_LOOKUP_ERR" | sed -n '1p') \
&& LOOKUP_URLS=$(echo "$OFFCHAIN_LOOKUP_ERR" | sed -n '2p') \
&& LOOKUP_CALL_DATA=$(echo "$OFFCHAIN_LOOKUP_ERR" | sed -n '3p') \
&& LOOKUP_CALLBACK_FUNCTION=$(echo "$OFFCHAIN_LOOKUP_ERR" | sed -n '4p') \
&& LOOKUP_EXTRA_DATA=$(echo "$OFFCHAIN_LOOKUP_ERR" | sed -n '5p') \
&& echo ${LOOKUP_SENDER} \
&& echo ${LOOKUP_URLS} \
&& echo ${LOOKUP_CALLBACK_FUNCTION}

### 解析 urls
> CCIP_READ_GATEWAYS=($(echo $LOOKUP_URLS | sed 's/[][]//g; s/"//g' | tr ',' '\n' | sed 's/^[[:space:]]*//')) \
&& printf '%s\n' "${CCIP_READ_GATEWAYS[@]}" \
&& echo "${#CCIP_READ_GATEWAYS[@]}" # 陣列長度

https://ccip-v3.ens.xyz
x-batch-gateway:true
2

### 選定一組 CCIP-Read Gateway
> CCIP_READ_GATEWAY=${CCIP_READ_GATEWAYS[1]}

### 若 URL 含 {data} 則呼叫 Offchain Resolver CCIP-Read Gateway
### 否則呼叫 L2 Resolver CCIP-Read Gateway
if [[ "$CCIP_READ_GATEWAY" == *"{data}"* ]]; then
  # Offchain Resolver CCIP-Read (GET)
  gateway_url="${CCIP_READ_GATEWAY//\{sender\}/${LOOKUP_SENDER}}"
  gateway_url="${gateway_url//\{data\}/${LOOKUP_CALL_DATA}}"
  GATEWAY_RESPONSE_DATA=$(curl -sS "$gateway_url" | jq -r '.data')
else
  # L2 Resolver CCIP-Read (POST)
  GATEWAY_RESPONSE_DATA=$(curl -sS -X POST "$CCIP_READ_GATEWAY" -H "Content-Type: application/json" -d "{\"data\":\"${LOOKUP_CALL_DATA}\",\"sender\":\"${LOOKUP_SENDER}\"}" | jq -r '.data')
fi

### 驗證及取得結果
> CCIP_READ_REVERSE_RESULT_RAW=$(cast call --rpc-url ${RPC_URL} ${UNIVERSAL_RESOLVER} ${UNIVERSAL_RESOLVER_CCIP_READ_CALLBACK_FN} ${GATEWAY_RESPONSE_DATA} ${LOOKUP_EXTRA_DATA}) && echo ${CCIP_READ_REVERSE_RESULT_RAW}

0x0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000231b0ee14048e9dccd1d247744d114a4eb5e8e63000000000000000000000000f9edb1a21867ac11b023ce34abad916d29abf1070000000000000000000000000000000000000000000000000000000000000009617275756e2e6574680000000000000000000000000000000000000000000000

### 註:若想看驗證失敗的呼叫(不會回傳 data)
> cast call --rpc-url ${RPC_URL} ${UNIVERSAL_RESOLVER} ${UNIVERSAL_RESOLVER_CCIP_READ_CALLBACK_FN} "0xdead${GATEWAY_RESPONSE_DATA:6}" ${LOOKUP_EXTRA_DATA} --trace

Error: server returned an error response: error code 3: execution reverted

### 最後解析出 Name 的內容
> CCIP_READ_REVERSE_RESULT=$(cast decode-abi ${UNIVERSAL_RESOLVER_REVERSE_FN} ${CCIP_READ_REVERSE_RESULT_RAW}) \
&& CCIP_READ_NAME_PRIMARY=$(echo "$CCIP_READ_REVERSE_RESULT" | sed -n '1p') \
&& CCIP_READ_NAME_RESOLVER=$(echo "$CCIP_READ_REVERSE_RESULT" | sed -n '2p') \
&& CCIP_READ_NAME_REVERSE_RESOLVER=$(echo "$CCIP_READ_REVERSE_RESULT" | sed -n '3p') \
&& echo ${CCIP_READ_NAME_PRIMARY} \
&& echo ${CCIP_READ_NAME_RESOLVER} \
&& echo ${CCIP_READ_NAME_REVERSE_RESOLVER}

"aruun.eth"
0x231b0Ee14048e9dCcD1d247744d114a4EB5E8E63
0xF9Edb1A21867aC11b023CE34Abad916D29aBF107
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment