-
Send EthereumBlock-Chain/Solidity 2023. 4. 4. 17:12
코인 이더리움을 송신하는 방법 및 관련 개념에 대해 정리하였습니다.
< 이더를 보내는 함수 >
코인 이더리움을 보내는 3가지 방법은 send, transfer, call이 있습니다.
키워드 Payable은 코인과 상호작용(송금)시 필요한 키워드입니다. 주로 함수, 주소, 생성자에 붙여 사용됩니다.
msg.value는 송금보낸 코인의 값입니다.- send : 2300 gas를 소비. 성공여부를 true 또는 false로 리턴
- transfer : 2300 gas를 소비. 실패시 에러를 발생
- call : 가변적인 gas 소비(gas값 지정 가능). 성공여부를 true 또는 false로 리턴
Ex)
event howMuch(uint256 _value); function sendNow(address payable _to) public payable { bool sent = _to.send(msg.value); // return true or false require(sent, "failed to send ethereum"); emit howMuch(msg.value); } function transferNow(address payable _to) public payable { _to.transfer(msg.value); emit howMuch(msg.value); } function callNow(address payable _to) public payable { // ~0.7 bool sent = _to.call.gas(1000).value(msg.value)(""); require(sent, "failed to send ethereum"); // 0.7~ bool sent = _to.call{value: msg.value, gas: 1000}(""); require(sent, "failed to send ethereum"); }
< balance와 msg.sender >
balance는 해당 지갑 주소의 현재 이더 잔액을 말합니다.
msg.sender는 스마트컨트랙트와 상호작용하는 지갑주소를 말합니다.Ex)
event SendInfo(address _msgSender, uint256 _currentValue); event MyCurrentValue(address _msgSender, uint256 _value); event CurrentValueOfSomeone(address _msgSender, address _to, uint256 _value); function sendEther(address payable _to) public payable { require(msg.sender.balance >= msg.value, "Your balance is not enough"); _to.transfer(msg.value); emit SendInfo(msg.sender, (msg.sender).balance); } function checkValueNow() public { emit MyCurrentValue(msg.sender, msg.sender.balance); } function checkUserMoney(address _to) public { emit CurrentValueOfSomeone(msg.sender, _to, _to.balance); }
< 생성자 payable, 함수 사용 권한 제한 >
생성자는 스마트컨트랙트 생성(첫 배포)시 작동하는데, 여기에 payable을 붙이면 이더를 스마트컨트랙트 안에 넣어줄 수 있습니다.
msg.sender를 require와 연계하여 특정 지갑주소에게만 특정 함수를 사용할 수 있도록 권한을 설정할 수 있습니다.Ex)
contract lecture34 { address owner; constructor() payable { owner = msg.sender; } modifier onlyOwner { require(msg.sender == owner, "Only Onwer!"); _; } event SendInfo(address _msgSender, uint256 _currentValue); event MyCurrentValue(address _msgSender, uint256 _value); event CurrentValueOfSomeone(address _msgSender, address _to,uint256 _value); function sendEther(address payable _to) public onlyOwner payable { require(msg.sender.balance>=msg.value, "Your balance is not enough"); _to.transfer(msg.value); emit SendInfo(msg.sender,(msg.sender).balance); } function checkValueNow() public onlyOwner { emit MyCurrentValue(msg.sender, msg.sender.balance); } function checkUserMoney(address _to) public onlyOwner { emit CurrentValueOfSomeone(msg.sender,_to ,_to.balance); } }
< fallback / receive >
fallback는 말 그대로 대비책 함수입니다. 무기명 함수이고, 다음과 같은 이유로 사용합니다.
fallback의 사용 이유 3가지
- 스마트 컨트랙이 이더를 받을 수 있게 한다. => payable
- 이더를 받고난 후 어떠한 행동을 취하게 할 수 있다. => payable
- call함수로 없는 함수가 불려질 때, 어떠한 행동을 취하게 할 수 있다. => external
< 0.6 이전, 이후 fallback >
이전 :
fallback() external payable { }
이후 : receive와 fallback 두가지 형태로 나뉨.
- receive : 순수하게 이더만 받을때 작동
- fallback : 함수를 실행하며 이더를 보낼때, 또는 불려진 함수가 없을 때 작동합니다.receive() external payable { } fallback() external payable { }
< call >
call 함수는 코인 송금 외에도, 외부 스마트컨트랙트의 함수에 접근할 수 있습니다.
Ex)
contract add { event JustFallback(string _str); event JustReceive(string _str); function addNumber(uint256 _num1, uint256 _num2) public pure returns(uint256) { return _num1 + _num2; } fallback() external { emit JustFallback("JustFallback is called"); } receive() external payable { emit JustReceive("JustReceive is called"); } } contract caller { event calledFunction(bool _success, bytes _output); // call을 이용한 송금 function transferEther(address payable _to) public payable { (bool success, ) = _to.call{ value:msg.value }(""); require(success, "failed to transfer ether"); } // call을 이용한 외부 스마트 컨트랙트 함수 부르기 function callMethod(address _contractAddr, uint256 _num1, uint256 _num2) public { (bool success, bytes memory outputFromCalledFunction) = _contractADdr.call( abi.encodeWithSignature("addNumber2(uint256, uint256)", _num1, _num2) ); require(success, "failed to call other function"); emit callMethod(success, outputFromCalledFunction); } }
< call vs delegate call >
delegate call은 스마트컨트랙트 B의 함수들을 스마트 컨트랙트 A에 옮겨놓는 것처럼 행동하는 것을 말합니다.
쉽게 말하자면, 스마트 컨트랙 A는 껍데기이고, 스마트 컨트랙 B는 주요 함수들을 갖고 있는 핵심이라 할 수 있습니다.조건 : A스마트컨트랙트와 B스마트컨트랙트는 같은 변수를 갖고 있어야 한다.
Ex: num 변수 값 변경
< Call > 1. 스마트컨트랙트 A의 msg.sender는 Alice의 주소
2. 스마트컨트랙트 B의 msg.sender는 스마트 컨트랙트 A의 주소
3. 스마트 컨트랙트 B의 num은 5로 변경되고, num=5라는 것은 스마트 컨트랙트 B에 저장된다.
< Delegate Call > 1. 스마트컨트랙트 B의 msg.sender는 스마트 컨트랙트 A의 주소가 아니라, Alice의 주소
2. 스마트컨트랙트 B의 함수가 불려서 num 값이 5가 되었지만, 정작 num=5라는 것은 스마트컨트랙트 A에 저장되어있고, 스마트컨트랙트 B의 num값은 그대로 3이다.
contract add { uint256 public num = 0; event Info(address _addr, uint256 _num); function plusOne() public { num = num + 1; emit Info(msg.sender, num); } } contract caller { uint256 public num = 0; function callNow(address _contractAddr) public payable { (bool success, ) = _contractAddr.call(abi.encodeWithSignature("plusOne()")); require(success, "failed to transfer ether"); } function delcateCallNow(address _contractAddr) public payable { (bool success, ) = _contractAddr.delegatecall(abi.encodeWithSignature("plusOne()")); require(success, "failed to transfer ether"); } }
< delegate call이 필요한 이유 >
num을 5로 변경하는 함수가, 10으로 변경하는 로직으로 수정해야 할 때
=>
스마트컨트랙트 A는 껍데기니까, 스마트컨트랙트 B의 주요 로직을 변경해서 새로운 스마트컨트랙트 B'를 배포한다.
그리고 스마트컨트랙트 A의 delegate call 주소를 B -> B'로 변경하면 완료!
이를 upgradable smart contract framework 라고 합니다.'Block-Chain > Solidity' 카테고리의 다른 글
Enum & Interface & Library & Import (0) 2023.04.24 Modifier & SPDX (0) 2023.03.22 에러 핸들러 (0) 2023.03.07 조건문 & 반복문 (0) 2023.02.15 Mapping & Array & struct(구조체) (0) 2023.01.16