안녕하세요, 먼저 질문이 solidity와 관련이 높기 때문에 포럼 취지에 맞지 않는 질문이라면 죄송합니다.
KIP17을 Factory pattern과 비슷하게 구현 중이고, Contract의 대략적인 코드는 아래와 같습니다.
- 먼저 Factory에서 각각 deploy될 KIP17의 상속 컨트랙트입니다.
contract NFTToken is KIP17Full {
address payable owner;
constructor(string memory _name, string memory _symbol) KIP17Full(_name, _symbol) public {
owner = msg.sender;
}
…그외 함수들)
-
Factory가 될 컨트랙트입니다.
contract NFTFactory{
NFTToken public minter;
address[] public minters;
address payable owner;
mapping (string => NFTMinter[]) public tokenManager;
constructor() public {
owner = msg.sender;
}
function mintNewToken(string memory _tokenHash, string memory _name, string memory _symbol) public returns (NFTToken){
minter = new NFTToken(_name, _symbol);
minters.push(address(minter));
tokenManager[_tokenHash].push(minter);
return tokenManager[_tokenHash][0];
}
function isMintExist(string memory _tokenHash) public view returns(uint){
return minters.length;
}
…그 외 함수들
}
각 NFTToken 컨트랜트들은 tokenHash라는 임의의 해쉬값에 의해 구분되고, 이를 tokenManager라는 mapping으로 관리하려고 합니다. Factory의 isMintExist는 최초로 mintNewToken이 호출된 다음 잘 저장이 되었는지 확인하고자 만든 함수입니다.
비슷한 패턴과 예제들을 솔리디티 예제들에서 보고 만든 것인데, 만들어진 NFTToken들이 각 mapping이나 배열에 전혀 저장이 되지 않습니다. isMintExist에서 minters.length를 찍어보아도 0으로 출력됩니다. 다만, 최초로 mintNewToken을 호출하면 리턴값으로 나오는 minter instance의 주소(address형과는 다르지만 형식이 주소이기에)는 잘 리턴됩니다만, 이후 isMintExist를 호출시에 모든 배열들이 초기화된 것처럼 어떤 값도 저장되어있지 않습니다.
혹시 ERC721과 KIP17이 관련 부분에서 다른 점이 있을까요?
안녕하세요
다음과같이 테스트 해보았는데 정상적으로 작동하는것을 확인했습니다.
pragma solidity 0.5.6;
import "@openzeppelin/contracts/ownership/Ownable.sol";
contract Item {
uint256 public id;
constructor() public {
}
function setId(uint256 newId) public {
id = newId;
}
}
contract Game is Ownable {
mapping(address => Item[]) items;
constructor() public {
Item item = new Item();
item.setId(100);
items[msg.sender].push(item);
items[msg.sender].length; // 1
items[msg.sender][0].id(); // 100
}
}
contract를 추가하는 방법도 있지만
struct를 사용하는 방법을 추천드립니다. struct를 사용하는 방법은 다음과 같습니다.
pragma solidity 0.5.6;
import "@openzeppelin/contracts/ownership/Ownable.sol";
contract Game is Ownable {
struct Item {
uint256 id;
}
mapping(address => Item[]) items;
constructor() public {
Item memory item;
item.id = 100;
items[msg.sender].push(item);
items[msg.sender].length; // 1
items[msg.sender][0].id; // 100
}
}
3개의 좋아요
감사합니다! 말씀하신대로 적용해보았는데요,
- 만들어질 컨트랙트에 따로 setInit()라는 function을 만들어서 넘어온 argument를 저장하는 함수를 만들었습니다.
contract NFTToken is KIP17Full {
address payable owner;
constructor(string memory _name, string memory _symbol) KIP17Full(_name, _symbol) public {
owner = msg.sender;
}
function setInit(string memory _name, string memory _symbol) public{
** name = _name;**
** symbol = _symbol;**
** }**
…그외 함수들)
- 팩토리 컨트랙트에는 새로운 컨트랙트를 선언하고 setInit()를 호출했습니다.
contract NFTFactory{NFTToken public minter;address[] public minters;address payable owner;mapping (string => NFTMinter[]) public tokenManager;constructor() public {
owner = msg.sender;
}function mintNewToken(string memory _tokenHash, string memory _name, string memory _symbol) public returns (NFTToken){
minter = new NFTToken(_name, _symbol);
**minter.setInit(_name, _symbol);**
minters.push(address(minter));
tokenManager[_tokenHash].push(minter);
return tokenManager[_tokenHash][0];
}function isMintExist(string memory _tokenHash) public view returns(uint){return minters.length;}
…그 외 함수들
}
deploy를 했더니 다음과 같은 오류가 발생했습니다.
“NFTFactory” received a generic error from Geth that
can be caused by hitting revert in a contract constructor or running out of gas.
아무래도 가스비 문제는 아니고 setInit()를 호출했을 때 revert된 것 같습니다.
앞서 제시해주신 예제에서는 Factory를 deploy하면서 constructor에서 곧바로 Item의 constructor를 호출하시는데, 저의 경우엔 프론트엔드에서 Factory의 함수를 호출할 때 각 NFTToken을 생성하도록 만들고 싶어서 따로 함수를 만들어주었는데요,
덕분에 문제의 답에 접근해가는 느낌입니다만, 아무래도 new NFTToken(_name, _symbol)로 컨트랙트를 생성할 때 문제가 생기는 것 같습니다…
안녕하세요
아래와 같이 다시 바오밥 네트워크에서 테스트 해보았습니다.
// Token.sol
pragma solidity 0.5.6;
import "hardhat/console.sol";
import "@klaytn/contracts/token/KIP17/KIP17Token.sol";
contract Token is KIP17Token {
constructor(address owner, string memory name, string memory symbol) public KIP17Token(name, symbol) {
/*
TokenFactory 안에서 실행하면 TokenFactory address만
MinterRole, PauserRole권한을 가지기 때문에 다음과같이 추가해주었습니다.
*/
addMinter(owner);
addPauser(owner);
}
}
pragma solidity 0.5.6;
import "hardhat/console.sol";
import "@openzeppelin/contracts/ownership/Ownable.sol";
import "./Token.sol";
contract TokenFactory is Ownable {
mapping(address => Token[]) tokens;
constructor() public {
}
function createToken(string memory name, string memory symbol) public {
Token token = new Token(msg.sender, name, symbol);
tokens[msg.sender].push(token);
}
function getMyTokens() public view returns (Token[] memory) {
return tokens[msg.sender];
}
}
이렇게 하면 잘 되는것 같습니다.
아래 createToken함수 호출 트랜잭션 데이터입니다.
주의사항
- A Contract에서 B Contract함수를 호출할때 B함수 안의 msg.sender값은 A Contract Address가 됩니다.
2개의 좋아요
감사합니다.
revert된 이유는 mapping(string => 컨트랙트)를 선언하고 argument로 넘어온 string이 memory여서 생기는 문제 같습니다. string 대신 address로 설정하니 잘 실행되었고, 제시해주신 예시 참고하여 잘 완성했습니다!
2개의 좋아요