Skip to content

Commit

Permalink
Merge pull request #78 from kaleido-io/decimals-bug
Browse files Browse the repository at this point in the history
Decimals bug
  • Loading branch information
dechdev authored May 3, 2022
2 parents 0877a37 + 8d993bb commit 0da90b0
Show file tree
Hide file tree
Showing 21 changed files with 221 additions and 103 deletions.
16 changes: 1 addition & 15 deletions server/src/controllers/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ import {
import { Request } from 'express';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { plainToClassFromExist } from 'class-transformer';
import { FireFlyTokenPoolResponse } from '@hyperledger/firefly-sdk';
import { firefly } from '../clients/firefly';
import {
formatTemplate,
FormDataSchema,
getBroadcastMessageBody,
getPrivateMessageBody,
mapPool,
quoteAndEscape as q,
} from '../utils';
import {
Expand Down Expand Up @@ -240,20 +240,6 @@ export class TokensController {
}
}

function mapPool(pool: FireFlyTokenPoolResponse) {
// Some contracts (base ERC20/ERC721) do not support passing extra data.
// The UI needs to adjust for this, as some items won't reliably confirm.
const schema: string = pool.info?.schema ?? '';
return {
id: pool.id,
name: pool.name,
symbol: pool.symbol,
type: pool.type,
decimals: pool.decimals ?? 0,
dataSupport: schema.indexOf('NoData') === -1,
};
}

/**
* Tokens - Code Templates
* Allows the frontend to display representative code snippets for backend operations.
Expand Down
8 changes: 4 additions & 4 deletions server/src/controllers/websocket.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { FireFlySubscriptionBase } from '@hyperledger/firefly-sdk';
import { nanoid } from 'nanoid';
import { firefly } from '../clients/firefly';
import { WebsocketHandler } from '../utils';
import Logger from '../logger';
import { FF_EVENTS, FF_TX } from '../enums';
import Logger from '../logger';
import { mapPool, WebsocketHandler } from '../utils';

/**
* Simple WebSocket Server
Expand Down Expand Up @@ -37,7 +37,7 @@ export class SimpleWebSocket {
type: FF_TX.TOKEN_TRANSFER,
});
if (operations.length > 0) {
event['pool'] = operations[0].input?.pool;
event['pool'] = mapPool(await firefly.getTokenPool(operations[0].input?.pool));
}
} else if (event.transaction?.type === FF_TX.TOKEN_APPROVAL) {
// Enrich token_approval transaction events with pool ID
Expand All @@ -46,7 +46,7 @@ export class SimpleWebSocket {
type: FF_TX.TOKEN_APPROVAL,
});
if (operations.length > 0) {
event['pool'] = operations[0].input?.pool;
event['pool'] = mapPool(await firefly.getTokenPool(operations[0].input?.pool));
}
}
}
Expand Down
16 changes: 15 additions & 1 deletion server/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { getMetadataArgsStorage, RoutingControllersOptions } from 'routing-contr
import { OpenAPI, routingControllersToSpec } from 'routing-controllers-openapi';
import { WebSocketServer } from 'ws';
import { validationMetadatasToSchemas } from 'class-validator-jsonschema';
import { FireFlyDataRequest } from '@hyperledger/firefly-sdk';
import { FireFlyDataRequest, FireFlyTokenPoolResponse } from '@hyperledger/firefly-sdk';
import stripIndent = require('strip-indent');

export enum FF_MESSAGES {
Expand Down Expand Up @@ -147,3 +147,17 @@ export function getMessageBody(body: any) {
}
return dataBody;
}

export function mapPool(pool: FireFlyTokenPoolResponse) {
// Some contracts (base ERC20/ERC721) do not support passing extra data.
// The UI needs to adjust for this, as some items won't reliably confirm.
const schema: string = pool.info?.schema ?? '';
return {
id: pool.id,
name: pool.name,
symbol: pool.symbol,
type: pool.type,
decimals: pool.decimals ?? 0,
dataSupport: schema.indexOf('NoData') === -1,
};
}
7 changes: 3 additions & 4 deletions server/test/tokens.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import * as request from 'supertest';
import FireFly, {
FireFlyTokenBalanceFilter,
FireFlyTokenBalanceResponse,
FireFlyTokenPoolResponse,
FireFlyTokenTransferResponse,
} from '@hyperledger/firefly-sdk';
import server from '../src/server';
import * as request from 'supertest';
import { firefly } from '../src/clients/firefly';
import { TokenMintBurn, TokenPool, TokenPoolInput, TokenTransfer } from '../src/interfaces';
import { TokenMintBurn, TokenPoolInput, TokenTransfer } from '../src/interfaces';
import server from '../src/server';

jest.mock('@hyperledger/firefly-sdk');
const mockFireFly = firefly as jest.MockedObject<FireFly>;
Expand Down
49 changes: 38 additions & 11 deletions ui/src/AppWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { EventContext } from './contexts/EventContext';
import { FormContext } from './contexts/FormContext';
import { FF_EVENTS, FINISHED_EVENT_SUFFIX } from './ff_models/eventTypes';
import { FF_TX_CATEGORY_MAP } from './ff_models/transactionTypes';
import { IEvent } from './interfaces/api';
import { IEvent, ITokenPool } from './interfaces/api';
import { IEventHistoryItem } from './interfaces/events';
import { ITutorial } from './interfaces/tutorialSection';

Expand Down Expand Up @@ -56,6 +56,9 @@ export const AppWrapper: React.FC = () => {
const [awaitedEventID, setAwaitedEventID] = useState<string | undefined>(
undefined
);
const [poolObject, setPoolObject] = useState<ITokenPool | undefined>(
undefined
);
const [refreshBalances, setRefreshBalances] = useState(new Date().toString());
const [refreshAPIs, setRefreshAPIs] = useState(new Date().toString());

Expand Down Expand Up @@ -157,7 +160,7 @@ export const AppWrapper: React.FC = () => {
);
};

const addLogToHistory = async (event: IEvent) => {
const addLogToHistory = (event: IEvent) => {
// Update balance and API boxes, if those events are confirmed
if (event.type === FF_EVENTS.TOKEN_TRANSFER_CONFIRMED)
setRefreshBalances(new Date().toString());
Expand All @@ -171,6 +174,7 @@ export const AppWrapper: React.FC = () => {
);
const txMap = deepCopyMap.get(event.tx);

// If transaction is already in map, append the event
if (txMap !== undefined) {
if (isFailed(event.type) || isFinalEvent(event)) {
setAwaitedEventID(undefined);
Expand All @@ -181,22 +185,43 @@ export const AppWrapper: React.FC = () => {
created: event.created,
isComplete: isFinalEvent(event),
isFailed: isFailed(event.type),
showIcons: txMap.showIcons,
showTxHash: txMap.showTxHash,
txName: txMap.txName,
})
);
} else {
}

const newEvent: IEventHistoryItem = {
events: [event],
created: event.created,
isComplete: isFinalEvent(event),
isFailed: isFailed(event.type),
showIcons: true,
showTxHash: true,
txName: event.transaction?.type
? t(FF_TX_CATEGORY_MAP[event.transaction.type].nicename)
: t('none'),
};

// If transactionID is unknown
if (event.tx === undefined) {
return new Map(
deepCopyMap.set(event.tx, {
events: [event],
created: event.created,
isComplete: isFinalEvent(event),
isFailed: isFailed(event.type),
txName: event.transaction?.type
? t(FF_TX_CATEGORY_MAP[event.transaction?.type].nicename)
: '',
deepCopyMap.set(event.id, {
...newEvent,
showIcons: false,
showTxHash: false,
txName: t('none'),
})
);
}

return new Map(
deepCopyMap.set(event.tx, {
...newEvent,
showIcons: event.pool ? event.pool.dataSupport : true,
})
);
});
};

Expand Down Expand Up @@ -233,6 +258,8 @@ export const AppWrapper: React.FC = () => {
isBlob,
setIsBlob,
searchParams,
poolObject,
setPoolObject,
}}
>
<Header></Header>
Expand Down
28 changes: 16 additions & 12 deletions ui/src/components/Accordion/TransactionAccordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,19 +72,23 @@ export const TransactionAccordion: React.FC<Props> = ({
{value.txName}
</Typography>
</Grid>
<Grid item pl={1}>
{!value.isComplete ? (
<CircularProgress color="warning" size="20px" />
) : !value.isFailed ? (
<CheckCircleOutline sx={{ color: FFColors.Green }} />
) : (
<ErrorOutline color={'error'} />
)}
</Grid>
</Grid>
<Grid item xs={12} pt={1}>
<HashPopover address={txID} />
{value.showIcons && (
<Grid item pl={1}>
{!value.isComplete ? (
<CircularProgress color="warning" size="20px" />
) : !value.isFailed ? (
<CheckCircleOutline sx={{ color: FFColors.Green }} />
) : (
<ErrorOutline color={'error'} />
)}
</Grid>
)}
</Grid>
{value.showTxHash && (
<Grid item xs={12} pt={1}>
<HashPopover address={txID} />
</Grid>
)}
</>
}
rightContent={
Expand Down
4 changes: 3 additions & 1 deletion ui/src/components/Buttons/RunButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const RunButton: React.FC<Props> = ({ endpoint, payload, disabled }) => {
const { setApiStatus, setApiResponse, payloadMissingFields } =
useContext(ApplicationContext);
const { addAwaitedEventID, awaitedEventID } = useContext(EventContext);
const { categoryID, isBlob } = useContext(FormContext);
const { categoryID, isBlob, poolObject } = useContext(FormContext);
const { setMessage, setMessageType } = useContext(SnackbarContext);

const handlePost = () => {
Expand Down Expand Up @@ -56,6 +56,8 @@ export const RunButton: React.FC<Props> = ({ endpoint, payload, disabled }) => {
const [response, data] = result;
setApiResponse(data);
if (response.status === 202) {
if (poolObject && !poolObject.dataSupport) return;

addAwaitedEventID(data);
} else if (!isSuccessfulResponse(response.status)) {
addAwaitedEventID(undefined);
Expand Down
42 changes: 26 additions & 16 deletions ui/src/components/Forms/Tokens/BurnForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { TUTORIAL_FORMS } from '../../../constants/TutorialSections';
import { ApplicationContext } from '../../../contexts/ApplicationContext';
import { FormContext } from '../../../contexts/FormContext';
import { SnackbarContext } from '../../../contexts/SnackbarContext';
import { PoolType } from '../../../ff_models/transferTypes';
import { ITokenPool } from '../../../interfaces/api';
import { DEFAULT_SPACING } from '../../../theme';
import { amountToDecimal } from '../../../utils/decimals';
Expand All @@ -24,7 +25,7 @@ import { MessageForm } from './MessageForm';
export const BurnForm: React.FC = () => {
const { jsonPayload, setJsonPayload, setPayloadMissingFields } =
useContext(ApplicationContext);
const { formID } = useContext(FormContext);
const { formID, setPoolObject } = useContext(FormContext);
const { reportFetchError } = useContext(SnackbarContext);
const { t } = useTranslation();

Expand Down Expand Up @@ -58,21 +59,21 @@ export const BurnForm: React.FC = () => {
!amount || !pool || (!isFungible() && !tokenIndex)
);
}
if (pool) {
if (decimalAmount === undefined)
setDecimalAmount(amountToDecimal('1', pool.decimals));

const body = {
pool: pool?.name,
amount: decimalAmount,
tokenIndex: tokenIndex?.toString(),
messagingMethod: withMessage ? messageMethod : null,
};
setJsonPayload(withMessage ? { ...jsonPayload, ...body } : body);
}

if (decimalAmount === undefined)
setDecimalAmount(amountToDecimal('1', pool?.decimals));

const body = {
pool: pool?.name,
amount: pool?.type === PoolType.F ? decimalAmount : amount,
tokenIndex: tokenIndex?.toString(),
messagingMethod: withMessage ? messageMethod : null,
};
setJsonPayload(withMessage ? { ...jsonPayload, ...body } : body);
}, [pool, decimalAmount, tokenIndex, formID]);

useEffect(() => {
if (formID !== TUTORIAL_FORMS.BURN) return;
const qParams = `?limit=25`;
isMounted &&
fetchCatcher(`${SDK_PATHS.tokensPools}${qParams}`)
Expand All @@ -81,6 +82,7 @@ export const BurnForm: React.FC = () => {
setTokenPools(poolRes);
if (poolRes.length > 0) {
setPool(poolRes[0]);
setPoolObject(poolRes[0]);
}
}
})
Expand All @@ -107,6 +109,11 @@ export const BurnForm: React.FC = () => {
};

const handleAmountChange = (event: React.ChangeEvent<HTMLInputElement>) => {
if (pool && pool.type === PoolType.NF) {
setAmount(event.target.value);
return;
}

if (!pool || isAmountInvalid(event.target.value)) return;

const formattedAmount = amountToDecimal(event.target.value, pool?.decimals);
Expand Down Expand Up @@ -140,9 +147,12 @@ export const BurnForm: React.FC = () => {
fullWidth
value={pool?.id ?? ''}
label={tokenPools.length ? t('tokenPool') : t('noTokenPools')}
onChange={(e) =>
setPool(tokenPools.find((t) => t.id === e.target.value))
}
onChange={(e) => {
setPool(tokenPools.find((t) => t.id === e.target.value));
setPoolObject(
tokenPools.find((t) => t.id === e.target.value)
);
}}
>
{tokenPools.map((tp, idx) => (
<MenuItem key={idx} value={tp.id}>
Expand Down
Loading

0 comments on commit 0da90b0

Please sign in to comment.