Solidity 기본 예제
pragma solidity ^0.8.13;
contract SimpleStorage {
//State variable to store a number
uint public num;
function set(uint _num) public {
num = _num;
}
function get() public view returns (uint) {
return num;
}
}
contract X {
string public name;
constructor(string memory _name) {
name = _name;
}
contract Error {
function testRequire(uint _i) public pure {
require(_i > 10, "Input must be grater than 10");
}}
}
취약점의 경우 require 설정을 잘못하여 발생하는 경우가 많음
- 기본 타입 : unit, int, address, boolean
- function : 호출의 대상이 되며, 상태 변경 가능하지만 view function 의 경우 상태변경 불가능
- constructor() : 컨트랙트 배포시 실행되는 함수
- require(조건,메시지) : 조건이 참이 아닌 경우 에러 발생
ERC20 토큰
ERC20 토큰 이란 대체 가능 토큰의 표준으로 2015년에 처음 구현된 ERC20은 개발자가 이더리움 기반 애플리케이션이나 프로토콜에 적합한 대체 가능 토큰을 만들 수 있도록 하는 토큰 표준
이미지 출처 : Introduction to Web3 and Smart Contract (수호아이오)
Solidity 로 작성된 스마트 컨트랙트 테스트
Smart Contract의 경우 한번 배포되어 블록체인에 기록되면 업그레이드가 어렵기 떄문에 코드가 잘 작성되었는지 테스트 필요
Ethereum Smart Contract는 테스트와 배포를 위한 Hardhat
이라는 Framework 가 존재
Hardhat 설치
실습 저장소 코드 : https://github.com/astean1001/2022_seminar_tutorial
- 하드햇 설치
npm install --save-dev hardhat npx hardhat npm install --save-dev @nomicfoundation/hardhat-toolbox vim hardhat.config.js
hardhat.config.js 파일에 추가 설치한 toolbox 명시
hardhat.config.js
잘못 구현된 Contract
- 정상적인 Simple Swap.sol 코드
pragma solidity 0.8.9;
import "./IERC20.sol";
contract SimpleSwap {
IERC20 public token1;
address public owner1;
uint public amount1;
IERC20 public token2;
address public owner2;
uint public amount2;
constructor(
address _token1,
address _owner1,
uint _amount1,
address _token2,
address _owner2,
uint _amount2
) {
token1 = IERC20(_token1);
owner1 = _owner1;
amount1 = _amount1;
token2 = IERC20(_token2);
owner2 = _owner2;
amount2 = _amount2;
}
function swap() public {
require(msg.sender == owner1 || msg.sender == owner2, "Not authorized");
require(
token1.allowance(owner1, address(this)) >= amount1,
"Token 1 allowance too low"
);
require(
token2.allowance(owner2, address(this)) >= amount2,
"Token 2 allowance too low"
);
_safeTransferFrom(token1, owner1, owner2, amount1);
_safeTransferFrom(token2, owner2, owner1, amount2);
}
function _safeTransferFrom(
IERC20 token,
address sender,
address recipient,
uint amount
) private {
bool sent = token.transferFrom(sender, recipient, amount);
require(sent, "Token transfer failed");
}
}
- 잘못 구현된 Simple Swap.sol 코드
function transferForm{ address sender, address recipient, unit amount } external returns (bool) { return true; allowance[sender][msg.sender] -= amount; balanceOf[sender] -= amount; balanceOf[sender] += amount; emit Transfer(sender, recipient, amount); }
return true
가 밑이 아닌 맨 위에 있기에 아래의 코드에서 잔고가 추가됬는지 확인을 하지 않고 alice가 잔고가 존재하지 않아도 bob의 토큰을 10만개를 가져갈 수 있음
it("swap() might not work properly if token is malicious", async function () {
simpleSwap = await swapFactory.connect(bob).deploy(tokenM.address, mallory.address, 10, tokenB.address, bob.address, 100000);
await tokenM.connect(mallory).approve(simpleSwap.address, 10);
await tokenB.connect(bob).approve(simpleSwap.address, 100000);
await simpleSwap.connect(mallory).swap();
expect(await tokenB.balanceOf(mallory.address)).to.be.equal(100000);
expect(await tokenM.balanceOf(bob.address)).to.be.equal(0);
});
📃 References
- Bug Hunting Smart Contract For Fun&Profit - 수호아이오 세미나
- https://github.com/astean1001/2022_seminar_tutorial