스마트 컨트랙트는 한번 기록이 되면 수정이 불가능 합니다.
Proxy
와 같은 특별한 형태도 있지만 일반적으로는 불가능하고 새로운 컨트랙트를 배포해야한 새로운 버전의 컨트랙트를 활용 가능합니다.
앞서 말했던 것처럼 Proxy
를 활용하면 컨트랙트를 업그레이드 가능하지만..
제가 스스로 테스트 해본 결과, Proxy
를 통해서 작동 시킬수 있지만 안타깝게도 데이터는 보존이 되지 않았습니다.
그래서 저는 다른 방식의 컨트랙트 데이터를 보존하면서, 업그레이드 가능한 컨트랙트를 구현 해 볼수 있는지 고민을 해보앗고
답은 데이터와 로직을 분리한 형태의 컨트랙트를 활용 하였습니다.
데이터와 로직을 분리한다는 말을 처음 들었으면 어떤 소리인지 의문이 들 수 있습니다.
저 같은 경우에는 처음에 변수명은 다른 컨트랙트에 적고, 함수를 통해서 해당 변수에 접근한다는 소리인가?? 정도로 간단하게 생각을 해보았는데.
이러한 방식은 그냥 같이 적는것과 차이가 없습니다.
그럼 어떻게 활용이 가능한지 간단한 코드를 통해서 살펴 보겠습니다.
👆 V1
contract V1{
uint256[] num;
function pushNum() public {
num.push(3);
}
function getNum() public view returns(uint256[] memory) {
return num;
}
}
간단한 컨트랙트가 있습니다.
👆 V2
import "./V1.sol";
contract V2 {
V1 private V1Contract;
function setV1(V1 _V1) public {
V1Contract = _V1;
}
funtion pushNum() public {
V1Contract.pushNum();
}
function getNum() public view returns(uint256[] memory){
V1Contract.getNum();
}
}
이와 V2의 컨트랙트도 있습니다.
V2같은 경우에는 V1의 컨트랙트 주소를 받아서 활용을 합니다.
msg.sender
는 V2의 컨트랙트 주소가 됩니다.
즉 이러한 구조로 작동을 하고 있습니다.
실질적으로 V2에서 실행을 시키면 V1의 값을 바꾸고 잇는 구조 입니다.
그럼 이떄 V2에 다른 함수를 추가하여 배포 하고 싶다면 어떻게 하면 될까요
간단합니다. 다른 컨트랙트를 배포하고 마찬가지로 V1을 실행시킬수 있는 환경을 조성 하면 됩니다.
👆 V3
import "./V1.sol";
contract V3 {
V1 private V1Contract;
function setV1(V1 _V1) public {
V1Contract = _V1;
}
funtion pushNum() public {
V1Contract.pushNum();
}
function getNum() public view returns(uint256[] memory){
V1Contract.getNum();
}
function getString() public pure returns(string memory){
return "string";
}
}
이러한 V3컨트랙트를 배포한다면 V2와는 다른 컨트랙트 이지만 마찬가지로 V1에 접근을 하고 있으며 데이터에 대한 정보는 V1에 있기 떄문에 이전 값을 잃어버지 않는 상태로 활용이 가능 합니다.
V2
는 없애 주어야 한다는 점 입니다.자 그러면 우리는 여기까지 진행을 하면 간단하게 데이터와 로직이 분리된 형태의 컨트랙트가 무엇인지 알 수가 있습니다.
👆 보통 컨트랙트를 import하는 것은 많이 부담이 되는 행위 입니다. 그러기 때문에 인터페이스를 구성하여 해당 인터페이스를 활용하는 것도 좋은 방법 입니다.
자 그러면 이제 어떻게 하면 Proxy를 활용 가능할지 확인해볼 시간 입니다.
일단 처음에 들 수 있는 생각은 굳이 Proxy
를 활용 해야해?? 일 껍니다.
저도 사실 지금도 그런 생각을 하고 있습니다.
실제로 서비스를 할 떄에는 Owner
계정이 존재하고 해당 Owner
계정이 컨트랙트를 Paused
걸고 그후 새로운 컨트랙트를 배포 하고, 그다음 다시 재 실행 시키면 되지 않나??
라는 생각을 하고 있습니다.
하지만 굳이 Proxy까지 추가를 했을떄의 이점을 뽑자면 방금전의 상황을 없앨 수 있습니다.
Paused
할 필요 없이 Proxy에 적용되어잇는 주소값을 변경만 해주면 되기 떄문에 좀더 편하다?? 라는 점이 있는 것 같습니다.
서론이 길었으니 바로 적용을 해보겠습니다.
Proxy를 어디에 연결하여 전송을 해야 할까요??
기본적으로 Proxy는 fallback
을 활용하여 로직을 실행을 시킵니다.
그러기 떄문에 실질적으로 로직이 실행이 되는 로직 컨트랙트에 연결이 되어야 합니다.
아니면 KlaySwap에서도 Factory형태를 위해서 Proxy를 활용하고 있는데 이 부분을 참고 해도 됩니다.