3가지의 변수가 있음.
1) local
2) state
3) global
블록체인과 관련한 정보를 제공한다.
ex) msg.sender, block.timestamp 등
추가 구글링.
contract Variables{
function doSomething() public {
uint i = 456;
//local 변수는 블록체인에 저장되지 않음
uint timestamp = block.timestamp ;
//global 변수는 이미 저장된것을 말하는 것 같아.
}
state 변수를 선언하거나 수정하려면, transaction을 전송해야 한다.(fee 사용)
하지만, 읽거나 불러오는(return) 건 transaction이 필요 없다.
trasaction은 ether 라는 비용을 지불한다.
1 ether == 10**18 wei
1) Gas
ether 비용
gas spent (쓴 가스) * gas price (가스 가격- 직접 설정, 높게 설정할수록 채굴자들이 블록에 먼저 넣으려고 할 것임.)
2) Gas limit
gas limit 이란,
"내가 설정하는 내 트랜잭션에서 사용할 최대 개스이다. "
block gas limit 이란,
"네트워크에서 정한, 블록에 허용된 최대 개스이다.
문법은 자바스크립트와 같다.
function IfElse ( uint x) public pure returns (uint) { if(x<10) { return 0; // x가 10보다 작으면 0을 반환 } else if (x>=10){ return 1; } // x가 10보다 크거나 같으면 1을 반환
while 과 do while 은 잘 안쓰임. gas limit을 넘어 트랜잭션이 중단될 수 있기 때문.
1) for 문
문법 :
for ( 초기 상태, 조건, 조건확인후 초기상태를 변화시킬 함수){ 반복할 함수 }
for (uint i =0; i <10; i++){
if(i==5){
break;
}
}
2) while 문
문법 :
while (조건){ 반복할 함수; }
Do while 과 차이 구글링
do ~ while 은 선실행 후평가문. 한번은 무조건 실행.
문법 :
mapping( key값 => value값) mapping 이름
key 값은로는 uint, address bytes 등이 올수 있고,
value 값으로는 아무거나 와도 된다.
참고로 이중으로 사용이 된다.
ex)
mapping (address => mapping (uint => bool)) public nested;
python에서 list과 같은 개념.
1) 선언방법
uint [] public array1;
혹은
uint [] public array2 =[ 1,2,3];
2) 특징
전체 array를 반환할 수 있음. 단, 길이가 정해진 array일 경우
3) 추가하기
array1.push(~);
4) pop 시키기
마지막 요소를 빼내고, array 길이가 1 준다.
array1.pop(~);
5) 길이 구하기
array1.length;
6) 삭제하기
방법 1 : delete array [삭제하려는 index];
방법 2: pop을 이용한 함수 만들기
uint[] public array1 = [1,2,3,4];
function remove(uint index) public {
array1[index] = array1[array1.length -1];
//삭제하려는 항목의 인덱스와 마지막 항목의 인덱스를 바꿈
array.pop();
}
remove (1);
//
[1,4,3]
작동과정
[1,2,3,4] ->index change-> [1,4,3,2]->pop! -> [1,4,3]
contract 밖에서도 선언될 수 있다.
열거할 때 사용.
1) 문법
enum 이름 {
element1,
element2,
element3
}
// SPDX-License-Identifier: MIT pragma solidity ^0.8.3; contract Enum { // Enum representing shipping status enum Status~~이름이지?~~ { Pending, Shipped, Accepted, Rejected, Canceled } // Default value is the first element listed in // definition of the type, in this case "Pending" Status public status; // Returns uint // Pending - 0 // Shipped - 1 // Accepted - 2 // Rejected - 3 // Canceled - 4 function get() public view returns (Status) { return status~~몬가 솔리디티 내부 변수인듯~~; } // Update status by passing uint into input function set(Status _status) public { status = _status; } // You can update to a specific enum like this function cancel() public { status = Status.Canceled; ~~이해안됨~~ } // delete resets the enum to its first value, 0 function reset() public { delete status; ~~이해됨 ~~ } }
struct을 만듦으로써 새로운 나만의 type를 만들 수 있다.
contract 밖에서 선언될 수 있고 다른 컨트랙트에서 import될 수 있다.
1) 문법
struct 이름 {
//예를들어
string text;
bool completed;
}
2) struct를 initialize(?)하는 3가지 방법
Todo struct의 array 형성
Todo[] public todos;
1. 함수처럼
todos.push(Todo(newText,false));
2. key- value 매핑
todos.push(Todo({text:newText, completed: false}));
3. 통째로
Todo memory todo;
todo.text = newText;
todo.completed = false;
todos.push(todo);
변수들은 storage, memory, 혹은 call data의 위치에 저장된다.
pragma solidity ^0.8.3;
contract DataLocations {
uint[] public arr; //**arr 라는 array 선언**
mapping(uint=>address)map; // **map이라는 uint를 address값 반환하는 mapping 선언**
struct MyStruct {
uint foo;
} // ** MyStruct 라는 struct는 foo 라는 uint를 element로 가 진다.**
mapping(uint => MyStruct) myStructs; // ** myStructs 라는
uint를 앞서 정의한 MyStruct로
반환하는 mapping 선언 **
function f() public {
_f(arr, map, myStructs[1]); //** 새로운 자료형 선언? 모지**
MyStruct storage myStruct = myStructs[1];
// myStruct라는 이름을 가진 MyStruct의 struct 타입을 myStruccts라는 매핑에 uint값 1을 넣어 대응시킨 것.
MyStruct memory myMemStruct = MyStruct(0);
}
//**MyStruct는 인수가 없는디?**
function _f(
uint[] storage _arr,
mapping(uint => address) storage _map,
MyStruct storage _myStruct
) internal {
// do something with storage variables
}
// You can return memory variables
function g(uint[] memory _arr) public returns (uint[] memory) {
// do something with memory array
}
function h(uint[] calldata _arr) external {
// do something with calldata array
}
}
function 이름 () public pure returns (uint){
함수 내용 ex) return (1)
}
function named() public pure returns (
uint x, bool b, uint y)
{
return(1, false, 2);
}
각 이름에 할당 될 수 도 있다.
function named() public pure returns (
uint x, bool b, uint y)
{
x =1;
b= false;
y=2;
}
다른 함수로 지정할 수 있다.
view 함수는 state 변화가 없을 것이라는 걸 의미
pure 함수는 어떤 state 변수를 읽거나 변경하지 않을 것이라는 걸 의미
contract ViewAndPure {
uint public x = 1; // state 변수 선언
// Promise not to modify the state.
function addToX(uint y) public view returns (uint) {
return x + y;
}
// x의 값을 변화시키지 못함. x에다 y를 더한 값을 출력할 뿐.
// Promise not to modify or read from the state.
function add(uint i, uint j) public pure returns (uint) {
return i + j;
//x를 불러오지도, 변화시키지도 않음
}
}```
에러는 트랜잭션 동안 이루어진 변화를 다시 되돌린다.
조건을 만족하지 않으면 실행하지 않음.
문법
if(조건){ revert("이유") };
confirm할 때 사용.
uint public balance;
uint public constant MAX_UINT = 2**256 - 1;
function deposit(uint _amount) public {
uint oldBalance = balance;
uint newBalance = balance + _amount;
// balance + _amount does not overflow if balance + _amount >= balance
require(newBalance >= oldBalance, "Overflow");
balance = newBalance;
assert(balance >= oldBalance);
함수 호출 이전, 이후에 실행된다.
1) 제어 접근
2) input값 입증
3) 재진입 해킹으로부터 보호
선언 방법
modifier 이름 () {
내용
_;
}
contract 만들면서 실행되는 선택적 함수이다.
배포될 때 딱 한번밖에 실행이 안되는 함수.
ex) 컨트랙트의 주인을 선언할때, 혹은 기본적으로 가지고 있어야 하는 정보를 설정할 ㄸ
is를 사용하여 상속.
자녀 컨트랙트에 의해 override 된 함수 :virtual
부모 함수에 의해 override된 함수: override.
pragma solidity ^0.8.3;
/* Graph of inheritance
A
/ \
B C
/ \ /
F D,E
*/
contract A {
function foo() public pure virtual returns (string memory) {
return "A";
}
}
// Contracts inherit other contracts by using the keyword 'is'.
contract B is A {
// Override A.foo()
function foo() public pure virtual override returns (string memory) {
return "B";
}
}
!!!! A.foo는 B를 반환?
contract C is A {
// Override A.foo()
function foo() public pure virtual override returns (string memory) {
return "C";
}
}
!!!! A.foo는 C를 반환?
// Contracts can inherit from multiple parent contracts.
// When a function is called that is defined multiple times in
// different contracts, parent contracts are searched from
// right to left, and in depth-first manner.
contract D is B, C {
// D.foo() returns "C"
// since C is the right most parent contract with function foo()
function foo() public pure override(B, C) returns (string memory) {
return super.foo();
}
}
!!!! 2개를 상속, 오른쪽 노드인 C를 상속, C를 반환
contract E is C, B {
// E.foo() returns "B"
// since B is the right most parent contract with function foo()
function foo() public pure override(C, B) returns (string memory) {
return super.foo();
}
}'
!!!! 오른쪽 노드인 B를 상속, C 를 반환
// Inheritance must be ordered from “most base-like” to “most derived”.
// Swapping the order of A and B will throw a compilation error.
contract F is A, B {
function foo() public pure override(A, B) returns (string memory) {
return super.foo();
}
}
순서: 가장 기초적인 것 부터 파생된 것을 호출.
다른 컨트랙트와 상호작용 상태 설정 가능
1) public- 모든 account, contract
2) private - 함수를 정의한 컨트랙트 안에서만- 상속한 contract에서는 호출 불가능.
-> state 변수는 external 로 불리지 못함.
다른 컨트랙트와 상호작용 가능한 새로운 자료형
interface
다른 인터페이스로부터 상속 가능
interface에서 정의된 함수는 external이어야 함
constructor, state 변수를 선언할 수 없음
function 이 있는 construct 혹은 객체로 이해.
payable을 선언한 함수나 address는 그들의 컨트랙트로 ether을 받을 수 있다.
보내기
1) transfer
2) send-> true나 false를 반환
3) call -> true나 false를 반환
받기
1) receive() external payable - > msg.data 에 아무것도 없을 때 사용
2) fallback() external payable-> msg.data에 무언가 있을 때.
어떠한 인수도 받지 않고, 아무것도 반환하지 않는 함수.
존재하지 않는 함수가 시행되었을 때나,
이더가 보내졌는데 그 컨트랙트에 receive()나 msg.data가 비었을 때.
fallback함수를 통해 이더를 보낼때 사용.
Delegatecall :
A가 delegatcall을 B로 시행하면, A의 storage, msg.sender, msg.value를 가지고 B의 코드가 실행된다.
ragma solidity ^0.8.3;
// NOTE: Deploy this contract first
contract B {
// NOTE: storage layout must be the same as contract A
uint public num;
address public sender;
uint public value;
function setVars(uint _num) public payable {
num = _num;
sender = msg.sender;
value = msg.value;
}
}
contract A {
uint public num;
address public sender;
uint public value;
function setVars(address _contract, uint _num) public payable {
// A's storage is set, B is not modified.
(bool success, bytes memory data) = _contract.delegatecall(
abi.encodeWithSignature("setVars(uint256)", _num)
);
}
}
abi...?
1) A.함수(인수,인수,인수) 로 호출
2) call 사용 - 권장되지 않음.
"new"키워드로 다른 컨트랙트를 만들 수 있다.
ex)
pragma solidity ^0.8.3;
contract Car {
address public owner;
string public model;
address public carAddr;
constructor(address _owner, string memory _model) payable {
owner = _owner;
model = _model;
carAddr = address(this);
}
}
contract CarFactory {
Car[] public cars;
function create(address _owner, string memory _model) public {
Car car ***what does this means?*** = new Car(_owner, _model);
cars.push(car);
}
***만약 새로운 contract라면, Car 이라는 array 가 이미 있잫아. ***
function createAndSendEther(address _owner, string memory _model) public payable {
Car car***what does it means?*** = (new Car){value: msg.value}(_owner, _model);
cars.push(car);
}
function getCar(uint _index)
public
view
returns (
address owner,
string memory model,
address carAddr,
uint balance
)
{
Car car = cars[_index];
return (car.owner(), car.model(), car.carAddr(), address(car).balance);
}
}
# Import
Local 파일 import
문법
import "./파일.sol";
깃허브에서 가져올 수도 있다.
import "urllink.sol";
# Library
컨트랙트와 비슷하지만, state변수 선언이 불가능하고, 이더를 보낼 수 없다.
library 내 함수들이 internal이면 contract에 내장할 수 있다. 그렇지 않다
그렇지 않다면 ㄴ먼저 시행되고 링크되어야 한다.
글로벌 함수같은 건가?
사용법
using 라이브러리명 for 자료형;