Block-Chain/Solidity
Send Ethereum
WebDevLee
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 변수 값 변경

1. 스마트컨트랙트 A의 msg.sender는 Alice의 주소
2. 스마트컨트랙트 B의 msg.sender는 스마트 컨트랙트 A의 주소
3. 스마트 컨트랙트 B의 num은 5로 변경되고, num=5라는 것은 스마트 컨트랙트 B에 저장된다.

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 라고 합니다.