Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: native token config #636

Merged
merged 12 commits into from
Jul 3, 2024
9 changes: 9 additions & 0 deletions src/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export const types = {
UPDATE_TX_HISTORY: 'UPDATE_TX_HISTORY',
UPDATE_MINING_SERVER: 'UPDATE_MINING_SERVER',
SET_MINING_SERVER: 'SET_MINING_SERVER',
SET_NATIVE_TOKEN_DATA: 'SET_NATIVE_TOKEN_DATA',
};

/**
Expand Down Expand Up @@ -502,3 +503,11 @@ export const setMiningServer = (url) => ({
type: types.SET_MINING_SERVER,
payload: url,
});

/**
* Set the native token data
r4mmer marked this conversation as resolved.
Show resolved Hide resolved
*/
export const setNativeTokenData = (data) => ({
pedroferreira1 marked this conversation as resolved.
Show resolved Hide resolved
type: types.SET_NATIVE_TOKEN_DATA,
payload: data,
});
2 changes: 1 addition & 1 deletion src/components/TokenAdministrative.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { TOKEN_DOWNLOAD_STATUS } from '../sagas/tokens';
import LOCAL_STORE from '../storage';

const mapStateToProps = (state) => {
const HTR_UID = hathorLib.constants.HATHOR_TOKEN_CONFIG.uid;
const HTR_UID = hathorLib.constants.NATIVE_TOKEN_UID;
const htrBalance = get(state.tokensBalance, `${HTR_UID}.data.available`, 0);

return {
Expand Down
13 changes: 8 additions & 5 deletions src/components/TxData.js
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,8 @@ class TxData extends React.Component {
*/
getOutputToken = (tokenData) => {
if (tokenData === hathorLib.constants.HATHOR_TOKEN_INDEX) {
return hathorLib.constants.HATHOR_TOKEN_CONFIG;
const wallet = getGlobalWallet();
return wallet.storage.getNativeTokenData();
}
const tokenConfig = this.props.transaction.tokens[tokenData - 1];
return tokenConfig;
Expand All @@ -325,8 +326,10 @@ class TxData extends React.Component {
* @return {string} Token symbol
*/
getSymbol = (uid) => {
if (uid === hathorLib.constants.HATHOR_TOKEN_CONFIG.uid) {
return hathorLib.constants.HATHOR_TOKEN_CONFIG.symbol;
if (uid === hathorLib.constants.NATIVE_TOKEN_UID) {
const wallet = getGlobalWallet();
const nativeToken = wallet.storage.getNativeTokenData();
return nativeToken.symbol;
}
const tokenConfig = this.props.transaction.tokens.find((token) => token.uid === uid);
if (tokenConfig === undefined) return '';
Expand All @@ -342,7 +345,7 @@ class TxData extends React.Component {
*/
getUIDFromTokenData = (tokenData) => {
if (tokenData === hathorLib.constants.HATHOR_TOKEN_INDEX) {
return hathorLib.constants.HATHOR_TOKEN_CONFIG.uid;
return hathorLib.constants.NATIVE_TOKEN_UID;
}
const tokenConfig = this.props.transaction.tokens[tokenData - 1];
return tokenConfig.uid;
Expand Down Expand Up @@ -718,7 +721,7 @@ class TxData extends React.Component {

const renderTokenList = () => {
const renderTokenUID = (token) => {
if (token.uid === hathorLib.constants.HATHOR_TOKEN_CONFIG.uid) {
if (token.uid === hathorLib.constants.NATIVE_TOKEN_UID) {
return <span>token.uid</span>;
} else if (token.unknown) {
return (
Expand Down
5 changes: 3 additions & 2 deletions src/components/tokens/TokenMint.js
Original file line number Diff line number Diff line change
Expand Up @@ -210,13 +210,14 @@ class TokenMint extends React.Component {

const wallet = getGlobalWallet();
const depositPercent = wallet.storage.getTokenDepositPercentage();
const nativeTokenConfig = wallet.storage.getNativeTokenData();

return (
<TokenAction
renderForm={renderForm}
title={t`Mint tokens`}
subtitle={`A deposit of ${depositPercent * 100}% in HTR of the mint amount is required`}
deposit={`Deposit: ${tokens.getDepositAmount(getAmountToCalculateDeposit(), depositPercent)} HTR (${hathorLib.numberUtils.prettyValue(this.props.htrBalance)} HTR available)`}
subtitle={`A deposit of ${depositPercent * 100}% in ${nativeTokenConfig.symbol} of the mint amount is required`}
tuliomir marked this conversation as resolved.
Show resolved Hide resolved
deposit={`Deposit: ${tokens.getDepositAmount(getAmountToCalculateDeposit(), depositPercent)} ${nativeTokenConfig.symbol} (${hathorLib.numberUtils.prettyValue(this.props.htrBalance)} ${nativeTokenConfig.symbol} available)`}
buttonName={t`Go`}
validateForm={this.mint}
getSuccessMessage={this.getSuccessMessage}
Expand Down
55 changes: 39 additions & 16 deletions src/reducers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@
* LICENSE file in the root directory of this source tree.
*/

import hathorLib from '@hathor/wallet-lib';
import { FEATURE_TOGGLE_DEFAULTS, VERSION } from '../constants';
import { types } from '../actions';
import { get } from 'lodash';
import { get, findIndex } from 'lodash';
import { TOKEN_DOWNLOAD_STATUS } from '../sagas/tokens';
import { WALLET_STATUS } from '../sagas/wallet';
import { PROPOSAL_DOWNLOAD_STATUS } from '../utils/atomicSwap';
import { HATHOR_TOKEN_CONFIG } from "@hathor/wallet-lib/lib/constants";
import { NATIVE_TOKEN_UID } from "@hathor/wallet-lib/lib/constants";
import helpersUtils from '../utils/helpers';
import LOCAL_STORE from '../storage';

Expand Down Expand Up @@ -63,9 +62,9 @@ const initialState = {
requestErrorStatusCode: undefined,
// Tokens already saved: array of objects
// {'name', 'symbol', 'uid'}
tokens: [hathorLib.constants.HATHOR_TOKEN_CONFIG],
tokens: [],
// Token selected (by default is HATHOR)
selectedToken: hathorLib.constants.HATHOR_TOKEN_CONFIG.uid,
selectedToken: NATIVE_TOKEN_UID,
/**
* List of all tokens seen in transactions
* @type {Record<string, string>}
Expand Down Expand Up @@ -118,19 +117,13 @@ const initialState = {
* - Those that were present in at least one proposal in which this wallet participated
* @type {Record<string,{ tokenUid: string, symbol: string, name: string, status: string, oldStatus?: string }>}
*/
tokensCache: {
'00': {
tokenUid: HATHOR_TOKEN_CONFIG.uid,
symbol: HATHOR_TOKEN_CONFIG.symbol,
name: HATHOR_TOKEN_CONFIG.name,
status: TOKEN_DOWNLOAD_STATUS.READY
}
},
tokensCache: {},
featureTogglesInitialized: false,
featureToggles: {
...FEATURE_TOGGLE_DEFAULTS,
},
miningServer: null,
nativeTokenData: null,
tuliomir marked this conversation as resolved.
Show resolved Hide resolved
};

const rootReducer = (state = initialState, action) => {
Expand Down Expand Up @@ -259,6 +252,8 @@ const rootReducer = (state = initialState, action) => {
return onWalletStateChanged(state, action);
case types.SET_MINING_SERVER:
return onSetMiningServer(state, action);
case types.SET_NATIVE_TOKEN_DATA:
return onSetNativeTokenData(state, action);
default:
return state;
}
Expand Down Expand Up @@ -373,8 +368,7 @@ const onUpdateTokenHistory = (state, action) => {
const onUpdateHeight = (state, action) => {
if (action.payload.height !== state.height) {
const tokensBalance = {};
const { uid } = hathorLib.constants.HATHOR_TOKEN_CONFIG;
tokensBalance[uid] = action.payload.htrUpdatedBalance;
tokensBalance[NATIVE_TOKEN_UID] = action.payload.htrUpdatedBalance;
const newTokensBalance = Object.assign({}, state.tokensBalance, tokensBalance);
return {
...state,
Expand Down Expand Up @@ -496,7 +490,7 @@ export const resetSelectedTokenIfNeeded = (state, action) => {
if (hasZeroBalance) {
return {
...state,
selectedToken: hathorLib.constants.HATHOR_TOKEN_CONFIG.uid
selectedToken: NATIVE_TOKEN_UID,
};
}

Expand Down Expand Up @@ -1012,4 +1006,33 @@ export const onSetMiningServer = (state, { payload }) => ({
miningServer: payload,
});

export const onSetNativeTokenData = (state, { payload }) => {
tuliomir marked this conversation as resolved.
Show resolved Hide resolved
let tokens = [...state.tokens];
if (state.tokens.length === 0) {
tokens = [{...payload, uid: NATIVE_TOKEN_UID}];
} else {
const nativeTokenIndex = findIndex(state.tokens, (e) => e.uid === NATIVE_TOKEN_UID);
pedroferreira1 marked this conversation as resolved.
Show resolved Hide resolved
if (nativeTokenIndex === -1) {
// In case the native token is not in the list, we add it as the first token
tokens = [{...payload, uid: NATIVE_TOKEN_UID}, ...state.tokens];
}
tokens[nativeTokenIndex] = {...payload, uid: NATIVE_TOKEN_UID};
tuliomir marked this conversation as resolved.
Show resolved Hide resolved
}

return {
...state,
nativeTokenData: payload,
pedroferreira1 marked this conversation as resolved.
Show resolved Hide resolved
tokens,
tokensCache: {
...state.tokensCache,
[NATIVE_TOKEN_UID]: {
tokenUid: NATIVE_TOKEN_UID,
symbol: payload.symbol,
name: payload.name,
status: TOKEN_DOWNLOAD_STATUS.READY
}
}
};
};

export default rootReducer;
14 changes: 10 additions & 4 deletions src/sagas/wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
transactionUtils,
errors as hathorErrors,
cryptoUtils,
versionApi,
} from '@hathor/wallet-lib';
import {
takeLatest,
Expand Down Expand Up @@ -60,6 +61,7 @@ import {
changeWalletState,
updateTxHistory,
setMiningServer,
setNativeTokenData,
} from '../actions';
import {
specificTypeAndPayload,
Expand Down Expand Up @@ -250,6 +252,9 @@ export function* startWallet(action) {
});
console.log('[+] Start wallet.', serverInfo);

const nativeToken = wallet.storage.getNativeTokenData();
yield put(setNativeTokenData(nativeToken));

let version;
let serverNetworkName = networkName;

Expand Down Expand Up @@ -313,7 +318,8 @@ export function* startWallet(action) {
}
}

yield call([wallet.storage, wallet.storage.registerToken], hathorLibConstants.HATHOR_TOKEN_CONFIG);
const nativeToken = wallet.storage.getNativeTokenData();
yield call([wallet.storage, wallet.storage.registerToken], nativeToken);

if (hardware) {
// This will verify all ledger trusted tokens to check their validity
Expand Down Expand Up @@ -366,7 +372,7 @@ export function* startWallet(action) {
* to asynchronously load all registered tokens
*/
export function* loadTokens() {
const htrUid = hathorLibConstants.HATHOR_TOKEN_CONFIG.uid;
const htrUid = hathorLibConstants.NATIVE_TOKEN_UID;

yield call(fetchTokenData, htrUid);
const wallet = getGlobalWallet();
Expand Down Expand Up @@ -647,7 +653,7 @@ export function* bestBlockUpdate({ payload }) {
}

if (currentHeight !== payload) {
yield put(tokenFetchBalanceRequested(hathorLibConstants.HATHOR_TOKEN_CONFIG.uid));
yield put(tokenFetchBalanceRequested(hathorLibConstants.NATIVE_TOKEN_UID));
}
}

Expand Down Expand Up @@ -682,7 +688,7 @@ export function* walletReloading() {
// We might have lost transactions during the reload, so we must invalidate the
// token histories:
for (const tokenUid of Object.keys(allTokens)) {
if (tokenUid === hathorLibConstants.HATHOR_TOKEN_CONFIG.uid) {
if (tokenUid === hathorLibConstants.NATIVE_TOKEN_UID) {
continue;
}
yield put(tokenInvalidateHistory(tokenUid));
Expand Down
15 changes: 8 additions & 7 deletions src/screens/CreateNFT.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ function CreateNFT() {
const createMeltAuthorityRef = useRef();

const { htrBalance, useWalletService } = useSelector((state) => {
const HTR_UID = hathorLib.constants.HATHOR_TOKEN_CONFIG.uid;
const HTR_UID = hathorLib.constants.NATIVE_TOKEN_UID;
const htrBalance = get(state.tokensBalance, `${HTR_UID}.data.available`, 0);

return {
Expand Down Expand Up @@ -264,17 +264,18 @@ function CreateNFT() {
const htrDeposit = wallet.storage.getTokenDepositPercentage();
const depositAmount = hathorLib.tokensUtils.getDepositAmount(amount, htrDeposit);
const nftFee = hathorLib.numberUtils.prettyValue(tokensUtils.getNFTFee());
const nativeTokenConfig = wallet.storage.getNativeTokenData();

return (
<div className="content-wrapper">
<BackButton />
<h3 className="mt-4">Create NFT</h3>
<p className="mt-5">{t`Here you will create a new NFT. After the creation, you will be able to send the units of this NFT to other addresses.`}</p>
<p>{t`NFTs share the address space with all other tokens, including HTR. This means that you can send and receive tokens using any valid address.`}</p>
<p>{t`NFTs share the address space with all other tokens, including ${nativeTokenConfig.symbol}. This means that you can send and receive tokens using any valid address.`}</p>
<p>{t`Remember to make a backup of your new token's configuration string. You will need to send it to other people to allow them to use your NFT.`}</p>
<p>
{str2jsx(
t`When creating and minting NFTs, a |bold:deposit of ${htrDeposit}%| in HTR is required and an additional |bold:fee of ${nftFee} HTR|. If these tokens are later melted, this HTR deposit will be returned (depending on the amount melted) and the fee will never be returned. Read more about the NFT standard |link:here|.`,
t`When creating and minting NFTs, a |bold:deposit of ${htrDeposit}%| in ${nativeTokenConfig.symbol} is required and an additional |bold:fee of ${nftFee} ${nativeTokenConfig.symbol}|. If these tokens are later melted, this ${nativeTokenConfig.symbol} deposit will be returned (depending on the amount melted) and the fee will never be returned. Read more about the NFT standard |link:here|.`,
{
bold: (x, i) => <strong key={i}>{x}</strong>,
link: (x, i) => <a key={i} href="true" onClick={goToRFC}>{x}</a>,
Expand Down Expand Up @@ -345,10 +346,10 @@ function CreateNFT() {
</div>
</div>
<hr className="mb-5 mt-5"/>
<p><strong>HTR available:</strong> {hathorLib.numberUtils.prettyValue(htrBalance)} HTR</p>
<p><strong>Deposit:</strong> {hathorLib.numberUtils.prettyValue(depositAmount)} HTR</p>
<p><strong>Fee:</strong> {nftFee} HTR</p>
<p><strong>Total:</strong> {hathorLib.numberUtils.prettyValue(tokensUtils.getNFTFee() + depositAmount)} HTR</p>
<p><strong>${nativeTokenConfig.symbol} available:</strong> {hathorLib.numberUtils.prettyValue(htrBalance)} ${nativeTokenConfig.symbol}</p>
<p><strong>Deposit:</strong> {hathorLib.numberUtils.prettyValue(depositAmount)} ${nativeTokenConfig.symbol}</p>
<p><strong>Fee:</strong> {nftFee} ${nativeTokenConfig.symbol}</p>
<p><strong>Total:</strong> {hathorLib.numberUtils.prettyValue(tokensUtils.getNFTFee() + depositAmount)} ${nativeTokenConfig.symbol}</p>
<button type="button" className="mt-3 btn btn-hathor" onClick={onClickCreate}>Create</button>
</form>
<p className="text-danger mt-3">{errorMessage}</p>
Expand Down
9 changes: 5 additions & 4 deletions src/screens/CreateToken.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import { getGlobalWallet } from "../modules/wallet";
function CreateToken() {

const { htrBalance, useWalletService } = useSelector(state => ({
htrBalance: get(state.tokensBalance, `${hathorLib.constants.HATHOR_TOKEN_CONFIG.uid}.data.available`, 0),
htrBalance: get(state.tokensBalance, `${hathorLib.constants.NATIVE_TOKEN_UID}.data.available`, 0),
useWalletService: state.useWalletService,
}));
const wallet = getGlobalWallet();
Expand Down Expand Up @@ -238,17 +238,18 @@ function CreateToken() {

const depositPercent = wallet.storage.getTokenDepositPercentage();
const htrDeposit = depositPercent * 100;
const nativeTokenConfig = wallet.storage.getNativeTokenData();

return (
<div className="content-wrapper">
<BackButton />
<h3 className="mt-4">Create Token</h3>
<p className="mt-5">{t`Here you will create a new customized token. After the creation, you will be able to send this new token to other addresses.`}</p>
<p>{t`Custom tokens share the address space with all other tokens, including HTR. This means that you can send and receive tokens using any valid address.`}</p>
<p>{t`Custom tokens share the address space with all other tokens, including ${nativeTokenConfig.symbol}. This means that you can send and receive tokens using any valid address.`}</p>
<p>{t`Remember to make a backup of your new token's configuration string. You will need to send it to other people to allow them to use your new token.`}</p>
<p>
{str2jsx(
t`When creating and minting tokens, a |bold:deposit of ${htrDeposit}%| in HTR is required. If these tokens are later melted, this HTR deposit will be returned. Read more about it |link:here|.`,
t`When creating and minting tokens, a |bold:deposit of ${htrDeposit}%| in ${nativeTokenConfig.symbol} is required. If these tokens are later melted, this ${nativeTokenConfig.symbol} deposit will be returned. Read more about it |link:here|.`,
{
bold: (x, i) => <strong key={i}>{x}</strong>,
link: (x, i) => <a key={i} href="true" onClick={goToRFC}>{x}</a>,
Expand Down Expand Up @@ -290,7 +291,7 @@ function CreateToken() {
<input ref={addressInputRef} type="text" placeholder={t`Address`} className="form-control" />
</div>
</div>
<p>Deposit: {tokens.getDepositAmount(amount, depositPercent)} HTR ({hathorLib.numberUtils.prettyValue(htrBalance)} HTR available)</p>
<p>Deposit: {tokens.getDepositAmount(amount, depositPercent)} ${nativeTokenConfig.symbol} ({hathorLib.numberUtils.prettyValue(htrBalance)} ${nativeTokenConfig.symbol} available)</p>
<button type="button" className="mt-3 btn btn-hathor" onClick={onClickCreate}>Create</button>
</form>
<p className="text-danger mt-3">{errorMessage}</p>
Expand Down
5 changes: 4 additions & 1 deletion src/screens/CustomTokens.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { GlobalModalContext, MODAL_TYPES } from '../components/GlobalModal';
import { useSelector } from 'react-redux';
import LOCAL_STORE from '../storage';
import { useNavigate } from 'react-router-dom';
import { getGlobalWallet } from '../modules/wallet';

/**
* Initial screen of custom tokens
Expand All @@ -27,6 +28,8 @@ function CustomTokens() {
const alertSuccessRef = useRef(null);
const navigate = useNavigate();
const { tokensBalance } = useSelector(state => ({ tokensBalance: state.tokensBalance }));
const wallet = getGlobalWallet();
const nativeToken = wallet.storage.getNativeTokenData();

/**
* Called when a new token was registered with success, then close the modal and show alert success
Expand Down Expand Up @@ -72,7 +75,7 @@ function CustomTokens() {
<div className="content-wrapper">
<BackButton />
<h3 className="mt-4">{t`Custom Tokens`}</h3>
<p className="mt-5">{t`You can create your own digital token with customized specifications on Hathor Network with only a few clicks. They will fully work under the same technical assumptions of high scalability and decentralized consensus of our native HTR tokens. Custom tokens will always work independently of the price of the native HTR token, and they can serve multiple purposes.`}</p>
pedroferreira1 marked this conversation as resolved.
Show resolved Hide resolved
<p className="mt-5">{t`You can create your own digital token with customized specifications on Hathor Network with only a few clicks. They will fully work under the same technical assumptions of high scalability and decentralized consensus of our native ${nativeToken.symbol} tokens. Custom tokens will always work independently of the price of the native ${nativeToken.symbol} token, and they can serve multiple purposes.`}</p>
<p><SpanFmt>{t`Every custom token has a unique **Configuration String** which must be shared with all other people that will use the custom token.`}</SpanFmt></p>
<p>{t`If you want to use a custom token that already exists, you need to register this token in your Hathor Wallet. For this, you will need the custom token's Configuration String, which you can get from the creators of the token.`}</p>
<div className="d-flex flex-row align-items-center justify-content-center mt-5">
Expand Down
2 changes: 1 addition & 1 deletion src/screens/Server.js
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ function Server() {

// Forces the selected token back to the default, as custom tokens will not be available in different networks
if (networkChanged) {
dispatch(selectToken(hathorLib.constants.HATHOR_TOKEN_CONFIG.uid));
dispatch(selectToken(hathorLib.constants.NATIVE_TOKEN_UID));
}

// We don't have PIN on hardware wallet
Expand Down
2 changes: 1 addition & 1 deletion src/screens/Settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ function Settings() {
// First we get all token configs from registered tokens array,
// remove the HTR token with filter, then map to each configuration string
const configurationStrings = registeredTokens.filter((token) => {
return token.uid !== hathorLib.constants.HATHOR_TOKEN_CONFIG.uid;
return token.uid !== hathorLib.constants.NATIVE_TOKEN_UID;
}).map((token) => {
return hathorLib.tokensUtils.getConfigurationString(token.uid, token.name, token.symbol);
});
Expand Down
Loading
Loading