저번 포스팅에서는 interface IERC20 과 ERC20 컨트랙트 , 토큰 컨트랙트를 직접 작성해보면서 JwToken을 발행해보았다. 이번에는 OpenZeppelin을 이용해 토큰 컨트랙트와 스왑 컨트랙트를 작성해보고자 한다.
이전 게시글 참고)
2022.07.22 - [Ethereum] - Ethereum/이더리움 - 인터페이스 & ERC-20 / 토큰 발행하기
< 목차 >
- OpenZeppelin
- 토큰 컨트랙트
- 스왑 컨트랙트
- 테스트 코드 작성하기
1. OpenZeppelin
오픈제플린(OpenZepplin)은 솔리디티 기반의 스마트 계약을 개발하는 표준 프레임워크이며 오픈제플린을 사용하면 보다 쉽게 ERC20 컨트랙트를 사용해 토큰을 발행할 수 있다. 다시말해, 이전처럼 ERC20 인터페이스와 ERC20 컨트랙트를 우리가 직접 작성하는 것이 아닌 오픈제플린에서 제공해주는 IERC20 인터페이스와 ERC20 컨트랙트를 사용해 토큰 발행이 가능하다. 이더리움 네트워크 상에서 토큰을 발행할 때 인터페이스 자체가 규격화 되어있기 때문에 오픈제플린에서 이미 만들어 놓은 인터페이스를 가져다 쓰는 방식이라고 생각하면 된다.
우선 다음과 같이 터미널에서 오픈제플린을 설치해주도록 하자.
$ npm install openzeppelin-solidity
그리고 node_modules 디렉토리 안에서 IERC20.sol 파일과 ERC20.sol 파일이 위치한 경로를 찾아줘야만 한다.
node_modules/openzeppelin-solidity/contracts/token/ERC20/
node_modules 디렉토리에 설치된 openzeppelin-solidity 디렉토리 안에 위와 같이 ERC20 이외에도 NFT 발행 규격인 ERC721 , ERC1155 등의 컨트랙트가 파일 형태로 제공되어 있는 것을 확인할 수 있다. 앞으로 토큰 컨트랙트를 작성할 때 오픈제플린에서 제공해주는 ERC20 규격을 사용해 작성해보고자 한다.
2. 토큰 컨트랙트
저번 포스팅에서 만들었던 JwToken 컨트랙트는 JwToken 컨트랙트 안에 이더(ether)에서 토큰으로 스왑하는 코드가 작성되어 있었다. 이번에는 컨트랙트의 내용을 분리해서 JwToken 컨트랙트 안에서는 토큰 발행과 관련된 코드들만 작성하고 EthSwap 이라는 컨트랙트를 따로 만들어서 EthSwap 컨트랙트 안에서는 토큰 스왑과 관련된 코드들만 작성해보고자 한다.
이전 게시글 참고)
2022.07.22 - [Ethereum] - Ethereum/이더리움 - 인터페이스 & ERC-20 / 토큰 발행하기
우선 오픈제플린에서 제공해주는 ERC20 컨트랙트를 상속 받아 JwToken 컨트랙트를 작성해주도록 하자.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
// Solidity에서는 import를 해오면 전체 코드를 가져오게 된다.(파일 전체를 불러오는 것)
// import ERC20
import "../node_modules/openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";
// ERC20을 상속 받아 JwToken 컨트랙트 작성
contract JwToken is ERC20 {
string public _name = "JwToken";
string public _symbol = "JTK";
uint256 public _totalSupply = 5000 * (10 ** decimals());
constructor() ERC20(_name, _symbol) {
// _mint() : 컨트랙트를 배포한 사람에게 토큰 발행
_mint(msg.sender, _totalSupply);
}
}
import 구문을 이용해 openzeppelin-solidity 디렉토리 안에 존재하는 ERC20.sol 파일을 가져온 다음 ERC20을 상속받아 JwToken 컨트랙트를 작성해주었다. _name , _symbol , _totalSupply 상태변수에 값을 할당한 다음 constructor( ) 함수 안에서 _mint( ) 함수를 호출하여 컨트랙트 배포 시 컨트랙트 배포자의 계정으로 5000개의 JTK 토큰이 발행되도록 하였다. 이 때 constructor( ) 함수 옆에 ERC20(_name, _symbol) 과 같이 작성되어 있는 것을 확인할 수 있는데 이는 ERC20 컨트랙트 안에 존재하는 constructor( ) 함수의 실행과 관련된 코드이다.
위와 같이 오픈제플린에서 제공해주는 ERC20.sol 파일 안에 constructor( ) 함수가 만들어져 있는 것을 확인할 수 있다. JwToken 컨트랙트의 constructor( ) 함수 옆에 ERC20( ) 을 작성함으로써 상속받은 ERC20 컨트랙트의 constructor( ) 함수 내용을 가져올 수 있다. 클래스 문법에서 자식 클래스가 부모 클래스의 속성을 가져올 때 constructor( ) 함수 안에서 super( ) 를 사용하는 것과 비슷한 맥락이라고 생각하면 된다.
3. 스왑 컨트랙트
이제 EthSwap 컨트랙트를 작성해보도록 하자. 다시 말하지만 JwToken 컨트랙트는 토큰 발행과 관련된 기능만 수행하고 EthSwap 컨트랙트는 토큰 스왑과 관련된 기능만을 수행하도록 컨트랙트를 분리시켜 놓은 것임을 명심하도록 하자. 이렇게 컨트랙트를 분리해서 두 개로 배포할 경우 컨트랙트 간에 서로 상호작용 할 수 있도록 코드를 작성해줘야만 한다. 컨트랙트 간에 상호작용 하는 부분에 유의하면서 코드를 살펴보도록 하자.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import "../node_modules/openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";
contract EthSwap {
// ERC20의 CA 값을 받을 변수 지정
ERC20 public token;
// 여기서 ERC20은 데이터 타입
uint public rate = 100;
// 배포한 ERC20의 CA를 인자값으로 받는다.
constructor(ERC20 _token) {
token = _token;
}
}
EthSwap 컨트랙트 안에서 만들어준 token 이라는 상태변수가 갖는 데이터 타입이 조금 생소할 것이다. token 변수의 데이터 타입으로 ERC20을 선언해줬는데 이는 ERC20.sol 파일 안에 작성된 ERC20 컨트랙트 자체를 데이터 타입으로 지정해 준 것이다. import 해온 ERC20.sol 파일 안에 ERC20 컨트랙트가 존재하기 때문에 이와같이 EthSwap 컨트랙트 안에서 ERC20 이라는 데이터 타입 지정이 가능해진다.
데이터 타입을 ERC20으로 지정해준 이유는 스마트 컨트랙트 간에 서로 상호작용하는 기능을 구현하기 위해 꼭 필요한 과정이다. EthSwap 컨트랙트 안에서 JwToken 컨트랙트 안에 구현되어 있는 함수를 호출하고 싶은 경우가 존재할 수 있다. 이 때 token 변수의 데이터 타입을 ERC20으로 지정하고 token 변수에 JwToken의 CA 값을 할당해주면 token 상태변수를 이용해 JwToken 컨트랙트 안에 작성된 함수들을 호출할 수 있게 된다. 즉, 상태변수에 상호작용하고 싶은 컨트랙트의 CA를 할당해 컨트랙트 간 상호작용 기능을 구현하는 것이라고 보면 된다.
따라서 EthSwap 컨트랙트 안에서 JwToken 컨트랙트와 상호작용 하는 기능을 구현하기 위해서는 JwToken 컨트랙트가 이미 이더리움 네트워크 상에 배포되어 있어야만 한다. JwToken 컨트랙트의 CA를 EthSwap 컨트랙트 constructor( ) 함수의 인자값으로 전달하여 EthSwap 컨트랙트 배포시 token 상태변수에 JwToken 컨트랙트의 CA 값이 할당되게끔 해줘야 하기 때문이다. 이와 관련해서는 배포 단계에서 다시 살펴보기로 하고 이제 EthSwap 컨트랙트 안에서 만들어줄 함수들을 살펴보자.
// EOA -> EthSwap (Contract) -> getToken() -> return JwToken CA
function getToken() public view returns (address) {
// token은 ERC20 타입이기 때문에 address로 형 변환
return address(token); // JwToken CA
}
getToken( ) 함수는 token 변수에 할당된 CA 값을 return 하는 함수이다. 우리가 작성하는 EthSwap 컨트랙트는 JwToken 컨트랙트와 상호작용하는 컨트랙트이다. EthSwap 컨트랙트를 향해 getToken( ) 함수를 호출할 경우 EthSwap이 상호작용 하고 있는 컨트랙트의 CA를 return 해준다고 보면 된다. 이 때 token 상태 변수는 ERC20 타입이기 때문에 address( token ) 을 통해 형 변환을 진행해 준 다음 return 해주었다.
// EOA -> EthSwap -> getSwapBalance() -> JwToken -> balanceOf()
// balanceOf()는 JwToken 컨트랙트에 있는 함수
// EthSwap이라는 컨트랙트에서 JwToken 컨트랙트에 요청을 보내 결과를 가져온 것
function getSwapBalance() public view returns (uint) {
return token.balanceOf(msg.sender);
}
getSwapBalance( ) 함수는 해당 함수를 호출한 계정의 토큰 잔액을 조회하는 함수이다. 이 때 명심해야 할 것은 balanceOf( ) 함수 자체는 JwToken 컨트랙트 안에 있는 함수라는 사실이다. EthSwap 컨트랙트를 향해 getSwapBalance( ) 함수를 호출할 경우 EthSwap 컨트랙트에서는 token 상태 변수를 이용해 JwToken 컨트랙트의 함수를 호출하게 된다. token.balanceOf( ) 와 같은 방식으로 EthSwap 컨트랙트가 JwToken 컨트랙트의 함수를 호출하고 그 결과값을 return 해준다. 이렇게 EthSwap 컨트랙트는 컨트랙트 배포시 인자값으로 전달한 JwToken 컨트랙트의 CA 를 통해 JwToken 컨트랙트와 상호작용 할 수 있게 되는 것이다.
function getThisAddress() public view returns (address) {
return address(this);
// 여기서 this는 해당 컨트랙트를 의미 (this == EthSwap)
// address(this) == EthSwap의 CA
}
function getMsgSender() public view returns (address) {
return msg.sender;
// msg.sender == EthSwap을 실행시킨 사람
/*
txObject = {
from: from에 들어가는 사람이 msg.sender
to:
value:
data:
}
*/
}
getThisAddress( ) 함수는 EthSwap 컨트랙트의 CA를 return 하는 함수이다. this가 가리키는 것은 컨트랙트 자신이기 때문에 address( this ) 로 형 변환을 진행해준 다음 return 해줄 경우 EthSwap의 CA를 반환하게 된다. 그리고 getMsgSender( ) 함수는 EthSwap 컨트랙트를 실행시킨 사람의 계정을 return 해주는 함수이다. 트랜잭션이 발생했을 때 트랜잭션 객체 안의 from 속성값으로 들어가는 계정이 msg.sender가 된다.
function getTokenOwner() public view returns (address) {
return token._owner();
}
getTokenOwner( ) 함수는 JwToken 컨트랙트를 배포시킨 토큰 발행자의 계정을 return 하는 함수이다. JwToken 컨트랙트가 상속받은 ERC20 컨트랙트 안에 정의되어 있는 _owner 상태변수의 getter 함수를 호출하여 토큰 발행자의 EOA를 getTokenOwner( ) 함수를 통해 조회할 수 있도록 하였다.
마지막으로 EthSwap 컨트랙트에서 가장 핵심적인 기능을 수행할 스왑 기능을 갖고 있는 함수를 만들어보고자 한다. 이더에서 토큰으로 스왑하는 기능은 buyToken( ) 함수 안에서, 토큰에서 이더로 스왑하는 기능은 sellToken( ) 함수 안에서 구현해주었다.
// ETH -> TOKEN buy
// 1 ETH 당 몇 개의 토큰을 줄 것인가 // 1 ETH : 100 TOKEN
function buyToken() public payable {
// buyToken
// send({from: , to: CA, value: })
uint256 tokenAmount = msg.value * rate; // 1ETH 전송시 토큰 100개 지급
require(token.balanceOf(address(this)) >= tokenAmount, "error [1]");
token.transfer(msg.sender, tokenAmount); // from: EthSwap CA , to: msg.sender
}
buyToken( ) 함수를 호출할 경우 EthSwap 컨트랙트 안에 정의된 rate 상태변수를 활용해 1 ETH 당 몇 개의 토큰을 지급할 것인지를 계산해 tokenAmount 변수에 할당한다. 그리고 require( ) 구문을 통해 EthSwap 컨트랙트의 CA가 기지고 있는 토큰의 개수가 tokenAmount 보다 크거나 같을 경우에만 token.transfer( ) 함수가 실행되도록 하였다.
현재 JTK 토큰은 JwToken 컨트랙트 배포 시점에 발행되며 배포자의 EOA로 총 발행량에 해당하는 모든 토큰이 지급된다. 하지만 EthSwap 컨트랙트 안에서 구현한 buyToken( ) 함수는 JwToken 컨트랙트 배포자로부터 JTK 토큰을 지급받는 것이 아닌 EthSwap 컨트랙트의 CA로부터 JTK 토큰을 지급받도록 되어있다. 왜냐하면 JwToken 컨트랙트 안에 있는 transfer( ) 함수를 EthSwap 컨트랙트 안에서 호출할 경우 from 속성값에는 EthSwap 의 CA 가 들어갈 수 밖에 없기 때문이다. 따라서 buyToken( ) 함수를 정상적으로 실행시키기 위해서는 JwToken 컨트랙트 배포자가 EthSwap 컨트랙트의 CA 안에 일정량 이상의 JTK 토큰을 넣어둬야만 한다. 즉, 스왑을 담당하는 주체가 EthSwap 컨트랙트인 것이다.
위와 같은 논리는 토큰을 이더로 스왑하는 sellToken( ) 함수 안에서도 똑같이 적용된다.
// TOKEN -> ETH sell
function sellToken(uint256 _amount) public payable {
// EthSwap에서 token.transfer() 함수를 호출하면 from이 EthSwap이 된다.
// account1에 대한 balanceOf()
require(token.balanceOf(msg.sender) >= _amount); // msg.sender == account1
uint256 etherAmount = _amount/rate; // 50/100 == 0.5 이더
require(address(this).balance >= etherAmount); // EthSwap이 가지고 있는 이더가 etherAmount보다 많은가?
token.transferFrom(msg.sender, address(this), _amount); // accoun1 -> EthSwap token 전송
payable(msg.sender).transfer(etherAmount); // EthSwap -> account1 이더 전송
}
account 1 이라는 계정으로 sellToken( ) 함수를 호출한다고 했을 때 우선 require( ) 구문을 통해 account 1 계정이 가지고 있는 토큰의 양이 이더로 스왑하고 싶은 토큰의 양 보다 많은지 체크한다. 조건이 만족될 경우 rate 상태변수를 이용해 전송한 토큰을 몇 이더로 스왑할 것인지 계산해 etherAmount 변수에 할당한다. 이후 EthSwap CA가 보관하고 있는 이더(ether)의 개수가 etherAmount 보다 많을 경우에만 token.transferFrom( ) 함수를 이용해 토큰 스왑을 진행한다. 이 때 JwToken 컨트랙트 안의 transfer( ) 함수가 아닌 transferFrom( ) 함수를 사용해 스왑을 진행하는 이유는 token.transfer( ) 함수를 EthSwap 컨트랙트 안에서 실행시키게 될 경우 from 속성값으로 EthSwap 컨트랙트의 CA가 들어가기 때문이다. 현재 우리는 account 1 계정으로부터 EthSwap CA에게 토큰이 전송되게끔 하고 싶기 때문에 token.transferFrom( ) 함수를 이용할 수밖에 없다.
account 1 계정에서 EthSwap CA 에게 토큰 사용 권한을 위임한 상태라면 EthSwap 컨트랙트 안에서 token.transferFrom( ) 함수를 사용해 account 1 의 토큰을 전송하는 것이 가능해진다. token.transfer( ) 함수를 사용할 수 없는 이유는 transfer( ) 함수를 호출하는 주체가 EthSwap 컨트랙트이기 때문이다. account 1이 소유하고 있는 토큰을 EthSwap CA에게 전송해야 하는데 token.transfer( ) 함수를 호출할 경우 from 값이 EthSwap 컨트랙트의 CA가 되어버린다. 따라서 account 1의 토큰을 EthSwap CA 에게 위임해 놓은 상태에서 token.transferFrom( ) 함수를 이용해 account 1의 토큰을 EthSwap CA 즉, 자기 자신( address(this) )에게 전송하는 방법을 취할 수 밖에 없는 것이다.
다시말해, token.transferFrom( msg.sender, address(this), _amount ) 에 의해 EthSwap CA가 account 1 의 토큰을 자기 자신에게 _amount 양만큼 전송하는 기능이 구현되는 것이다. 그리고 payable( msg.sender ).transfer( etherAmount ) 에 의해 EthSwap CA에서 account 1에게 etherAmount 에 해당하는 이더가 전송된다. 거듭 강조하지만, token.transferFrom( ) 의 실행 주체는 EthSwap 컨트랙트이고 EthSwap CA가 msg.sender로부터 토큰 권한을 위임 받았다는 전제 하에 msg.sender의 토큰을 자기자신(EthSwap CA)에게 전송하는 것이다. 그리고 EthSwap CA는 payable을 통해 msg.sender에게 이더를 전송해주기만 하면 된다.
다음은 EthSwap 컨트랙트의 전체 코드이다.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import "../node_modules/openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";
contract EthSwap {
ERC20 public token;
uint public rate = 100;
// 배포한 ERC20의 CA를 인자값으로 받는다.
constructor(ERC20 _token) {
token = _token;
}
// EOA -> EthSwap (Contract) -> getToken() -> return JwToken CA
function getToken() public view returns (address) {
return address(token); // JwToken CA
}
// EOA -> EthSwap -> getSwapBalance() -> JwToken -> balanceOf()
function getSwapBalance() public view returns (uint) {
return token.balanceOf(msg.sender);
}
function getThisAddress() public view returns (address) {
return address(this);
// 여기서 this는 해당 컨트랙트를 의미 (this == EthSwap)
}
function getMsgSender() public view returns (address) {
return msg.sender;
}
function getTokenOwner() public view returns (address) {
return token._owner();
}
// ETH -> TOKEN buy
function buyToken() public payable {
uint256 tokenAmount = msg.value * rate;
require(token.balanceOf(address(this)) >= tokenAmount, "error [1]");
token.transfer(msg.sender, tokenAmount); // from: EthSwap CA, to: msg.sender
}
// TOKEN -> ETH sell
function sellToken(uint256 _amount) public payable {
require(token.balanceOf(msg.sender) >= _amount);
uint256 etherAmount = _amount/rate;
require(address(this).balance >= etherAmount);
token.transferFrom(msg.sender, address(this), _amount);
payable(msg.sender).transfer(etherAmount);
}
}
4. 테스트 코드 작성하기
이제 마지막으로 EthSwap 컨트랙트의 테스트 코드를 작성해보면서 우리가 구현한 함수들이 잘 동작하는지 확인해보도록 하자. 테스트 코드 작성 및 실행에는 truffle을 사용하였고 ganache-cli를 사용해 로컬 이더리움 네트워크 내에 JwToken 컨트랙트와 EthSwap 컨트랙트를 배포하였다.
// truffle migrations
const JwToken = artifacts.require('JwToken');
const EthSwap = artifacts.require('EthSwap');
module.exports = async function (deployer) {
try {
await deployer.deploy(JwToken); // JwToken 배포 진행
const token = await JwToken.deployed(); // 배포 완료된 인스턴스 가져오기
// console.log(token.address); // JwToken CA
await deployer.deploy(EthSwap, token.address); // EthSwap 배포 진행
const ethSwap = await EthSwap.deployed();
// console.log(ethSwap);
} catch (e) {
console.log(e.message);
}
};
truffle migration 으로 컨트랙트 배포를 진행할 때 JwToken 컨트랙트를 먼저 배포한 다음 아래와 같이 EthSwap 컨트랙트 배포 시에는 JwToken 컨트랙트의 CA를 인자값으로 전달한 것을 확인할 수 있다.
// token = JwToken.deployed()
deployer.deploy( EthSwap, token.address )
아래는 EthSwap 컨트랙트 안에 작성된 함수들의 테스트 코드이다. 테스트 코드에 대한 자세한 설명은 생략하고자 하는데 중요한 포인트 두 가지만 짚고 넘어가려 한다. 우선 buyToken( ) 함수를 실행하기 전에 token.transfer( ) 함수를 통해 EthSwap CA 에 토큰을 넣어주는 것을 잊지 말도록 하자. 그리고 sellToken( ) 함수 실행 전에는 token.approve( ) 함수를 호출해 EthSwap CA에게 account 1이 소유하고 있는 토큰의 권한을 위임해줘야만 한다. 이 두가지에 유념하면서 각자 테스트 코드를 작성해보기 바란다.
const JwToken = artifacts.require('JwToken');
const EthSwap = artifacts.require('EthSwap');
function toEther(n) {
return web3.utils.toWei(n, 'ether');
}
// contract() : 재배포를 진행하면서 테스트 코드를 돌려준다.
contract('EthSwap', ([deployer, account1, account2]) => {
let token, ethSwap;
describe('EthSwap deployment', () => {
// console.log(deployer, account1, account2);
it('deployed', async () => {
token = await JwToken.deployed();
ethSwap = await EthSwap.deployed();
console.log('JwToken CA : ', token.address);
console.log('EthSwap CA : ', ethSwap.address);
});
it('토큰 배포자의 기본 초기값 확인', async () => {
const balance = await token.balanceOf(deployer);
assert.equal(balance.toString(), '5000000000000000000000');
console.log('deployer EOA 토큰 밸런스 : ', balance.toString() / 10 ** 18);
});
it('ethSwap - getSwapBalance()', async () => {
const swapBal = await ethSwap.getSwapBalance();
console.log('getSwapBalance() : ', swapBal.toString() / 10 ** 18);
});
it('ethSwap - getToken()', async () => {
const address = await ethSwap.getToken();
assert.equal(address, token.address);
});
it('ethSwap - getMsgSender(), getThisAddress()', async () => {
const msgSender = await ethSwap.getMsgSender(); // EthSwap 컨트랙트를 실행시킨 사람의 EOA
const thisAddress = await ethSwap.getThisAddress();
assert.equal(msgSender, deployer);
assert.equal(thisAddress, ethSwap.address);
});
// JwToken 컨트랙트 배포한 사람의 EOA를 알고 싶다.
it('token - owner 확인', async () => {
const owner = await token._owner();
assert.equal(owner, deployer);
console.log('owner EOA : ', owner);
});
it('ethSwap - getTokenOwner()', async () => {
const owner = await ethSwap.getTokenOwner();
assert.equal(owner, deployer);
});
it('token - balanceOf()', async () => {
// token.transfer(to, 1000)
// JwToken에 있는 transfer() 함수 실행해서 토큰 전송
// 1 token == 1 wei (단위 맞춰줌)
await token.transfer(ethSwap.address, toEther('1000'));
const balance = await token.balanceOf(ethSwap.address);
const ownerBal = await token.balanceOf(deployer);
console.log('deployer EOA 토큰 밸런스 : ', ownerBal.toString() / 10 ** 18);
console.log('ethSwap CA 토큰 밸런스 : ', balance.toString() / 10 ** 18);
});
it('ethSwap - buyToken()', async () => {
let balance = await token.balanceOf(account1); // 0
let ethBal = await web3.eth.getBalance(account1);
assert.equal(balance.toString(), '0');
console.log('account1 토큰 밸런스 : ', balance.toString());
console.log('account1 이더 밸런스 : ', ethBal.toString() / 10 ** 18);
await ethSwap.buyToken({
from: account1,
value: toEther('1'),
});
balance = await token.balanceOf(account1);
console.log('buyToken() 실행 이후 account1 토큰 밸런스 : ', parseInt(balance.toString()) / 10 ** 18); // 100 token
const ethAccount = await web3.eth.getBalance(account1);
console.log('buyToken() 실행 이후 account1 이더 밸런스 : ', ethAccount / 10 ** 18);
const ethSwapBalance = await web3.eth.getBalance(ethSwap.address);
console.log('ethSwap CA에 들어간 이더 밸런스 : ', web3.utils.fromWei(ethSwapBalance));
const ethSwapTokenBal = await token.balanceOf(ethSwap.address);
console.log('ethSwap CA에 있는 토큰 밸런스 : ', ethSwapTokenBal.toString() / 10 ** 18);
});
it('ethSwap - sellToken()', async () => {
const account1_balance = await token.balanceOf(account1);
// console.log(web3.utils.fromWei(account1_balance.toString(), 'ether'));
let swapEth = await web3.eth.getBalance(ethSwap.address);
let swapToken = await token.balanceOf(ethSwap.address);
let accountEth = await web3.eth.getBalance(account1);
let accountToken = await token.balanceOf(account1);
console.log(`
sellToken() 실행 전
swapEth : ${swapEth / 10 ** 18}
swapToken : ${swapToken / 10 ** 18}
accountEth : ${accountEth / 10 ** 18}
accountToken : ${accountToken / 10 ** 18}
`);
// approve(위임받는사람, 보낼양)
/*
A : 위임 해주는 사람
B : 위임 받는 사람
C : 돈을 받는 사람
A -> B : 1000
approve(B:위임 받는 사람, 1000)
컨트랙트를 발동시키는 사람이 위임을 해준다.
account1 -> ethSwap (CA)
*/
// from : account1 == 위임해주는 사람
// ethSwap (CA) == 위임 받는 사람
await token.approve(ethSwap.address, toEther('50'), {
from: account1,
});
// await token.transfer(ethSwap.address, toEther('50'), {from: account1})
// 마지막 객체는 인자값 X => .send({from: accoun1}) 과 동일
await ethSwap.sellToken(toEther('50'), {
from: account1,
});
swapEth = await web3.eth.getBalance(ethSwap.address);
swapToken = await token.balanceOf(ethSwap.address);
accountEth = await web3.eth.getBalance(account1);
accountToken = await token.balanceOf(account1);
console.log(`
sellToken() 실행 이후
swapEth : ${swapEth / 10 ** 18}
swapToken : ${swapToken / 10 ** 18}
accountEth : ${accountEth / 10 ** 18}
accountToken : ${accountToken / 10 ** 18}
`);
});
});
});
'Ethereum' 카테고리의 다른 글
Ethereum/이더리움 - NFT / contract ERC721 (0) | 2022.07.27 |
---|---|
Ethereum/이더리움 - NFT / Remix로 컨트랙트 배포하기 / OpenSea에 NFT 올리기 (2) | 2022.07.26 |
Ethereum/이더리움 - 인터페이스 & ERC-20 / 토큰 발행하기 (0) | 2022.07.22 |
Ethereum/이더리움 - Solidity(솔리디티) function payable (0) | 2022.07.21 |
Ethereum/이더리움 - 스마트 컨트랙트로 투표 Dapp 만들기 (0) | 2022.07.18 |