Smart Contract Factory Pattern
2022.09.11
사용 이유
- 런타임에서 스마트 계약의 여러 인스턴스를 빠르게 생성해야 할때
- 모두 동일한 기능을 가진 많은 수의 계약을 처리할 때
solidity의 factory 유형
-
The normal factory pattern
이 공장 패턴은 각 배포에서 가스를 절약하기 위해 최적화 없이 다른 계약의 여러 인스턴스 를 배포
-
The cloned factory pattern
이 공장 패턴은 각 배포에서 가스를 절약하기 위해 최적화 중점을 둔 다른 계약의 인스턴스 배포
팩토리 패턴 예제
1. 일반적인 팩토리 패턴 예제
- 일반 팩토리 단점 : 높은 가스 비용이 든다.
// SPDX-License-Identifier: MIT
pragma solidity >0.4.23 <0.9.0;
contract Foundation {
string public name;
address public owner;
constructor(
string memory _name,
address _owner
) public {
name = _name;
owner = _owner;
}
}
-
개별 인스턴스 생성하는 팩토리 계약을 만들 컨트랙트
// SPDX-License-Identifier: MIT pragma solidity >0.4.23 <0.9.0; import "./Foundation.sol"; contract FoundationFactory { // Foundation 컨트랙트의 인스턴스 보유하는 변수 Foundation[] private _foundations; // Foundation 계약의 인스턴스를 배포하고 블록체인에 저장 function createFoundation( string memory name ) public { Foundation foundation = new Foundation( name, msg.sender ); _foundations.push(foundation); } // 블록체인에 저장된 Foundation 컨트랙트의 모든 인스턴스 검색 function allFoundations(uint256 limit, uint256 offset) public view returns (Foundation[] memory coll) { return coll; } }
여기에서 이 코드는 여러 인스턴스를 생성하려는
Foundation
계약을 가져오고_foundations
변수는 생성된Foundation
계약의 인스턴스를 보유합니다.createFoundation
함수는Foundation
계약의 인스턴스를 배포하고 블록체인에 저장하는 반면,allFoundations
함수는 블록체인에 저장된Foundation
계약의 모든 인스턴스를 검색합니다.
2. 클론 팩토리 패턴
- 동일한 계약을 배포하기 때문에 계약의 각 인스턴스는 동일한 바이트 코드를 갖기 때문에 각 배포에 대한 모든 바이트 코드를 저장하면 바이트 코드에 대한 가스 비용이 반복적으로 낭비됨
→ 이에 대한 해결책은 Foundation
계약의 한 인스턴스만 배포하고 Foundation
계약의 다른 모든 인스턴스가 프록시 역할을 하도록 하여 Foundation
계약의 첫 번째 인스턴스에 호출을 위임하고 프록시 계약의 맥락에서 함수를 실행할 수 있도록 하는 메커니즘입니다.
이로써 재단계약의 각 인스턴스는 자체적인 상태를 갖게 되며, 단순히 우리가 수립한 재단계약의 인스턴스를 라이브러리로 사용하게 된다.
-
이때 eip-1167 매커니즘 사용
알려진 고정 주소에 대한 모든 호출을 변경할 수 없는 방식으로 간단하고 저렴하게 복제 계약 기능에 위임하는 최소 바이트 코드 구현을 지정
-
해당 매머니즘을 사용하기 위한 모듈
npm install @optionality.io/clone-factory
// SPDX-License-Identifier: MIT
pragma solidity >0.4.23 <0.9.0;
import "./Foundation.sol";
import "@optionality.io/clone-factory/contracts/CloneFactory.sol";
import "zeppelin-solidity/contracts/ownership/Ownable.sol";
contract FoundationFactory is Ownable, CloneFactory {
address public libraryAddress;
event FoundationCreated(address newFoundation);
function FoundationFactory(address _libraryAddress) public {
libraryAddress = _libraryAddress;
}
function setLibraryAddress(address _libraryAddress) public onlyOwner {
libraryAddress = _libraryAddress;
}
function createFoundation(string _name) public onlyOwner {
address clone = createClone(libraryAddress);
Foundation(clone).init(_name);
FoundationCreated(clone);
}
}
주의 사항
- 마스터 컨트랙트가 문제가 생기면, 모든 클론의 작동이 중지 되어 상태와 잔액이 동결된다.
- 마스터 계약 생성자가 호출되는 유일한 시간은 마스터 계약이 생성되는 동안이므로 마스터 계약이 생성자에서 사전 초기화되었는지 확인해야한다.