온라인 및 모바일 서비스상의 결제, 보상 및 거래에 사용되는 암호화폐 플랫폼”을 제공하는 것을 목표로 합니다.
미콘캐시는 플랫폼에 연동된 서비스에서 얻은 보상을 자산의 가치로 만들어 드립니다. 사용자는 서비스가 종료되더라도 미콘캐시로 새로운 서비스에 대한 요구를 충족할 수 있으며, 개인간 거래를 할 수 있으며, 실물경제에서 사용할 수 있는 시스템으로 순환됩니다
-> 아하 얘네도 자기네 플랫폼을 살리고 그 외로도 쓰일 코인을 만든거구만, 그게 미콘캐시
미콘캐시(MCH)는 온라인 및 모바일 서비스 상의 결제, 보상 거래에 사용되는 자체 암호화폐 플랫폼이다. 전국 1만3700여 곳의 ATM기를 통해 출금이 가능하다는 점이 특징이다.
미콘캐시의 간편 결제 서비스 'M.PAY'는 비트코인, 이더리움, 트론, 비트코인캐시, 라이트코인 등 주요 코인을 시세에 맞는 M.PAY로 전환해서 현금처럼 사용할 수 있도록 지원한다.
Q. 온라인 결제와 관련된 암호화폐가 다양하게 있다. 미콘캐시만의 특징은? A. 미콘캐시는 앱 지갑 내의 M.PAY 결제 서비스를 통해 온라인 결제 및 보상이 이루어진다. 미콘캐시뿐만 아니라 다양한 가상자산들이 M.PAY로 전환되어 사용될 수 있다. M.PAY는 MCH와 1:1로 교환되며 사용시 시세를 반영하여 결제가 이루어진다. 그밖에 암호화폐는 M.PAY 전환시 해당 암호화폐와 MCH 시세를 반영하여 전환된다.
//기존
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
//ERC721.sol가 없고 이제 ERC721URIStorage.sol이거 씀
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
근데 나는 여전히 계속 빨간줄 뜨더라고 그래서
../node_modules/
로 경로 찾아주니까 빨간줄 사라짐
2. DeclarationError
사실 무엇보다
변수들을 못찾았는데
DeclarationError: Undeclared identifier. Did you mean "hash"? --> project:/contracts/UniqueAsset.sol:24:11: | 24 | require(hashes[hash] != 1); | ^^^^^^
,DeclarationError: Undeclared identifier. Did you mean "hash"? --> project:/contracts/UniqueAsset.sol:25:3: | 25 | hashes[hash] = 1; | ^^^^^^
그이유는!!!!
괄호를 잘못 묶었더라고..ㅋㅋㅋㅋ...
contract 안에 함수 있는건데
contract랑 함수를 따로 해서 contract에 담아둔 애들이름 못 쓰는 거였다
결과 컴파일 잘된다!
3. 경고 Visibility for constructor is ignored.
경고 뜨는 건
Warning: Visibility for constructor is ignored. If you want the contract to be non-deployable, making it "abstract" is sufficient.
(경고: 생성자의 가시성은 무시됩니다. 계약을 배포할 수 없도록 하려면"추상"으로 만드는 것으로 충분합니다.) --> project:/contracts/UniqueAsset.sol:16:4: | 16 | constructor() public ERC721("UniqueAsset", "UNA") {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
생성자에 더이상 가시성(public/external)이 필요하지 않다고 한다.
왜냐면 생성자는 처음에 배포됐을 때만 읽히니까 다음에 또 불려질 일이 없다.
public을 지웠더니 경고가 사라졌다.
추가적으로는
솔리디티 버전을 코드랑 잘 맞춰줘야하는 것. 필요할 때 f1눌러서 버전수정했다.
따라한 내용 정리
필요한 것
vscode(nodejs, 솔리디티확장자)
IPFS 설치
Ganache — 이더리움의 로컬 블록체인 — 설치
트러플 설치
NodeJS 설치
피나타 API 키
스마트 계약 작성
(오픈제플린에서 가져와서 쓸거임)
새프로젝트 폴더 만듦
mkdir mySpecialAsset
디랙토리 초기화
npm init -y
Truffle을 활용해 스마트 계약 프로젝트 초기화
truffle init
Open Zeppelin 계약에 액세스할거라서 설치
npm install @openzeppelin/contracts
contracts 폴더 구조 안에 파일 생성! (솔리디티 버전 맞춰서 하기)
UniqueAsset.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;
import"../node_modules/@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import"../node_modules/@openzeppelin/contracts/utils/Counters.sol";
contract UniqueAsset is ERC721URIStorage{
//Counters to help us increment the identifiers for the tokens we mint.// 카운터 라이브러리(갯수세는역할함)를 쓴다. 이 유형으로
using Counters for Counters.Counter;
//변수만듦to keep track of all of the tokens we’ve issued.
Counters.Counter private _tokenIds;
//mapping for the IPFS hashes associated with tokens.
mapping(string => uint8) hashes;
constructor() ERC721("UniqueAsset", "UNA") {}
// 특정IPFS해시에 아직 mint안되어있으면 mint하는 함수// a method to our contract that will allow us to mint an NFT for a specific IPFS hash if not token has been minted yet for that hash//nft수령인//NFT를 생성할 콘텐츠와 관련된 IPFS 해시 //자산에 대한 JSON 메타데이터에 대한 링크를 참조해야 한다functionawardItem(address recipient, string memory hash, string memory metadata) publicreturns (uint256)
{
require(hashes[hash] != 1);
hashes[hash] = 1;
_tokenIds.increment();
uint256 newItemId = _tokenIds.current();
_mint(recipient, newItemId);
_setTokenURI(newItemId, metadata);
return newItemId;
}
}
폴더구조 migrations 들어가서
2_deploy_contract.js
파일 생성
var UniqueAsset = artifacts.require("UniqueAsset");
module.exports = function (deployer) {
deployer.deploy(UniqueAsset);
};
다 하면 컴파일
truffle compile
만약 오류가 발생하면 Ganache가 실행 중인 포트를 수동으로 설정 (아니면 나처럼 버전, 모듈경로 등의 문제일수도 있음 위에 참조)
truffle-config.js
(가나슈 퀵스타 누르면 뜨는 창에있는거랑 동일하게)
truffle migrate
이걸로
NFT 스마트 계약을 배포
이제 스마트 계약을 처리했으므로 기본 자산을 IPFS로 가져와 관련 NFT를 발행할 때 사용할 수 있는지 확인
IPFS에 자산 추가
우리는 Pinata를 사용하여 자산을 IPFS에 추가하고 고정된 상태로 유지하도록 할 것입니다.
해당 해시는 자산의 검증 가능한 표현이고IPFS 네트워크의 자산을 의미한다.누군가가 자산을 변조하고 변경한 경우 해시가 달라진다. 이 해시는 우리의 스마트 계약을 통해 NFT를 발행할 때 사용해야 하는 것이다.공개 게이트웨이를 제공하는 모든 IPFS 호스트는 이제 콘텐츠를 표시할 수 있다.. 라고 위 링크 글에 있다.
이게 Pinata에 자산을 업로드 하는 거라는데 pinata 내 계정에는 뭔가 달라진게 안보인다.
팀원한테 물어보니 payable이 truffle버전 5이상부터 된다는 얘기를 해서 (하지만 관련문서를 찾아봐도 잘안나온다 솔리디티 버전에 관한 얘기는 있는데 "address payablenot supported in solc 0.4.x or earlier." 나는 solc버전이 0.8이었어서...)
npm uninstall -g truffle 하고
npm i g truffle@5.5.0
로 다시 까니까 됐다.
(packagelock json이 이런거 통일시켜주는 애려나? 근데 그거 있으면 npm i 모듈 깔때 에러 떠서 삭제하고 하는데 흠...)
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.0 < 0.9.0;
//주소.balance//주소의 현재 갖고 있는 이더의 잔액을 의미함. // msg.value는 송금액을 의미한다 달라!// msg.sender// msg.sender는 스마트컨트랙을 사용하는 주체라고 볼 수 있다 // 앞으로 설명할 call 과 delegate call에서 주요 내용이니 관심잇게 봐라
contract MobileBanking{
event SendInfo(address _msgSender, uint256 _currentValue);
event MyCurrentValue(address _msgSender,uint256 _value);
event CurrentValueOfSomeone(address _msgSender, address _to, uint256 _value);
//이더를 보낼라면 //을 넣을 줄 알았지만!!! 보내는 주소, 받을 사람 주소만 있음 되는구나.. 이더를 받아야되기때문에 payable써준다 //함수전체도 이더보는거라 payable써줌functionsendEther(address payable _to) publicpayable{
require (msg.sender.balance >= msg.value , "your balance is not enough");
//트랜스퍼 함수를 통해 이더 송금
_to.transfer(msg.value);
//보낸 나의 주소, 내 잔고
emit SendInfo(msg.sender, (msg.sender).balance);
}
//그냥 현재 잔고확인 함수인듯?functioncheckValueNow() public{
emit MyCurrentValue(msg.sender, (msg.sender).balance);
}
//어떤 계정의 잔고 확인하고 싶을때, 확인하고 싶은 주소 넣기functioncheckUserValue(address _to) public{
emit CurrentValueOfSomeone(msg.sender, _to, _to.balance);
}
}
이더송금하기
sendEther함수 사용
1. 코드를 컴파일한다
또는 우클릭해서 원하는 코드 컴파일.
2. ACCOUNT 에서 현재 내 계정주소 확인하고 Deploy 배포한다.
1로 표시한거 > 계정주소 넣을 부분 2로 표시한거 > 이더금액 넣는 부분 3로 표시한거 > Deploy 배포 4로 표시한거 > 함수사용하는 부분 5로 표시한거 > 내역 (숫자 이거 순서 아님주의!!!)
//2개의 원소를 담을 수 있는 고정길이의 배열;
uint[2] fixedArray;
//고정길이의 배열, 5개의 스트링을 담을 수 있다.
string[5] stringArray;
//동적배열은 고정된 크기가 없으며 계속 크기가 커질 수 있다.
uint[] dynamicArray;
구조체의 배열을 생성할 수 있다!
아까 우리가 sturct Person {uint age; string name;} 이렇게 만들었던 구조체를 이용하면
Person[] people; // 이는 동적 배열로, 원소를 계속 추가할 수 있다.
상태변수가 블록체인에 영구적으로 저장할 수 있는 것처럼 구조체의 동적배열을 생성하면
마치 데이터베이스처럼 컨트랙트에 구조화된 데이터를 저장하는데 유용하다.
Public 배열
public으로 배열을 선언할 수 있음 솔리디티는 이런 배열을 위해 getter메소드를 자동적으로 생성한다.
Person[] public people;
다른 컨트랙트들은 이 배열을 읽을 수는 있게되지만 쓸 수는 없다.
이는 컨트랙트에 공개데이터를 저장할 때 유용한 패턴이다.
지금까지 한 코드
pragma solidity ^0.4.19;
contract ZombieFactory {
uint dnaDigits = 16;
uint dnaModulus = 10 ** dnaDigits;
struct Zombie {
string name;
uint dna;
}
// 좀비들을 다른 앱에 자랑하고 싶어서 좀비 군대 저장소를public으로 함.//Zombie구조체의 public배열을 생성하고 이름을 zombies로 한다.
Zombie[] public zombies;
}
챕터 7: 함수 선언
솔리디티에서 함수 선언
functioneatHam(string _name, uint _amount){
}
(함수의 인자명을 _로 시작하는 건 전역변수랑 구별하려고 하는 관례때문임~)
이 함수를 오출 할때는 이렇게 할 수 있겠지
eatHam("jun",100);
챕터 8: 구조체와 배열 활용하기
새로운 구조체를 생성하기
전에 만들어 놓은 Person구조체를 사용할거야.
//구조체 Person 틀
struct Person {
uint age;
string name;
}
//Person배열을 peole로 이름지음
Person[] public people;
그 틀로 새로운 Person satoshi을 찍어낼거야.
//Person틀쓸거고 변수명은 satoshi임 // age, name
Person satoshi = Person(172,"Satoshi");
//people배열에 이 사람을 추가한다.
peole.push(satoshi);
이 두 줄을 한 줄로 표현하면 이거임
people.push(Person(16,"Jun"));
챕터 9: Private / Public 함수
솔리디티에서 함수는 기본적으로 public으로 선언된다.
누구나 또는 다른 어느 컨트랙트가 우리의 컨트랙트의 함수를 호출하고 코드를 실행할 수 있다는 의미다.
하지만 이건 공격에 취약해질 수 있는 가능성이 있어서
기본적으로 함수는 private로 선언하고 공개할 함수만 public으로 선언하는 게 좋다
#1에서는 누구나 블록추가할 수 있었지만 이제는 계산을 푸는 작업을 해야 블록추가할 수 있게할거야. 이 계산 푸는 거를 채굴이라고 함. 또한 퍼즐의 난이도를 통해 블록 조절할거야. 좀 있다가는 트랜잭션(거래내역)에 대해서도 다룰거야. 이걸 통해서 보상받고 거래주고받고 하는거겠지
블록에 추가할 두 가지 속성 (-> 작업증명 퍼즐(블록의 해쉬값 찾기)을 풀기 위한 값)
difficulty 난이도 (=유효한 블록의 해쉬값은 얼마나 많은 0으로 시작해야하는가, 작은값찾기임)
해쉬값을 2진수로 표현한 다음 시작부분0의 갯수세어보는 것.
난이도 difficulty 가 4이면 임계값인 nonce가 1,2,3 올라가면서 해쉬값을 찾는다. 해쉬값계산 함수를 보면
난이도가 4일 때 hash:08f3... 이거를 binaryHash 2진수로 변환한거임 0: "0000", 8:"1000", f:"1111",3:"0011"
(숫자형태아니고 문자열 string형태라서 옆에 붙는다)
우리는 hexToBinary 함수를 안썼어. 그래서 nonce추가되는걸로 난이도 따라 해시값찾는걸로 나와. naive는 binaryHash기준으로 난이도 비교적 쉽게 통과되고 우리는 hash대로. 그니까 난이도 8일때 hash값 지금 0027..나오면 우린 안돼 더 돌려서 hash값 앞에 0이 최소한 8개여야 통과되는 거임.
아무튼 다시 naive코드로 와서
블록의 해시값이 난이도 difficulty를 만족하는지 확인하는 코드
const hashMatchesDifficulty=(hash, difficulty)=>{
해시값을 이진수화해.
난이도만큼 0을 반복해.
이진수화한 거가. 지정한문자로 시작하는지 확인. 0이 8개인 문자로.
}
난이도를 만족하는 해시값찾기 위해 같은 블록에서 여러번 계산해줘야한다. 이때 nonce값을 쓰는데 sha256라는 해시함수를 쓴다. 채굴이 바로 매번 다른 nonce값을 시도해보는 과정을 뜻한다.
블록구조, 최초의 블록 구조에 difficulty와 nonce추가
class Block{
난이도 추가
넌스값 추가
}
블록찾기
nonce값은 언제 증가시키는 findBlock 함수. 해시값을 찾을 때까지 반복문 돌릴거임.
const findBlock{
초기넌스값은 0
hash를 해시계산함수 안에 블록애들 넣어서 찾아.
만약에 난이도 확인하는 함수에 (해시랑, 난이도)보내서 통과되면
블록을 뱉어내(블록내용들을 담아서)
}
그렇게 블록이 채굴되면 네트워크통해서 발송(broadcast)되겠지
난이도(difficulty)를 어떻게 결정할건가
난이도값을 계산하는 로직필요해서 상수를 몇 개 정의하면서 만들었어. 하드코딩한거임. 물론 naive에서..ㅋㅋ
BLOCK_GENERATION_INTERVAL: 블록은 얼마나 자주 채굴되는가(Bitcoin의 경우 10분 간격이죠.)
DIFFICULTY_ADJUSTMENT_INTERVAL: 난이도difficulty는 얼마나 자주 조정되는가(Bitcoin은 2016블록마다 조정돼요.)
// in secondsconst BLOCK_GENERATION_INTERVAL: number = 10;
// in blocksconst DIFFICULTY_ADJUSTMENT_INTERVAL: number = 10;
신기한게 여기서는 그냥 임의로 지정한 숫자 10을 넣었는데 이게 어떻게 10초, 10블록으로 인식하나 싶었거든. 이게 같이 비교되는 애들따라서 인식을 하는거래. 코드를 보자
난이도가져오는함수
const getDifficulty=(블록체인)={
가장 마지막 블록= 블록체인.길이-1
만약에
마지막블록.index % BLOCK_GENERATION_INTERVAL(10블록으로 인식됨) 이 0이면 그리고 마지막블록.index가 0이 아니면