블록체인의 불변성이라는 특성 때문에 한 번 저장된 데이터는 변경이나 삭제가 불가능하며 오로지 추가만 가능하다. 이는 스마트 컨트랙트를 작성할 때도 마찬가지다. 한 번 배포가 된 스마트 컨트랙트는 버그가 발견되더라도 변경이 불가능하다. 만약 수정된 새로운 컨트랙트를 배포하더라도 사용자에게 새로운 컨트랙트 주소를 전달해야 하며, 이전 데이터를 옮겨오는데 상당한 시간과 비용이 발생할 수 있다.
이런 문제를 해결할 수 있는 방식이 Proxy Pattern
이다. 로직과 데이터를 분리하여 각각 다른 Contract를 만들고 Proxy contract
는 이 컨트랙트의 주소를 저장한다. 사용자는 오로지 Proxy contract
에만 상호작용하기 때문에, 새로운 컨트랙트가 배포 되더라도 Proxy contract
에 저장된 주소만 변경한다면 사용자는 아무런 변경 사항 없이 업그레이드 된 컨트랙트를 사용할 수 있다.
여기서 fallback
함수와 delegate call
을 활용하기 때문에 이 개념에 대해서 알아둘 필요가 있다.
사용자가 컨트랙트의 특정 함수를 호출했는데, 만약 그 함수가 컨트랙트에 없다면 fallback 함수를 호출하게 된다. Proxy Contract를 구현할 때 fallback 함수를 적극 활용한다. 특징은 아래와 같다.
external
로 선언되어야 한다.modifier
를 가질 수 있고 virtual
, override
모두 가능하다.이런 특징 외에도 컴파일 버전 0.6.0을 기점으로 선언 방법 등이 다르다.
~0.5.17
function () external 또는 function () external payable
- 함수의 이름이 없이 함수가 선언된다.
payable
를 선언하면,transfer
함수나send
함수를 사용하지 않고 이더를 전송받을 수 있다.
0.6.0~
function
키워드 없이 선언된다.
1) receive 함수receive () external payable
receive
함수가 이더를 전송받는 기능을 담당한다.- 무조건
external
과payable
을 함께 선언해야 한다.- 만약
receive
함수가 없다면payable
을 포함하는fallback
함수에서 이더를 전송 받을 수 있다.2) fallback 함수
fallback () external [payable] 또는 fallback () external [payable] or fallback (bytes calldata input) external [payable] returns (bytes memory output)
fallback
함수와receive
함수로 분리하여 담당한다.- 만약 매개변수가 존재한다면,
msg.data
와 같은 full data를 컨트랙트에 보내야 output을 반환한다. 반환 값은 abi로 인코딩되어 있지 않으며, 아무런 수정없이 반환된다.
delegate call
은 다른 외부 컨트랙트의 함수를 사용하면서 데이터는 호출한 컨트랙트에 저장한다.
msg.sender
와 msg.value
는 호출될 당시의 값이 사용되며 변경되지 않는다.delegate call
은 boolean 타입의 값과 호출한 함수의 리턴 값을 반환한다. (boolena타입은 호출 성공 시 true를, 호출 실패 시 false를 반환)upgradable contract
를 구현할 때 많이 사용된다.proxy contract
는 다른 컨트랙트를 대신 호출해주는 컨트랙트로, 이를 이용해서 upgradable contract
를 구현한다. 따라서 실제 로직을 가지고 있지는 않지만, 로직을 갖고 있는 컨트랙트, 즉 implementation contract
의 주소를 담고 있다.
사용자는 proxy contract
를 이용하여 함수를 호출하지만, 실제로 함수는 implementation contract
에 있기 때문에 fallback
함수를 활용한다. 또한, 업그레이드 될 때마다 데이터가 변하면 안되기 때문에 delegate call
로 함수를 호출하게 된다.
아래 그림은 proxy contract
의 코드를 분석한 내용입니다.
혹시나 더 자세한 내용이 필요하신 분은 아래 Reference에 기재된 링크를 참고해주세요.