Skip to content

Instantly share code, notes, and snippets.

@sbin0819
Last active July 6, 2022 07:28
Show Gist options
  • Save sbin0819/2029990c459ed676d09af4a1df27788a to your computer and use it in GitHub Desktop.
Save sbin0819/2029990c459ed676d09af4a1df27788a to your computer and use it in GitHub Desktop.
# Chapter 1
Web3.js
## basic
### 컨트랙트
```solidity
pragma solidity ^0.4.19;
contract HelloWorld {
}
```
### 구조체
```solidity
struct Person {
uint age;
string name;
}
```
### 함수
```solidity
uint[] numbers;
function _addToArray(uint _number) private {
numbers.push(_number);
}
```
### 함수 제어자
- 상태를 변화시키지 않음 `view`
- 함수가 앱에서 어떤 데이터도 접근하지 않는 것 `pure`
### 이벤트
```solidity
event IntegersAdded(uint x, uint y, uint result);
function add(uint _x, uint _y) public {
uint result = _x + _y;
// 이벤트를 실행하여 앱에게 add 함수가 실행되었음을 알린다:
IntegersAdded(_x, _y, result);
return result;
}
```
```js
YourContract.IntegersAdded(function(error, result) {
// 결과와 관련된 행동을 취한다
})
```
# Chapter 2
## 매핑과 주소
`mapping`, `adress`
- 주소
이더리움 블록체인의 주소
- 매핑
데이터를 저장하는 또다른 방법
```solidity
// 금융 앱용으로, 유저의 계좌 잔액을 보유하는 uint를 저장한다:
mapping (address => uint) public accountBalance;
// 혹은 userID로 유저 이름을 저장/검색하는 데 매핑을 쓸 수도 있다
mapping (uint => string) userIdToName;
```
매핑은 기본적으로 키-값 (key-value) 저장소로, 데이터를 저장하고 검색하는 데 이용된다. 첫번째 예시에서 키는 address이고 값은 uint이다. 두번째 예시에서 키는 uint이고 값은 string이다.
## msg.sender
솔리디티에는 모든 함수에서 이용 가능한 특정 전역 변수들이 있지. 그 중의 하나가 현재 함수를 호출한 사람 (혹은 스마트 컨트랙트)의 주소를 가리키는 msg.sender이지.
참고: 솔리디티에서 함수 실행은 항상 외부 호출자가 시작하네. 컨트랙트는 누군가가 컨트랙트의 함수를 호출할 때까지 블록체인 상에서 아무 것도 안 하고 있을 것이네. 그러니 항상 msg.sender가 있어야 하네.
```solidity
mapping (address => uint) favoriteNumber;
function setMyNumber(uint _myNumber) public {
// `msg.sender`에 대해 `_myNumber`가 저장되도록 `favoriteNumber` 매핑을 업데이트한다 `
favoriteNumber[msg.sender] = _myNumber;
// ^ 데이터를 저장하는 구문은 배열로 데이터를 저장할 떄와 동일하다
}
function whatIsMyNumber() public view returns (uint) {
// sender의 주소에 저장된 값을 불러온다
// sender가 `setMyNumber`을 아직 호출하지 않았다면 반환값은 `0`이 될 것이다
return favoriteNumber[msg.sender];
}
```
이 간단한 예시에서 누구나 setMyNumber을 호출하여 본인의 주소와 연결된 우리 컨트랙트 내에 uint를 저장할 수 있지.
msg.sender를 활용하면 자네는 이더리움 블록체인의 보안성을 이용할 수 있게 되지. 즉, 누군가 다른 사람의 데이터를 변경하려면 해당 이더리움 주소와 관련된 개인키를 훔치는 것 밖에는 다른 방법이 없다는 것이네.
## require
```solidity
function sayHiToVitalik(string _name) public returns (string) {
// _name이 "Vitalik"인지 비교한다. 참이 아닐 경우 에러 메시지를 발생하고 함수를 벗어난다
// (참고: 솔리디티는 고유의 스트링 비교 기능을 가지고 있지 않기 때문에
// 스트링의 keccak256 해시값을 비교하여 스트링 값이 같은지 판단한다)
require(keccak256(_name) == keccak256("Vitalik"));
// 참이면 함수 실행을 진행한다:
return "Hi!";
}
```
## 상속
`is`
```solidity
contract Doge {
function catchphrase() public returns (string) {
return "So Wow CryptoDoge";
}
}
contract BabyDoge is Doge {
function anotherCatchphrase() public returns (string) {
return "Such Moon BabyDoge";
}
}
```
## import
## storage vs memory
Storage는 블록체인 상에 영구적으로 저장되는 변수를 의미하지. Memory는 임시적으로 저장되는 변수로, 컨트랙트 함수에 대한 외부 호출들이 일어나는 사이에 지워지지. 두 변수는 각각 컴퓨터 하드 디스크와 RAM과 같지.
`상태 변수(함수 외부에 선언된 변수)`는 초기 설정상 `storage`로 선언되어 블록체인에 영구적으로 저장되는 반면, `함수 내에 선언된 변수`는 `memory`로 자동 선언되어서 함수 호출이 종료되면 사라지지.
*하지만 이 키워드들을 사용해야 하는 때가 있지. 바로 함수 내의 구조체와 _배열_을 처리할 때지:*
```solidity
contract SandwichFactory {
struct Sandwich {
string name;
string status;
}
Sandwich[] sandwiches;
function eatSandwich(uint _index) public {
// Sandwich mySandwich = sandwiches[_index];
// ^ 꽤 간단해 보이나, 솔리디티는 여기서
// `storage`나 `memory`를 명시적으로 선언해야 한다는 경고 메시지를 발생한다.
// 그러므로 `storage` 키워드를 활용하여 다음과 같이 선언해야 한다:
Sandwich storage mySandwich = sandwiches[_index];
// ...이 경우, `mySandwich`는 저장된 `sandwiches[_index]`를 가리키는 포인터이다.
// 그리고
mySandwich.status = "Eaten!";
// ...이 코드는 블록체인 상에서 `sandwiches[_index]`을 영구적으로 변경한다.
// 단순히 복사를 하고자 한다면 `memory`를 이용하면 된다:
Sandwich memory anotherSandwich = sandwiches[_index + 1];
// ...이 경우, `anotherSandwich`는 단순히 메모리에 데이터를 복사하는 것이 된다.
// 그리고
anotherSandwich.status = "Eaten!";
// ...이 코드는 임시 변수인 `anotherSandwich`를 변경하는 것으로
// `sandwiches[_index + 1]`에는 아무런 영향을 끼치지 않는다. 그러나 다음과 같이 코드를 작성할 수 있다:
sandwiches[_index + 1] = anotherSandwich;
// ...이는 임시 변경한 내용을 블록체인 저장소에 저장하고자 하는 경우이다.
}
}
```
## 함수 접근 제어자 더 알아보기
- internal
- external
```solidity
contract Sandwich {
uint private sandwichesEaten = 0;
function eat() internal {
sandwichesEaten++;
}
}
contract BLT is Sandwich {
uint private baconSandwichesEaten = 0;
function eatWithBacon() public returns (string) {
baconSandwichesEaten++;
// eat 함수가 internal로 선언되었기 때문에 여기서 호출이 가능하다
eat();
}
}
```
## 인터페이스
```solidity
contract LuckyNumber {
mapping(address => uint) numbers;
function setNum(uint _num) public {
numbers[msg.sender] = _num;
}
function getNum(address _myAddress) public view returns (uint) {
return numbers[_myAddress];
}
}
```
```solidity
contract NumberInterface {
function getNum(address _myAddress) public view returns (uint);
}
```
## 인터페이스 활용
```solidity
contract MyContract {
address NumberInterfaceAddress = 0xab38...
// ^ 이더리움상의 FavoriteNumber 컨트랙트 주소이다
NumberInterface numberContract = NumberInterface(NumberInterfaceAddress)
// 이제 `numberContract`는 다른 컨트랙트를 가리키고 있다.
function someFunction() public {
// 이제 `numberContract`가 가리키고 있는 컨트랙트에서 `getNum` 함수를 호출할 수 있다:
uint num = numberContract.getNum(msg.sender);
// ...그리고 여기서 `num`으로 무언가를 할 수 있다
}
}
```
## 다수의 반환값 처리하기
```solidity
function multipleReturns() internal returns(uint a, uint b, uint c) {
return (1, 2, 3);
}
function processMultipleReturns() external {
uint a;
uint b;
uint c;
// 다음과 같이 다수 값을 할당한다:
(a, b, c) = multipleReturns();
}
// 혹은 단 하나의 값에만 관심이 있을 경우:
function getLastReturnValue() external {
uint c;
// 다른 필드는 빈칸으로 놓기만 하면 된다:
(,,c) = multipleReturns();
}
```
```solidity
contract ZombieFeeding is ZombieFactory {
address ckAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
KittyInterface kittyContract = KittyInterface(ckAddress);
// 여기에 있는 함수 정의를 변경:
function feedAndMultiply(uint _zombieId, uint _targetDna) public {
require(msg.sender == zombieToOwner[_zombieId]);
Zombie storage myZombie = zombies[_zombieId];
_targetDna = _targetDna % dnaModulus;
uint newDna = (myZombie.dna + _targetDna) / 2;
// 여기에 if 문 추가
_createZombie("NoName", newDna);
}
function feedOnKitty(uint _zombieId, uint _kittyId) public {
uint kittyDna;
(,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);
// 여기에 있는 함수 호출을 변경:
feedAndMultiply(_zombieId, kittyDna);
}
}
```
## if 문
자바스크립트 if 문과 동일
```solidity
function eatBLT(string sandwich) public {
// 스트링 간의 동일 여부를 판단하기 위해 keccak256 해시 함수를 이용해야 한다는 것을 기억하자
if (keccak256(sandwich) == keccak256("BLT")) {
eat();
}
}
```
# 솔리디티 고급개념
## 가스(Gas)
### 가스 - 이더리움 DApp이 사용하는 연료
함수를 실행하는 데에 얼마나 많은 가스가 필요한지는 그 함수의 로직(논리 구조)이 얼마나 복잡한지에 따라 달라지네. 각각의 연산은 소모되는 가스 비용(gas cost)이 있고, 그 연산을 수행하는 데에 소모되는 컴퓨팅 자원의 양이 이 비용을 결정하네. 예를 들어, storage에 값을 쓰는 것은 두 개의 정수를 더하는 것보다 훨씬 비용이 높네. 자네 함수의 전체 가스 비용은 그 함수를 구성하는 개별 연산들의 가스 비용을 모두 합친 것과 같네.
### 가스는 왜 필요한가?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment