[소스 투고] 다른 Steem Token Claim 기능 in Steemcoinpan

Words
590
Reading
3 min
Listen
Play
7y

안녕하세요 realmankwon@realmankwon입니다.

어제 Token의 임대 정보를 보여주는 기능을 만들어서 포스팅을 하였습니다.
그런데 만들고 나서 보니 현재 Pending Token이 있는 Token들을 보여주고 바로 Claim을 할 수 있으면 좋겠다는 생각이 들었습니다.
그래서 바로 기능을 추가하였습니다.

* Pending Token 보여주기

만약 가지고 있는 Token 중에 Claim 해야할 Pending Token이 있다면 빨간색으로 표시한 후

현재 보유 수량 + Pending Token 양

으로 표시하게끔 변경하였습니다.

1 ) 기본적으로 다음으로 포멧으로 Token의 수량이 표시됩니다. 스테이킹, 임대를 주거나 받은 것이 없으면 표시하지 않도록 소스를 수정하였습니다.

Token 이름 _ 현재보유수량 _ Pending 수량(Claim 가능 수량)

2 ) 스테이킹이나 임대주거나 받은 것이 있다면 다음과 같이 표시 됩니다.

Token 이름 _ 현재보유수량 _ Pending 수량(Claim 가능 수량) _ (스테이킹수량 _ + 임대받은 수량 _ - 임대 준 수량)

  • Pending 수량이 있으면 빨간색으로 표시되어 버튼을 클릭하면 바로 Claim을 할 수 있습니다.
    Claim이 완료되고 나면 결과에 대해 현재는 별다른 표시가 없지만 관련해서는 조금 더 수정을 해야할 듯 합니다.
    화면으로 보여드리면 "와 신박하다" 하실텐데 그 점이 조금 아쉽네요.

1. steemApi.js

Token의 pending 수량을 알기 위해 Status 값과 수량의 계산을 위해 precision이 필요하여 info 값을 추가로 저장하였습니다.

    const account = urlParts[1];
    const [
        tokenBalances,
        tokenUnstakes,
        tokenStatuses,
        transferHistory,
        allTokenBalances,
        allTokenInfo,
    ] = await Promise.all([
        ssc.findOne('tokens', 'balances', {
            account,
            symbol: LIQUID_TOKEN_UPPERCASE,
        }),
        ssc.findOne('tokens', 'pendingUnstakes', {
            account,
            symbol: LIQUID_TOKEN_UPPERCASE,
        }),
        getScotAccountDataAsync(account),
        getSteemEngineAccountHistoryAsync(account),
        ssc.find('tokens', 'balances', {
            account,
        }),
        getScotDataAsync('info', {}),
    ]);
    if (tokenBalances) {
        state.accounts[account].token_balances = tokenBalances;
    }
    if (tokenUnstakes) {
        state.accounts[account].token_unstakes = tokenUnstakes;
    }
    if (tokenStatuses && tokenStatuses[LIQUID_TOKEN_UPPERCASE]) {
        state.accounts[account].token_status = tokenStatuses[LIQUID_TOKEN_UPPERCASE];
        state.accounts[account].all_token_status = tokenStatuses;
    }
    if (transferHistory) {
        // Reverse to show recent activity first
        state.accounts[
            account
        ].transfer_history = transferHistory.reverse();
    }

    if (allTokenBalances) {
        state.accounts[account].all_token_balances = allTokenBalances;
    }

    if(allTokenInfo){
        state.accounts[account].all_token_info = allTokenInfo;
    }

2 . UserWallet.jsx

Token을 표시하는 부분에서 Pending Token 정보를 추가하였고 버튼 클릭 시 Claim 되도록 함수를 추가하였습니다.

   if(account.has('all_token_balances'))
    {
        const allTokenBalances = account.get('all_token_balances').toJS();
        const allTokenStatus = account.get('all_token_status');
        const allTokenInfo = account.get('all_token_info');
    
        // added by realmankwon (2019-06-18) sort by alphabet asc
        allTokenBalances.sort(
            (a, b) =>
                a.symbol >
                b.symbol
                    ? 1
                    : -1
        );

        for (let v = 0; v < allTokenBalances.length; ++v) {
            const tokenBalance = allTokenBalances[v];
            
            // added by realmankwon (2019-06-18) except LIQUID_TOKEN_UPPERCASE 
            if(tokenBalance.symbol === LIQUID_TOKEN_UPPERCASE) continue;
            const pendingToken = allTokenStatus.getIn([tokenBalance.symbol, 'pending_token']);
            const tokenPrecision = allTokenInfo.getIn([tokenBalance.symbol, 'precision']);
            let tokenReward = 0;
            if(pendingToken && tokenPrecision ){
                tokenReward = pendingToken / Math.pow(10, tokenPrecision);
            }

            const tokenFormattedStr = 
                    <FormattedAssetToken
                        balance={tokenBalance.balance}
                        stake={tokenBalance.stake}
                        delegationsIn={tokenBalance.delegationsIn}
                        delegationsOut={tokenBalance.delegationsOut}
                        reward={tokenReward}
                        symbol={tokenBalance.symbol}
                    />;

            const tokenBalanceStr = isMyAccount && tokenReward > 0 
                ? <span key={tokenBalance.symbol} onClick = {e => {
                    this.handleClaimTokenRewards(account, tokenBalance.symbol);
                    }}>
                    {tokenFormattedStr}
                </span>
                : <span key={tokenBalance.symbol} >
                    {tokenFormattedStr}
                </span>;

            all_token_balances_list.push(
                tokenBalanceStr
            );
        }

이 부분이 Claim을 처리하는 함수 부분입니다.

handleClaimTokenRewards = (account, token) => {
    this.props.claimTokenRewards(account, token);
};

    claimTokenRewards: (account, token) => {
        const username = account.get('name');
        const successCallback = () => {
            dispatch(
                globalActions.getState({ url: `@${username}/transfers` })
            );
        };

        const operation = {
            id: 'scot_claim_token',
            required_posting_auths: [username],
            json: JSON.stringify({
                symbol: token,
            }),
        };

        dispatch(
            transactionActions.broadcastOperation({
                type: 'custom_json',
                operation,
                successCallback,
            })
        );
    },

3 . FormattedAssetToken.jsx

   const FormattedAssetToken = ({ balance, stake, delegationsIn, delegationsOut, reward, symbol }) => {
   if (balance && typeof balance === 'string') {
         balance = parsePayoutAmount(balance);
    }
    const bal = formatDecimal(balance);

let isStake = false;
let isDelegationIn = false;
let isDelegationOut = false;
let isReward = false;

if (stake && typeof stake === 'string') {
    stake = parsePayoutAmount(stake);
    if(stake > 0)
        isStake = true;
}
const stk = formatDecimal(stake);

if (delegationsIn && typeof delegationsIn === 'string') {
    delegationsIn = parsePayoutAmount(delegationsIn);
    if(delegationsIn > 0)
        isDelegationIn = true;
}
const dIn = formatDecimal(delegationsIn || 0) ;

if (delegationsOut && typeof delegationsOut === 'string') {
    delegationsOut = parsePayoutAmount(delegationsOut);
    if(delegationsOut > 0)
        isDelegationOut = true;
}
const dOut = formatDecimal(delegationsOut || 0) ; 


    if(reward > 0)
        isReward = true;

const rew = formatDecimal(reward || 0) ; 
const tokenClassName = isReward ? `FormattedAssetTokenReward` : `FormattedAssetToken`;

return isStake || isDelegationIn || isDelegationOut ? (
    <span className={tokenClassName}>
        <span className="prefix">{symbol} </span>
        <span className="integer">{bal[0]}</span>
        <span className="decimal">{bal[1]}</span>
        { isReward && ` + `}
        { isReward &&
            <span className="integer">{rew[0]}</span>
        }
        { isReward &&
            <span className="decimal">{rew[1]}</span>
        }
        {'('}
        <span className="integer">{stk[0]}</span>
        <span className="decimal">{stk[1]}</span>{', +'}
        <span className="integer">{dIn[0]}</span>
        <span className="decimal">{dIn[1]}</span>{', -'}
        <span className="integer">{dOut[0]}</span>
        <span className="decimal">{dOut[1]}</span>{')'}
    </span>
) : (
    <span className={tokenClassName}>
        <span className="prefix">{symbol} </span>
        <span className="integer">{bal[0]}</span>
        <span className="decimal">{bal[1]}</span>
        { isReward && ` + `}
        { isReward &&
            <span className="integer">{rew[0]}</span>
        }
        { isReward &&
            <span className="decimal">{rew[1]}</span>
        }
    </span>
);
  });