bool public isOpen;
constructor() {
isOpen = true;
}
해당코드는 컨트랙트 배포에 따라 즉각적으로 state value의 값을 설정한다.
Solidity contracts의 생성자는 객체 지향 언어의 클래스에서 생성자와 상당히 유사하다. 생성자 함수는 계약 구축 중에 한 번만 호출되고 다시는 호출되지 않는다. 일반적으로 초기 컨트랙트 값을 설정하는 데 사용된다.
먼저 자바스크립트에서의 variable shadowing은 어떨까?
let a = "Hello World";
if(true) {
let a = "Hello World 2";
console.log(a); // Hello World 2
}
이것은 바깥쪽 a가 안쪽 a 변수에 의해 그늘져 있는 경우이다. console.log 라인에서 우리는 외부 변수에 접근할 수 없다.
자바스크립트에서 섀도잉은 그렇게 자주 등장하지 않다.
class Food {
constructor() {
this.name = "pizza";
}
changeName(name) {
// not shadowed!
this.name = name;
}
}
JavaScript에서 이 키워드를 사용하여 클래스 내의 멤버 변수를 참조한다. 같은 파라미터명을 사용해도 멤버 변수 앞글자를 써야 하기 때문에 섀도우가 생기지 않는다.
마찬가지로 솔리디티는 어떨까?
string public name;
constructor(string name) {
// name is shadowed!
}
여기서도 또한 외부 name 변수가 생성자 파라미터 name으로 인해 shadowed 된다.
contract MyContract {
string public name;
constructor(string name) {
MyContract.name = name;
}
}
따라서 이를 해결하려면 MyContract에 대한 참조를 사용하여 상태 변수를 업데이트해야한다.
일반적으로 탐색 해제 매개 변수(undescore parameter )(즉, _variableName)가 다른 매개 변수보다 낫다.
따라서
bool public isOpen;
constructor(bool _isOpen) {
isOpen = _isOpen;
}
매개 변수 이름(ex. _isOpen) 앞에 밑줄을 사용하는건, 변수가 상태 변수와 동일한 이름을 갖지 못하게 한다.
Solidity에서는 이것을 사용하지 않고 상태 변수를 참조할 수 있기 때문에 이러한 현상이 자주 발생할 수 있다.
계약은 생성자 외에도 트랜잭션이나 쿼리로 호출될 수 있는 다른 함수들을 정의할 수 있다.
여기서 트랜잭셕과 쿼리의 차이는 무엇일까?
트랜잭션은 외부 소유 계정에서 발생한다. 트랜잭션의 목적은 블록체인의 일부 상태를 수정하는 것이다. 거래는 가스 사용량에 따라 비용이 발생하는데, 물론 계산과 저장 사용량에 따라 다르다.
우리는 서명된 트랜잭션을 만들고 있다. 공개 주소에 해당하는 디지털 서명의 v, r, s 구성 요소를 포함했기 때문에 서명되었다.
모든 이더리움 트랜잭션에 포함되는 또 다른 중요한 속성은 데이터이다.
우리가 한 트랜잭션을 위해 단순히 한 계정에서 다른 계정으로 ethers를 보내고 있었다. 우린 사실 EVM과 어떤 식으로도 교류하지 않고 있으며, 이러한 이유로 데이터는 비워두었다.
EVM과 상호 작용하는 트랜잭션의 경우 특정 함수를 대상으로 데이터를 채우고 해당 함수에 인수를 전달한다.
트잰잭션의 중요한 장점은 디지털 서명, 블록체인 상태 수정 및 비용 가스이다.
반면에 쿼리는 상태를 수정하지 않으며 쿼리에 응답하는 단일 노드에 의해서만 실행되므로 가스 비용이 발생하지 않는다.
쿼리에 응답하기 위해 노드에 일부 인증이 필요할 수 있다. Infura 서버를 사용하는 경우 계획에 따라 쿼리 수(트랜잭션뿐만 아니라)를 제한한다.
함수가 Smart Contract 자체 내에서 상태를 수정할지 여부를 명시적으로 정의할 수 있다. 함수가 솔리디티에 의해 view 또는 pure 함수로 표시되면 상태를 수정할 수 없다.
함수의 구조는 자바스크립트와 유사하다.
function myFunction() external {
// do something!
}
pragma solidity ^0.8.4;
contract Contract {
uint public x;
constructor(uint _x){
x=_x;
}
function increment() external{
x=x+1;
}
}
반환 값이 유용한 두 가지 경우가 있는데, 쿼리 및 컨트랙트 계정 간 통신이다.
쿼리는 eth_call로 JSON-RPC를 통해 함수를 직접 호출하여 스마트 계약 상태에 대한 직접적인 응답을 얻을 수 있다.
컨트랙트 통신을 위해서는 return value가 매우 중요할 수 있다. 예를 들어, 성공을 나타내는 bool 값을 반환해야 할 수 있다. 또 다른 예로, 컨트랙트는 분산형 거래에서 가장 최신의 가격 정보를 알아야 할 수도 있다.
// SPDX-License-Identifier: MIT
pragma solidity 0.7.5;
contract Contract {
bool _isRunning = true;
function isRunning() external view returns(bool) {
// return the state variable
return _isRunning;
}
}
isRunning() 함수는 함수 signature인 returns(bool)을 통해 bool 타입을 반환하는 것을 나타낸다.
이후, return 키워드를 사용하여 이 함수 내에서 부울 값을 반환할 수 있다.
반환 선언은 컴파일러가 우리 코드에서 컴파일 타임 오류를 검사하는 것을 도울 뿐만 아니라, ABI를 생성하는 방법도 제공한다. 이는 외부 프로그램이 Seagate의 Solidity 계약과 통신하는 데 도움이 됩니다!이는 외부 프로그램이 Solidity 계약과 통신하는 데 도움이 됩니다!
view 키워드를 isRunning 함수 시그니처에 추가하면 상태 변수가 수정되지 않는다.view 함수는 컨트랙트 상태를 읽을 수 있지만 수정할 수는 없다.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
contract Contract {
uint public x;
constructor(uint _x) {
x = _x;
}
function increment() external {
x++;
}
function add(uint y) external view returns(uint) {
return x + y;
}
}
트랜잭션이 이더리움 채굴자에게 브로드캐스트될 때, 채굴자들은 해당 트랜잭션을 로컬 메모리 풀에 추가한다. 새 트랜잭션 블록을 만들 때 이 메모리 풀에서 선택할 수 있다. 그들은 일반적으로 거래의 gasPrice에 근거하여 그렇게 할 것이다. 채굴자는 거래 수수료로 보상을 받기 때문에, gasPrice가 높을수록, 그들은 거래를 블록에 포함시켜야 하는 큰 동기가 생긴다.
이 모든 것은 트랜잭션이 예측할 수 없는 시간에 비동기적으로 발생한다는 것을 의미한다. 반대로 반환 값은 EVM 계산의 임시 결과이며 블록체인에 저장되지 않는다. 트랜잭션이 발생한 후 값을 조회할 경우 Solidity 이벤트를 통해 EVM에 로깅 메커니즘이 있다.
때때로 상태를 읽거나 쓰지 않는 솔리디티 기능이 필요하다. 이 기능들은 pure로 분류될 수 있다.
두 개의 uint 값을 합친다고 가정하자.
function double(uint x, uint y) external pure returns(uint) {
return x + y;
}
이 함수는 읽기/쓰기 상태 없이 단순 연산을 수행하므로 pure라고 말할 수 있다.
만약 pure 함수에서 상태를 수정하려고 시도한다면 컴파일러는 "Function declared as pure, but this expression (potentially) modifies the state".라는 오류를 발생시킨다.
또다른 예시로
function double(uint x, uint y) external pure returns(uint sum) {
sum = x + y;
}
return 키워드에서 반환된 파라미터 sum을 지정한다. 그런 다음 x + y를 함수 안에서 할당했한다. sum의 값은 내재하여 반환된다.
Solidity에서 두 함수가 서로 다른 경우 동일한 이름으로 선언하는 것도 가능하다.
function add(uint x, uint y) external pure returns(uint) {
return x + y;
}
function add(uint x, uint y, uint z) external pure returns(uint) {
return x + y + z;
}
Solidity는 제공된 인수와 일치하는 함수 signatures을 호출한다. 예를 들어, add(2,4)는 첫 번째 함수를 호출하고 add(2,3,4)는 두 번째 함수를 호출한다.
또한 Solidity는 함수에서 여러 값을 반환할 수 있다.
function addTwo(uint x, uint y) external pure returns(uint, uint) {
return (x + 2, y + 2);
}
return 키워드는 두 개의 반환 값을 지정한다. 또한 여러 개의 값을 반환하기 위해 괄호로 묶는 튜플을 사용한다.
튜플은 솔리디티에서 공식적인 유형이 아니다. 값을 반환하거나 할당값을 수정하기 위한 임시 구조로 사용할 수 있는 값의 목록이다. 튜플의 데이터 유형은 혼합될 수 있다.
또한 할당값을 바꾸기위해 튜플을 사용할 수 있다.
위에서 정의한 addTwo 함수를 호출하면 다음과 같다.
(uint x, uint y) = addTwo(4, 8);
console.log(x); // 6
console.log(y); // 10
Overload Double사용한 솔리디티 컨트랙트
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
contract Contract {
function double(uint x,uint y) external pure returns(uint a, uint b) {
a= x*2;
b= y*2;
}
}