컨트랙트에 저장할 수 있는 영역 데이터를 영구적으로 저장할 수 있다.
contract storage 데이터를 저장한다.
storage에 상태를 저장해서 유지시킬 수 있다. (블록체인 스마트 컨트랙트)
class Counter {
value: number;
constructor() {}
serValue() {}
getValue() {}
}
const _counter = new Counter();
const _counter2 = new Counter();
// SPDX-License-Identifier:MIT
// 라이센스 버전
//솔리디티 버전
pragma solidity ^0.8.0;
//컨트랙트 코드
contract Counter{
uint256 value;
constructor(){};
function setValue(uint256 _value) public{
value=_value;
}
function getValue() public view returns (uint256){
//상태변수를 변경하지 않고 조회하기 위해 view를 쓴다.
return value;
}
}
javascript의 클래스는 인스턴스를 생성을 하는 과정이 (new 키워드를 사용해서 생성)
new키워드를 통해 생성된 인스턴스들은 다른 메모리 주소를 참조하고 있기에 동일한 객체가 아니다.
solidity 에서의 컨트랙틑 컴파일된 코드의 내용이 EVM을 통해 실행되고 CA생성될때 solidity코드의 내용으로 인스턴스가 한 번 생성된다.
이후에 생성된 인스턴스를 CA로 참조해서 컨트랙트에 접근해서 사용하는 데이터는 같은 데이터를 참조하게 된다. (싱글톤 패턴의 방식)
싱글톤 팬턴의 방식:인스턴스 객체를 하나 생성해서 어디서든 생성한 인스턴스만 참조하는 디자인 패턴
스마트 컨트랙트 프로세스
간단한 카운터를 만들자.
스마트 컨트랙트의 코드가 실행될 때 EVM에서 연산을 얼마나 할지와 네트워크의 환경에 기준으로 수수료가스가 측정된다.
네트워크 상황과 코드의 복잡성에 따라서 연산(우리가 직접 연산을 하기는 어렵고 가스비 추정은 가능하다.)
상태변수의 값을 조회하는 함수는 연산을 하는 과정이 없기 때문에 가스비를 필요로 하지 않는다.
상태변수의 값을 변경하는 경우 연산이 포함되어 (한정된 네트워크 자원 사용) 연산에 따른 가스비를 지불해야한다.
연산을 하는 과정에서 코드의 무한루프를 연산하게 되면 과도한 가스비 발생을 방지하기 위해 gasLimit가 초과되면 트랜잭션이 블록에 담기지 않는다.
// SPDX-License-Identifier:MIT
pragma solidity ^0.8.13;
contract Counter {
uint256 value;
constructor() {}
function setValue(uint256 _value) public {
// 상태변수 변경
value = _value;
}
function getValue() public view returns (uint256) {
return value;
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/web3@1.10.0/dist/web3.min.js"></script>
</head>
<body>
<ul id="Accounts"></ul>
<div>
<label for="">use Account</label> <br />
<input type="text" id="useAccount" /> <br />
<label for="">use Contract</label> <br />
<textarea name="" id="contract" cols="30" rows="10"></textarea> <br />
<button id="sendTransactionBtn">컨트랙트 배포</button>
<div>카운트 앱</div>
<div id="counterValue"></div>
<button id="callBtn">조회</button>
<button id="sendBtn">증가</button>
<button id="decreBtn">감소</button>
</div>
</body>
<script>
// 네트워크 연결
// ganache === http://127.0.0.1:8545
const web3 = new Web3("http://127.0.0.1:8545");
// getAccount == 네트워크의 계정들 조회
web3.eth.getAccounts().then((data) => {
let items = "";
data.forEach(async (el) => {
// wei 단위로 계정의 잔액 조회
const balance = await web3.eth.getBalance(el);
// 단위 변경 ETH단위로 단위변경
const eth_balance = await web3.utils.fromWei(balance);
items += `<li>${el}:${eth_balance}ETH</li>`;
Accounts.innerHTML = items;
});
});
// 컨트랙트 배포
// 코드 배포
// npx solc --bin --abi 파일의 경류
// 컨트랙트를 배포할 때 수수료를 지불할 컨트랙트 배포자 계정
// bin 컴파일된 컨트랙트 코드 내용
// 트랜잭션 생성
sendTransactionBtn.onclick = () => {
console.log(contract.value);
console.log(useAccount.value);
web3.eth
.sendTransaction({
// 컨트랙트 배포자 계정
from: useAccount.value,
// gas제한량
gas: "3000000",
// 컴파일된 컨트랙트 바이트 코드
data: contract.value,
})
.then(console.log);
//컨트랙트 배포 후 트랜잭션 처리가 되면
// 응답으로 컨트랙트 주소를 주는데 CA
// 컨트랙트 참조에 사용하는 주소 CA
// "0x106d44a567a75bb9d14f7DA6155F315D9E4B7146"
};
// 배포한 컨트랙트 실행
// abi를 사용해서 컨트랙트 코드를 정의하고 실행
// interface
// 코드를 활성화 시켜서 사용할 때
// 정의한 구조대로 사용하기 위해서
const abi = [
// 생성자 함수
// inputs 매개변수 전달할 매개변수가 없으니까 []
// stateMutability === nonpayable 이더리움을 받지 않는 상태 전환 함수
// payable== 이더를 전달받을 수 있는 상태 변환 함수
// type=== constructor 생성자 함수의 타입
// outputs 함수의 출력 내용
// internalType 상태변수의 함수 값에 대한 타입
{ inputs: [], stateMutability: "nonpayable", type: "constructor" },
{
// stateMutability ===view
// 상태변화 없이 읽기 전용 이라는 상태
inputs: [],
name: "getValue",
// name : 사용하는 매개변수의 이름
outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
stateMutability: "view",
// type=== function 함수 형식 타입
type: "function",
},
{
// name : 사용하는 매개변수의 이름
inputs: [{ internalType: "uint256", name: "_value", type: "uint256" }],
name: "setValue",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
];
// 카운트의 값을 조회하는 함수
const getValue = async () => {
// encodeFunctionCall
// 첫번째 매개변수 :abi
// 두번째 매개변수: 함수에 쓰일 매개변수
// encodeFunctionCall 16문자열을 반환
// 컨트랙트 함수의 내용과 우리가 전달한 매개변수를 전달해서 해시코드로 변환
// EVM에서 실행을 시킨다.
const getCodeHash = await web3.eth.abi.encodeFunctionCall(abi[1], []);
console.log(getCodeHash);
// call 은 읽기 전용
// 원격 프로시저 호출 값을 조회
const data = await web3.eth.call({
// 가스비가 없기 때문에 from이 필요가 없다
to: "0x106d44a567a75bb9d14f7DA6155F315D9E4B7146",
data: getCodeHash,
});
// data에는 16진수로 변환된 값이 넘어오는데
const result = await web3.utils.toBN(data).toString(10);
console.log(result);
counterValue.innerHTML = result;
return parseInt(result);
};
callBtn.onclick = getValue;
// setValue 상태변수 변경
const setValue = async () => {
const _getValue = await getValue();
const setCodeHash = await web3.eth.abi.encodeFunctionCall(abi[2], [
_getValue + 1,
]);
console.log(setCodeHash);
if (!useAccount.value) {
return alert("계정을 입력해 주세요");
}
const tx = {
from: useAccount.value, //트랜잭션을 발생시키는 계정
to: "0x106d44a567a75bb9d14f7DA6155F315D9E4B7146", //CA 계정 주소
data: setCodeHash,
gas: 50000,
gasPrice: 20000000000,
};
const data = await web3.eth.sendTransaction(tx);
console.log(data);
getValue();
};
const setValueDecre = async () => {
const _getValue = await getValue();
const txData = await web3.eth.abi.encodeFunctionCall(abi[2], [
_getValue - 1,
]);
if (!useAccount.value) {
return alert("계정 입력해주세요");
}
const transactionInterface = {
from: useAccount.value,
to: "0x106d44a567a75bb9d14f7DA6155F315D9E4B7146",
data: txData,
gas: 500000,
gasPrice: 2000000000,
};
const data = await web3.eth.sendTransaction(transactionInterface);
getValue();
};
sendBtn.onclick = setValue;
decreBtn.onclick = setValueDecre;
</script>
</html>
아직 개념 단계고 복잡한 알고리즘이나 거래는 하지 않았지만 트랜잭션이 배포되는 과정이나 언제 생성이 되는지, 테스트 네트워크는 뭔지 다시 한 번 복습해야할 필요성을 느꼈다.