// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
contract FundMe {
uint256 public minimumUsd = 50 * 1e18;
address[] public funders;
mapping(ddress => uint256) public addressToAmountFunded;
function fund() public payable{
require(getConversionRate(msg.value) >= minimumUsd, "Didn't send enough!"); // 1e18 == 1 * 10 ** 18
funders.push(msg.sender);
addressToAmountFunded[msg.sender] = msg.value;
}
...
계약에 돈을 보낸 사람을 추적하기 위해 funders
배열을 만들어준다. 그리고 얼마를 보냈는지 확인을 위해 mapping
을 통해 값에 msg.value
를 추가한다.
contracts/
SimpleStorage.sol
StorageFactory.sol
ExtraStorage.sol
FundMe.sol
PriceConverter.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
library PriceConverter {
function getPrice() internal view returns(uint256){
// ABI
// Address 0xD4a33860578De61DBAbDc8BFdb98FD742fA7028e
AggregatorV3Interface priceFeed = AggregatorV3Interface(0xD4a33860578De61DBAbDc8BFdb98FD742fA7028e);
(,int256 price,,,) = priceFeed.latestRoundData();
// ETH in terms of USD
// 1300.00000000
return uint256(price * 1e10); // 1**10 == 10000000000
}
function getVersion() internal view returns (uint256) {
AggregatorV3Interface priceFeed = AggregatorV3Interface(0xD4a33860578De61DBAbDc8BFdb98FD742fA7028e)
return priceFeed.version();
}
function getConversionRate(uint256 ethAmount) internal view returns (uint256) {
uint256 ethPrice = getPrice();
// 1300_00000000000000000 = ETH / USD price
// 1_0000000000000000000 ETH
uint256 ethAmountInUsd = (ethPrice * ethAmount) / 1e18;
return ethAmountInUsd;
}
}
contract대신에 library를 입력해준다. 변수를 가질 수 없고 라이브러리의 모든 기능은 internal이 된다.
FundMe.sol에서 getPrice
, getVersion
및 getConversionRate
함수를 잘라내서 PriceConverter.sol에 붙여넣는다. AggregatorV3Interface
역시 사용하지 않으므로 import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
도 잘라내어 붙여넣는다. 마지막으로 가져온 함수의 상태변수를 internal
로 바꾸어준다.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import "./PriceConverter.sol"
contract FundMe {
using PriceConverter for uint256;
uint256 public minimumUsd = 50 * 1e18;
address[] public funders;
mapping(ddress => uint256) public addressToAmountFunded;
function fund() public payable{
require(msg.value.getConversionRate() >= minimumUsd, "Didn't send enough!"); // 1e18 == 1 * 10 ** 18
funders.push(msg.sender);
addressToAmountFunded[msg.sender] = msg.value;
}
}
Solidity에서 'using X for Y'의 의미는 라이브러리의 함수 X를 타입 Y로 사용한다는 것이다.
예를 들어 Using SafeMath for uint256 라고 하면,
add, sub, mul, div같은 SafeMath 의 함수의 bound는 uint256타입인 것이다.
이것이 작동하는 방식은 라이브러리를 만든 후 스마트계약에서 사용할 때 일반적으로 변수가 전달될 것으로 예상하는 함수는 msg.value를 첫 번째 변수로 간주한다. 따라서 msg.value는 라이브러리 함수의 첫 번째 매개 변수로 간주된다.
function getConversionRate (uint256 ethAmount, uint256 somethingElse) internal view returns (uint256)
msg.value.getConversionRate(123);
두 번째 변수를 함수에 전달하려는 경우 일반 함수 호출처럼 전달된다. 123
은 두번째 파라미터이다.
//SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
contract SafeMathTester{
uint8 public bigNumber = 255; // unchecked
function add() public{
bigNumber= bigNumber + 1;
}
}
uint8이 가질 수 있는 가장 큰 숫자 255에 1을 추가하면 오버플로우가 발생 0을 반환
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract SafeMathTester{
uint8 public bigNumber = 255;
function add() public{
unchecked{bigNumber= bigNumber + 1;
}
}
0.8.0 이후에는 에러를 발생하지만 uncheked
를 통해 이전 버전과 동일하게 수행한다. 계약이 더 효율적이라고 한다.
FundMe.sol
...
function withdraw() public{
for (uint256 funderIndex= 0; funderIndex < funders.length; funderIndex++){
address funder= funders[funderIndex];
addressToAmountFunded[funder]= 0;
}
}
}
배열을 반복하고 보낸금액을 0으로 설정한다.
FundMe.sol
...
function withdraw() public{
for (uint256 funderIndex= 0; funderIndex < funders.length; funderIndex++){
address funder= funders[funderIndex];
addressToAmountFunded[funder]= 0;
}
funders= new address[](0);
}
}
배열을 초기화 해준다.
이 시리즈는 freeCodeCamp.org의 강의를 들으면서 공부한 내용을 정리하기 위해 작성했습니다.