일단 솔리디티 코드 작성 작업이 끝나게 되었습니다.
이더스캔에서 이름이 인식되지 않는 이유는 별게 아니였습니다
일단 변경하는데에 시간이 필요하였습니다.
저는 함수 이름을 바꿔서 그런줄 알아서
함수 이름을 그대로 사용하 였습니다.
하지만 이런식이면 ERC-721과 ERC-20을 하나의 컨트랙트에서 사용이 불가능 합니다...
그러기 떄문에
import "./libraries/Token.sol";
import "./libraries/NFT.sol";
contract Character is NFT("item", "ITM") {
Token private gold;
이런식으로 참고할수 있는 변수를 선언함으로써 활용을 하였습니다.
constructor(address token) {
gold = Token(token);
}
function goldTotalSupply() public view returns (uint256) {
return gold.totalSupply();
}
function goldBalanceOf(address account) public view returns (uint256) {
return gold.balanceOf(account);
}
function goldCheck(address account) public view returns (bool) {
return gold.check(account);
}
function goldMint(address to, uint256 amount) public {
gold.mintGold(amount, to);
}
function goldTransfer(
address from,
address to,
uint256 amount
) public {
gold.transfer(from, to, amount);
}
function goldBurn(address account, uint256 amount) public {
gold.burn(account, amount);
}
function mintAll(address[] memory account, uint256 amount) public {
gold.mintGoldAll(account, amount);
}
그후 Character
컨트랙트를 작성 하였습니다.
function makeCharacter(address _address) public returns (bool) {
require(checkUser[_address] == false);
checkUser[_address] = true;
Characters storage character = _Character[_address];
character.Pow = 1;
character.limit = 300;
emit NewUser(_address, character.Pow, character.limit);
return true;
}
function IncreasePow(address _address) public isOwner(_address) {
require(goldBalanceOf(_address) >= PowFee);
goldBurn(_address, PowFee);
Characters storage character = _Character[_address];
uint32 number = uint32(getStatus());
character.Pow += number;
}
까다롭게 작성을 하지는 않았습니다.
단순히 캐릭터를 생성해 주게 되고 해당 캐릭터의 능력치를 조절할수 있는 함수를 몇가지 추가 하였습니다.
이 부분에서 좀더 랜덤적인 요소를 추가하는 것이 게임의 재미를 향상 시킬것 같아서
keccack256
을 활용 하였습니다.
function getRandomNumber() internal view returns (uint256) {
uint256 total = goldTotalSupply();
return uint256(keccak256(abi.encodePacked(msg.sender, total))) % 100;
}
function getStatus() public view returns (uint256) {
return getRandomNumber() % 10;
}
이후 저는 컨트랙트 작업이 끝났다고 생각을 하였고
이제 web3를 활용하여 서버에서 일정 시간마다 토큰을 전송해주는 부분을 작성 하였습니다.
node-schedule
를 활용하였습니다.node-schdule
를 활용하여 일정 시간마다 함수나 axios
가 작동하게 해주었습니다.
배치서버를 구현하여 하나의 서버에서 모든것을 처리하기보다는 다른 서버에서 일을 나눠서 작업하게 구현을 하였습니다.
const TokenSchema = mongoose.Schema({
check: {
type: Boolean,
default: false,
},
To: {
type: String,
default: "",
},
To_Array: {
type: [String],
default: [],
},
messageHash: String,
v: String,
r: String,
s: String,
rawTransaction: String,
transactionHash: String,
});
let tx = {
from: process.env.Server_Address,
to: process.env.Character_CA,
nonce: nonce,
gas: 50000,
data: method.mintAll(address, 10).encodeABI(),
};
await web3.eth.accounts
.signTransaction(tx, process.env.Server_PrivateKey)
.then(async (Tx) => {
const makeTokenDB = await new TokenDB({
To_Array: address,
messageHash: Tx.messageHash,
v: Tx.v,
r: Tx.r,
s: Tx.s,
rawTransaction: Tx.rawTransaction,
transactionHash: Tx.transactionHash,
});
makeTokenDB.save();
});
이런식으로 DB에 값을 저장한뒤에
const answer = await TokenDB.find({ check: false });
for (let i = 0; i < answer.length; i++) {
await web3.eth.sendSignedTransaction(
answer[i].rawTransaction,
(err, hash) => {
if (err) console.log(err);
else console.log(hash);
}
);
await TokenDB.findOneAndUpdate(
{ id: answer[i].id },
{
check: true,
},
{
new: true,
}
);
console.log("BlockChain토큰 지급 완료!");
}
이후 다른 시간대에 DB에 있는 값을 뺴와서 실제로 트랜잭션을 전송하게 해준뒤에
전송한 DB값을 갱신하에 후에 다시 가져오지 않게 수정합니다.
일단 오류 문구는 Returned error: nonce too low
라는 문구가 발생을 하였습니다.
DB에 값이 한개가 있고 이 부분을 처리하면 저런 오류가 발생을 하지 않지만
두개 이상이 되면 바로 오류가 발생을 하였습니다.
이 문제는 굉장히 간단한 문제였지만 처음 보는 에러여서 저는 많은 시간을 투자 해야 했습니다...
실제로 트랜잭션이 전송이 되지 않았기 떄문에 발생하는 오류 입니다.
만약 DB에 저장되는 트랜잭션이 2개라면 아직은 트랜잭션이 전송이 되지 않았기 때문에 같은 논스의 값을 가진 트랜잭션이 저장이 될 것 입니다.
그러면 먼저 저장된 값이 트랜잭션이 전송이 되면 그후에 오는 Nonce
값은 이전 값의 Nonce
값을 가지고 있기 떄문에
저런 오류가 발생을 하는 것 이였습니다.
그러기 떄문에 저는 트랜잭션을 DB에 저장을 할떄에 Nonce
값을 1씩 갱신시켜 줘야 합니다.
일단 기본적으로 web3에서 nonce
값을 가져오는 코드는 web3.eth.getTransactionCount(address)
입니다.
하지만 이 방법으로 갱신을 하는 것은 실제로 트랜잭션이 검증이 이루어 지지 않은 상태이기 떄문에
검증이 이루어 지기 전에는 계속해서 같은 값이 나오게 될 것입니다.
그러기 떄문에 임의적으로 갱신시킨 nonce값을 저장해 주어야 합니다.
export let nonce;
const getnonce = async () => {
const firstNonce = await web3.eth.getTransactionCount(
process.env.Server_Address
);
nonce = firstNonce;
console.log("account의 nonce값 = " + nonce);
};
export const plusnonce = async () => {
nonce++;
};
두가지 함수를 사용 하였습니다.
일단 서버가 시작될떄에 nonce
라는 변수를 갱신시켜 줍니다.
그후 DB에 값이 저장될떄마다 plusnonce
를 작동시킴으로써 임의적으로 증가된 nonce를 트랜잭션에 넣어주는 것 입니다.
그러면 처음 DB에 저장이 되는 nonce
는 1, 그후는 2, 3. 4 이런식으로 저장이 될 것입니다.
web3.eth.getTransactionCount(address)
값은 계속 고정이 된다는 점 입니다.간단한 문제였지만 생각보다 시간을 많이 잡아먹었습니다...ㅠㅠ
하지만 그래도 이제 솔리디티 작성 부분은 끝났습니다.
하지만 일단 기본적으로 모두 재료가 완료가 되었으니
이제 본격적으로 web3작업을 진행하고자 합니다!!
추가적으로 발견한 오류 또는 진행 사항을 작성해볼 것 입니다.
감사합니다!