티스토리 뷰
개발 부스러기는 완결된 형식의 글이 아닌, 다양한 시행착오를 기록하는 글입니다.
1. 함수 시그니처란?
다음과 같이 정의된 함수에서
function transfer(address to, uint256 amount) external {
...
}
함수의 이름과 공백 없이 콤마(,)로 연결 파라미터의 타입들을 소괄호로 묶은 문자열을 연결한 것을 함수 시그니처(function signature)라고 한다.
transfer(address,uint256)
함수 시그니처는 ABI(Application Binary Interface)를 파싱 할 때 사용하거나, 함수 선택자(function selector)를 계산하기 위해 사용된다. 함수 선택자는 함수 시그니처를 입력으로 한 keccack256 함수의 결괏값의 상위 4바이트를 가지는 값이며, 이 값은 스마트 컨트랙트의 실행 과정에서 실행할 함수를 식별하기 위해 사용된다.
bytes4(keccak256(bytes("transfer(address,uint256)")))
이번에는 함수 시그니처를 ABI로 파싱하는 방법을 살펴보자.
2. 함수 시그니처를 ABI로 파싱하기
viem 라이브러리를 사용하면 손쉽게 진행할 수 있다. 함수 시그니처 앞에 'function 키워드를 붙여야 한다. 이벤트의 경우는 'event, 커스텀 에러의 경우는 'error'를 붙여서 파싱 할 수 있다.
import { parseAbi } from "viem";
const abi = parseAbi(["function transfer(address,uint256)"]);
console.log(abi[0]);
// {
// name: 'transfer',
// type: 'function',
// stateMutability: 'nonpayable',
// inputs: [ { type: 'address' }, { type: 'uint256' } ],
// outputs: []
// }
Solidity 코드 상에서 함수 선택자를 계산하기 위해 필요한 함수 선택자에 비해, 파라미터의 이름이나 statusMutability 등을 포함하여 파싱할 수도 있다.
import { parseAbi } from "viem";
const abi = parseAbi(["function transfer(address to, uint256 amount) payable"]);
console.log(abi[0]);
// {
// name: 'transfer',
// type: 'function',
// stateMutability: 'payable',
// inputs: [
// { type: 'address', name: 'to' },
// { type: 'uint256', name: 'amount' }
// ],
// outputs: []
// }
이를 활용하여 다음과 같이 함수 시그니처를 파싱하여 ABI를 구하고, 이를 Form 컴포넌트로 전달하여 동적으로 입력 필드를 구성할 수 있다.
const installDataAbi = useMemo(() => {
if (!moduleMetadata) return [];
let signature = moduleMetadata.installDataSignature;
// check if signature has 'function' prefix
if (!signature.startsWith("function")) {
signature = `function ${signature}`;
}
try {
const abi: Abi = parseAbi([signature]);
return abi;
} catch (error) {
console.error("Error parsing install data signature", error);
return [];
}
}, [moduleMetadata]);
{installDataAbi.length > 0 && (
<InstallDataForm abi={installDataAbi} />
)}
3. ABI를 기반으로 동적으로 입력 필드 구성하기
컴포넌트로 전달된 ABI에서 AbiFunction 타입의 functionAbi를 추출한다.
if (abi.length === 0 || abi[0].type !== "function") {
return <Text>No valid install data ABI found.</Text>;
}
const functionAbi = abi[0] as Extract<Abi[0], { type: "function" }>;
functionAbi의 inputs 필드의 모든 값을 사용해 입력 필드를 구성한다.
{
functionAbi.inputs.map((input, index) => (
<FormControl key={index}>
<FormLabel>{input.name || `Input ${index + 1}`}</FormLabel>
<Input
type="text"
onChange={(e) => handleInputChange(input.name || "", e.target.value)}
placeholder={input.type}
/>
</FormControl>
));
}
이 때, useState 훅을 사용해 상태를 저장한다.
const [formValues, setFormValues] = useState<Record<string, string>>({});
const handleInputChange = (name: string, value: string) => {
setFormValues((prev) => ({ ...prev, [name]: value }));
};
데이터를 인코딩하기 위해 버튼을 클릭하면 'handleSubmit' 함수가 실행된다. 우선 입력값들을 각 타입에 맞게 타입을 변환하여 배열 구조로 values 값에 저장한다. 그리고 AbiParameter 타입의 배열인 functionAbi.inputs를 첫 번째 인자로, 변환된 입력값의 배열 values를 두 번째 인자로 전달하여 encodeAbiParameters 함수를 실행한다. 그 결괏값을 상태 변수에 저장한다.
const [encodedData, setEncodedData] = useState<`0x${string}` | null>(null);
const handleSubmit = () => {
try {
const functionAbi = abi[0];
if (functionAbi.type !== "function") {
throw new Error("Invalid ABI: first element is not a function");
}
const values = functionAbi.inputs.map((input) => {
const value = formValues[input.name || ""];
return parseInputValue(input, value);
});
const encodedData = encodeAbiParameters(functionAbi.inputs, values);
setEncodedData(encodedData);
} catch (error) {
console.log(error);
}
};
const parseInputValue = (input: AbiParameter, value: string): any => {
switch (input.type) {
case "uint256":
case "int256":
return BigInt(value);
case "bool":
return value.toLowerCase() === "true";
case "address":
return value as `0x${string}`;
// Add more cases for other types as needed
default:
return value;
}
};
4. 결과물
참고
'개발 부스러기' 카테고리의 다른 글
React에서 텍스트 오버플로우 감지 및 스크롤 애니메이션 구현하기 (0) | 2024.09.15 |
---|---|
Yarn을 사용한 모노레포에서 cannot find module 문제 해결하기 (0) | 2024.09.11 |
Next.js App Router + RainbowKit + SIWE + NextAuth.js를 사용해 지갑으로 로그인하는 기능 구현하기 (0) | 2024.07.31 |
Damn Vulnerable DeFi Foundry V3 업데이트 작업 (0) | 2024.02.28 |
Solidity 런타임 바이트코드 분해하기 (0) | 2024.02.13 |