
4. 웹(ReactJS) 연동
1. React 코드 수정
1.1. app.js
: contract 를 생성했던 address 를 contractAddress 변수에 활당해야한다.
import React from 'react';
import TronLinkGuide from 'components/TronLinkGuide';
import TronWeb from 'tronweb';
import Utils from 'utils';
import Swal from 'sweetalert2';
import Content from './Content';
import './App.scss';
// 창조자 주소
const FOUNDATION_ADDRESS = 'TCh7ZJM7mtxtDxfM6UwQrjBAFiqu1QCAqX';
// 받을 사람주소
const RECEIVER_ADDRESS = 'TZ9ZDkk2enDUzsnhpGHZS5mnuQeKemKUSe';
const RECEIVER_ADDRESS2 = 'TVLX75yPmRnJkE4sP6Sir5sPEaaKc6hMWz';
// contract 주소
const contractAddress = 'TDegrR2vLenMVvYYsd1FbYqUZoDUpksA7f';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
balance: 0,
getbalanceaddress: '',
transferaddress: '',
transferamount: '',
contractAddress: '',
tokenname: '',
tokensymbol: '',
tronwebaddress: '',
totalSupply: '',
burnamount: '',
transferfromfromaddress: '',
transferfromtoaddress: '',
transferfromamount: '',
approvespender: '',
approveamount: '',
burnfromfrom: '',
burnfromamount: '',
tronWeb: {
installed: false,
loggedIn: false
},
}
this.updateGetBalanceInputValue = this.updateGetBalanceInputValue.bind(this)
this.updateTransferInputValue = this.updateTransferInputValue.bind(this)
this.updateTransferAmountInputValue = this.updateTransferAmountInputValue.bind(this)
this.updateContractAddressInput = this.updateContractAddressInput.bind(this)
this.updateBurnAmountInputValue = this.updateBurnAmountInputValue.bind(this)
this.updateTansferFromFromInputValue = this.updateTansferFromFromInputValue.bind(this)
this.updateTansferFromToInputValue = this.updateTansferFromToInputValue.bind(this)
this.updateTansferFromAmountInputValue = this.updateTansferFromAmountInputValue.bind(this)
this.updateApproveSpender = this.updateApproveSpender.bind(this)
this.updateApproveValue = this.updateApproveValue.bind(this)
this.updateBurnFromFromValue = this.updateBurnFromFromValue.bind(this)
this.updateBurnFromAmountValue = this.updateBurnFromAmountValue.bind(this)
}
async componentDidMount() {
this.setState({loading: true})
await new Promise(resolve => {
console.log('window.tronWeb===>' + window.tronWeb);
const tronWebState = {
installed: !!window.tronWeb,
loggedIn: window.tronWeb && window.tronWeb.ready
};
if (tronWebState.installed) {
this.setState({
tronWeb: tronWebState
});
return resolve();
}
let tries = 0;
const timer = setInterval(() => {
if (tries >= 10) {
const TRONGRID_API = 'https://api.trongrid.io';
window.tronWeb = new TronWeb(
TRONGRID_API,
TRONGRID_API,
TRONGRID_API
);
this.setState({
tronWeb: {
installed: false,
loggedIn: false
}
});
clearInterval(timer);
return resolve();
}
tronWebState.installed = !!window.tronWeb;
tronWebState.loggedIn = window.tronWeb && window.tronWeb.ready;
if (!tronWebState.installed)
return tries++;
this.setState({
tronWeb: tronWebState
});
resolve();
}, 100);
})
if (!this.state.tronWeb.loggedIn) {
// Set default address (foundation address) used for contract calls
// Directly overwrites the address object as TronLink disabled the
// function call
window.tronWeb.defaultAddress = {
hex: window.tronWeb.address.toHex(FOUNDATION_ADDRESS),
base58: FOUNDATION_ADDRESS
};
window.tronWeb.on('addressChanged', () => {
if (this.state.tronWeb.loggedIn)
return;
this.setState({
tronWeb: {
installed: true,
loggedIn: true
}
});
});
}
/*
await Utils.setTronWeb(window.tronWeb, contractAddress);
// 창조자 주소
//console.log(Utils.tronWeb.address.fromHex((((await Utils.tronWeb.trx.getAccount()).address).toString())));
const tronwebaddress = Utils.tronWeb.address.fromHex((((await Utils.tronWeb.trx.getAccount()).address).toString()))
// 창조자 가진 금액
console.log("contract token total money : " + await Utils.tronWeb.trx.getBalance());
await this.setState({tronwebaddress : tronwebaddress})
*/
//await this.updateContractAddressInput();
}
//--------------------------------------------------------------------------------------------
// DYNAMIC CONTRACT ADDRESS
//--------------------------------------------------------------------------------------------
async updateContractAddressInput(evt) {
await this.setState({
contractAddress: evt.target.value
//contractAddress: 'TPHeKw1nmDgAC6FiHS7v5VGSEnQ3d4WjJ7'
});
console.log('contractAddress : ', this.state.contractAddress);
await Utils.setTronWeb(window.tronWeb, this.state.contractAddress);
//const tmp_name = await Utils.contract.name().call();
const tmp_tronwebaddress = Utils.tronWeb.address.fromHex((((await Utils.tronWeb.trx.getAccount()).address).toString()));
await this.setState({
tokenname: await Utils.contract.name().call(),
tokensymbol: await Utils.contract.symbol().call(),
totalSupply: ((await Utils.contract.totalSupply().call()).toNumber()) / 100000000,
tronwebaddress: tmp_tronwebaddress
});
// const testValue =await Utils.hexAddressToBase58('41a2726afbecbd8e936000ed684cef5e2f5cf43008');
// console.log(testValue);
}
//--------------------------------------------------------------------------------------------
// GET BALANCE
//--------------------------------------------------------------------------------------------
async getBalance(_getbalanceaddress) {
const balance = ((await Utils.contract.balanceOf(_getbalanceaddress).call()).toNumber()) / 100000000;
//const balance = await Utils.contract.decimals().call();
console.log('balance', balance);
this.setState({balance: balance})
}
updateGetBalanceInputValue(evt) {
console.log('getbalanceaddress : ', this.state.getbalanceaddress);
this.setState({
getbalanceaddress: evt.target.value
});
}
//--------------------------------------------------------------------------------------------
// TRANSFER
//--------------------------------------------------------------------------------------------
Transfer(_to, _amount) {
Utils.contract.transfer(_to, _amount).send({
shouldPollResponse: true,
callValue: 0
}).then(res => Swal({
title: 'Transfer Successful',
type: 'success'
})).catch(err => Swal({
title: 'Transfer Failed',
type: 'error'
}));
}
updateTransferInputValue(evt) {
this.setState({
transferaddress: evt.target.value
});
console.log('transferaddress : ', this.state.transferaddress);
}
updateTransferAmountInputValue(evt) {
console.log('transferamount : ', this.state.transferamount);
this.setState({
transferamount: evt.target.value
});
}
//--------------------------------------------------------------------------------------------
// TRANSFER FROM
//--------------------------------------------------------------------------------------------
async TransferFrom(_from, _to, _amount) {
const allowance = await Utils.contract.allowance(_from, this.state.tronwebaddress).call();
console.log('allowance : ', allowance);
Utils.contract.transferFrom(_from, _to, _amount).send({
shouldPollResponse: true,
callValue: 0
}).then(res => Swal({
title: 'Transfer Successful',
type: 'success'
})).catch(err => Swal({
title: 'Transfer Failed',
type: 'error'
}));
}
updateTansferFromFromInputValue(evt) {
this.setState({
transferfromfromaddress: evt.target.value
});
console.log('transferfromfromaddress : ', this.state.transferfromfromaddress);
}
updateTansferFromToInputValue(evt) {
this.setState({
transferfromtoaddress: evt.target.value
});
console.log('transferfromtoaddress : ', this.state.transferfromtoaddress);
}
updateTansferFromAmountInputValue(evt) {
this.setState({
transferfromamount: evt.target.value
});
console.log('transferfromamount : ', this.state.transferfromamount);
}
//--------------------------------------------------------------------------------------------
// APPROVE
//--------------------------------------------------------------------------------------------
Approve(_spender, _amount) {
Utils.contract.approve(_spender, _amount).send({
shouldPollResponse: true,
callValue: 0
}).then(res => Swal({
title: 'Approval Successful',
type: 'success'
})).catch(err => {
Swal({
title: 'Approval Failed',
type: 'error'
})
console.log('err ==> ' + JSON.stringify(err));
}
);
}
updateApproveValue(evt) {
this.setState({
approveamount: evt.target.value
});
console.log('approveamount : ', this.state.approveamount);
}
updateApproveSpender(evt) {
this.setState({
approvespender: evt.target.value
});
console.log('approvespender : ', this.state.approvespender);
}
//--------------------------------------------------------------------------------------------
// BURN
//--------------------------------------------------------------------------------------------
Burn(_amount) {
Utils.contract.burn(_amount).send({
shouldPollResponse: true,
callValue: 0
}).then(res => Swal({
title: 'Burn Successful',
type: 'success'
})).catch(err => Swal({
title: 'Burn Failed',
type: 'error'
}));
}
updateBurnAmountInputValue(evt) {
console.log('burnamount : ', this.state.burnamount);
this.setState({
burnamount: evt.target.value
});
}
//--------------------------------------------------------------------------------------------
// BURN FROM
//--------------------------------------------------------------------------------------------
BurnFrom(_from, _amount) {
Utils.contract.burnFrom(_from, _amount).send({
shouldPollResponse: true,
callValue: 0
}).then(res => Swal({
title: 'Burn Successful',
type: 'success'
})).catch(err => Swal({
title: 'Burn Failed',
type: 'error'
}));
}
updateBurnFromAmountValue(evt) {
console.log('burnfromamount : ', this.state.burnfromamount);
this.setState({
burnfromamount: evt.target.value
});
}
updateBurnFromFromValue(evt) {
console.log('burnfromfrom : ', this.state.burnfromfrom);
this.setState({
burnfromfrom: evt.target.value
});
}
render() {
if (!this.state.tronWeb.installed)
return <TronLinkGuide/>;
if (!this.state.tronWeb.loggedIn)
return <TronLinkGuide installed/>;
return (
<div className='row'>
<div className='col-lg-12 text-center'>
<hr/>
<div className="topnav">
<img src={'CodeXpert.png'} width="200px"/>
</div>
<hr style={{color: 'black', backgroundColor: 'black', height: 0.5}}/>
<h1 style={{color: 'black'}}>Tron TRC20 Token Management Platform</h1>
<hr style={{color: 'black', backgroundColor: 'black', height: 0.5}}/>
<p> Your Address : {this.state.tronwebaddress} </p>
<br/>
<br/>
{/* ---------------------------------------------------------------------------------------------- */}
{/* 컨태랙트 주소 복사 */}
{/* ---------------------------------------------------------------------------------------------- */}
<p> Paste your contract address here : </p>
<input style={{width: "400px"}} value={this.state.contractAddress}
onChange={this.updateContractAddressInput}/>
<br/>
<p> Token name : {this.state.tokenname}</p>
<p> Token Symbol : {this.state.tokensymbol}</p>
<p> Total Supply : {this.state.totalSupply}</p>
<hr style={{color: 'white', backgroundColor: 'white', height: 0.5}}/>
<br/>
<br/>
{/* ---------------------------------------------------------------------------------------------- */}
{/* 잔액 가져오기 */}
{/* ---------------------------------------------------------------------------------------------- */}
<h1>잔액 보기</h1>
<br/>
<input style={{width: "400px"}} value={this.state.getbalanceaddress}
onChange={this.updateGetBalanceInputValue}/>
<br/>
<br/>
<button className='btn btn-primary' onClick={(event) => {
event.preventDefault()
this.getBalance(this.state.getbalanceaddress)
}}>Get Balance
</button>
<br/>
<br/>
<p>Your balance is : {this.state.balance}</p>
<br/>
{/* ---------------------------------------------------------------------------------------------- */}
{/* 금액 보내기 */}
{/* ---------------------------------------------------------------------------------------------- */}
<hr style={{color: 'white', backgroundColor: 'white', height: 0.5}}/>
<h1>금액 보내기</h1>
<br/>
<br/>
<p> To : </p>
<input style={{width: "400px"}} value={this.state.transferaddress}
onChange={this.updateTransferInputValue}/>
<p> Amount : </p>
<input style={{width: "200px"}} value={this.state.transferamount}
onChange={this.updateTransferAmountInputValue}/>
<br/>
<br/>
<button className='btn btn-primary' onClick={(event) => {
event.preventDefault()
this.Transfer(this.state.transferaddress, this.state.transferamount * 100000000)
}}>Transfer
</button>
<br/>
{/* ---------------------------------------------------------------------------------------------- */}
{/* 3자 이체 */}
{/* ---------------------------------------------------------------------------------------------- */}
<hr style={{color: 'white', backgroundColor: 'white', height: 0.5}}/>
<h1>3자이체</h1>
<br/>
<p> From : </p>
<input style={{width: "400px"}} value={this.state.transferfromfromaddress}
onChange={this.updateTansferFromFromInputValue}/>
<p> To : </p>
<input style={{width: "400px"}} value={this.state.transferfromtoaddress}
onChange={this.updateTansferFromToInputValue}/>
<p> Amount : </p>
<input style={{width: "200px"}} value={this.state.transferfromamount}
onChange={this.updateTansferFromAmountInputValue}/>
<br/>
<br/>
<button className='btn btn-primary' onClick={(event) => {
event.preventDefault()
this.TransferFrom(this.state.transferfromfromaddress, this.state.transferfromtoaddress, this.state.transferfromamount * 100000000)
}}>Transfer From
</button>
<br/>
<br/>
{/* ---------------------------------------------------------------------------------------------- */}
{/* APPROVE 하기 */}
{/* ---------------------------------------------------------------------------------------------- */}
<hr style={{color: 'white', backgroundColor: 'white', height: 0.5}}/>
<h1>승인받기(APPROVE)</h1>
<br/>
<p> Spender : </p>
<input style={{width: "400px"}} value={this.state.approvespender}
onChange={this.updateApproveSpender}/>
<p> Amount : </p>
<input style={{width: "400px"}} value={this.state.approveamount}
onChange={this.updateApproveValue}/>
<br/>
<br/>
<button className='btn btn-primary' onClick={(event) => {
event.preventDefault()
this.Approve(this.state.approvespender, this.state.approveamount * 100000000)
}}>Approve
</button>
<br/>
{/* ---------------------------------------------------------------------------------------------- */}
{/* burn 하기 */}
{/* ---------------------------------------------------------------------------------------------- */}
<hr style={{color: 'white', backgroundColor: 'white', height: 0.5}}/>
<h1>태우기(burn)</h1>
<br/>
<p> Amount to burn : </p>
<input style={{width: "200px"}} value={this.state.burnamount}
onChange={this.updateBurnAmountInputValue}/>
<br/>
<br/>
<button className='btn btn-primary' onClick={(event) => {
event.preventDefault()
this.Burn(this.state.burnamount * 100000000)
}}>Burn
</button>
<br/>
<br/>
<br/>
{/* ---------------------------------------------------------------------------------------------- */}
{/* 3자 태우기 이체 */}
{/* ---------------------------------------------------------------------------------------------- */}
<hr style={{color: 'white', backgroundColor: 'white', height: 0.5}}/>
<h1>3자 태우기(burn)</h1>
<br/>
<p> From : </p>
<input style={{width: "200px"}} value={this.state.burnfromfrom}
onChange={this.updateBurnFromFromValue}/>
<p> Amount to burn : </p>
<input style={{width: "200px"}} value={this.state.burnfromamount}
onChange={this.updateBurnFromAmountValue}/>
<br/>
<br/>
<button className='btn btn-primary' onClick={(event) => {
event.preventDefault()
this.BurnFrom(this.state.burnfromfrom, this.state.burnfromamount * 100000000)
}}>Burn From
</button>
<br/>
<br/>
<br/>
<br/>
</div>
</div>
);
}
}
export default App;
1.2. utils/index.js
//const contractAddress = 'TA2zHDeqLw5DUd8FrRjP7Zn3FV64S3wj6K'
//const contractAddress = 'TGNN7ZgoqGNTqkBmtumY1BioSmZGtsWKbR'
const HEX_PREFIX = '41';
const utils = {
tronWeb: false,
contract: false,
async setTronWeb(tronWeb, contractAddress) {
this.tronWeb = tronWeb;
this.contract = await tronWeb.contract().at(contractAddress)
},
async hexAddressToBase58 (hexAddress) {
let retval = hexAddress;
try {
let tronWeb = this.tronWeb;
if (hexAddress.startsWith("0x")) {
hexAddress = HEX_PREFIX + hexAddress.substring(2);
}
let bArr = tronWeb.utils['code'].hexStr2byteArray(hexAddress);
retval = tronWeb.utils['crypto'].getBase58CheckAddress(bArr);
} catch (e) {
//Handle
}
return retval;
}
};
export default utils;
→ hexAddressToBase58 는 핵사주소를 기본 값으로 변경시키는 로직 부분이다.
2. 실행
→ yarn start

2.1. Contract 링크
: contract 주소를 넣는다.
: 주소를 입력하면 바로 Token 정보를 가져온다.

2.2. 잔액 확인
: contract 주소를 넣는다.
: 주소를 입력하면 바로 Token 정보를 가져온다.

2.3. 금액보내기
: 임의로 계정 2개를 생성해놓자.( 나는 이전에 이미 생성을 해놓은 상태였다.)

TONLINK 계정은 contract 로 만들어놓은 계정을 선택해 놓은 상태에서 jin2 계정으로 보내본다.
5000개 토큰을 보내고 확인해보자.

“Transfer” 를 클릭하면 Request Signature 창이 뜨게 되고 계약 내용을 확인하고 Accept 를 눌러주면된다.
오류가 났나? 재밌는 건 어쩌다가 몇 번 오류가 날 수도 있다. 이는 shouldPollResponse: true 가 설정되어 있을 때 네트워크 또는 기타 이유로 인해 발생될 수도 있다고 한다. 아직 Tron Shasta 가 불안한가? 어쨋든 이러한 것이 귀찮다면 이부분을 주석 처리해서 해보면.. 잘 보내질 것 이다.
제대로 Token이 보내졌다면 TronLinK 해당 게정에 토큰이 들어와있는지 확인해보자. 아래와 같이 제대로 500 Token 이 들어가있는 걸 확인할 수 있다.

2.4. 3자 이체
: 내가 누군가에게 단순히 Token 을 보내는 것은 위의 Transfer 기능을 활용하면 된다.
하지만 우리가 살고 있는 실세계의 은행을 생각해보면.. 내 돈을 제 3자(곧 은행) 에게 이체를 하고
내 돈을 활용 할 권한을 주는것이라고 생각해본 적은 없는가?
제3자(은행)에게 내 돈을 사용할 수 있는 특정 권한을 줌으로써 은행은 여러 대출 상품을 만들고 보험도 만들고,
때로는 해당 돈을 직원들 월급을 주기도한다. 물론 내돈을 사용해서 – 가 나게 하면 소송감이기하지만
은행은 정해진 틀안에서 내 돈을 활용하고 이자까지 주고있다.
어찌됐든 우리는 권한을 주었고 은행은 우리가 허용한 권한 안에서 우리의 돈을 자알~ 굴리고 있는 것이다.
그러면 블록체인에서의 3자 이체 권한을 주는 방법을 살펴보자.
우리가 현재 등록한 계정을 다시 한 번 살펴보자

TronlInk 의 주체를 devdevil0625 로 지정 된 상태에서 park2213 에게 1000 개의 Token 을 jin2 가 보낼 수 있는 권한을 줄 수 있도록 테스트 해본다.
이번 테스트에서 주목할 점은 jin2 가 devdevil0625의 토큰을 park2213 에게 보내는 것이다.
2.4. 승인받기
TronLink → devdevil0625의 주소 (TCh7ZJM7mtxtDxfM6UwQrjBAFiqu1QCAqX)
Spender → jin2 의 주소 (TVLX75yPmRnJkE4sP6Sir5sPEaaKc6hMWz)
Amount → 1000
위처럼 샛팅한 뒤에 Request Signature 창이 팝업 되면 Accept를 클릭 한

3자 이체 수행을 해본다.

park2213의 토큰 1000개가 모두 보내어졋다.

2.5. 태우기 (burn)
현재 devdevil0625 의 토큰 잔액 확인
→ 998500

park2213의 토큰 1000개가 모두 보내어졋다.

최종 확인
→ 993500

2.6. 3자 태우기
먼저 승인받기 (5000 개를 태울 수 있는 권한을 jin2 에게 준다.)

TronLink 지갑 소유자를 jin2 로 변경한 뒤

3자 태우기 실행

최종 확인
