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

Balance conversion for balance types #376

Merged
merged 7 commits into from
Sep 29, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
189 changes: 111 additions & 78 deletions packages/ui/src/components/EasySetup/ManualExtrinsic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
Alert,
Box,
FormControl,
InputAdornment,
MenuItem,
Select,
SelectChangeEvent,
Expand All @@ -13,19 +14,22 @@ import { ISubmittableResult } from '@polkadot/types/types'
import React, { ChangeEvent, useCallback, useEffect, useMemo, useState } from 'react'
import { useApi } from '../../contexts/ApiContext'
import paramConversion from '../../utils/paramConversion'
import { getTypeDef } from '@polkadot/types/create'
// import { getTypeDef } from '@polkadot/types/create'
Tbaut marked this conversation as resolved.
Show resolved Hide resolved
import { getGlobalMaxValue, inputToBn } from '../../utils'

interface Props {
extrinsicIndex?: string
className?: string
onSetExtrinsic: (ext: SubmittableExtrinsic<'promise', ISubmittableResult>, key?: string) => void
onSetErrorMessage: React.Dispatch<React.SetStateAction<string>>
onSelectFromCallData: () => void
hasErrorMessage: boolean
}

interface ParamField {
name: string
type: string
typeName: string
optional: boolean
}

Expand All @@ -43,59 +47,7 @@ const initFormState = {

const argIsOptional = (arg: any) => arg.type.toString().startsWith('Option<')

const transformParams = (
paramFields: ParamField[],
inputParams: any[],
opts = { emptyAsNull: true }
) => {
// if `opts.emptyAsNull` is true, empty param value will be added to res as `null`.
// otherwise, it will not be added
const paramVal = inputParams.map((inputParam) => {
// to cater the js quirk that `null` is a type of `object`.
if (
typeof inputParam === 'object' &&
inputParam !== null &&
typeof inputParam.value === 'string'
) {
return inputParam.value.trim()
} else if (typeof inputParam === 'string') {
return inputParam.trim()
}
return inputParam
})

const params = paramFields.map((field, ind) => ({
...field,
value: paramVal[ind] || null
}))

return params.reduce((previousValue, { type = 'string', value }) => {
if (value == null || value === '')
return opts.emptyAsNull ? [...previousValue, null] : previousValue

let converted = value

// Deal with a vector
if (type.indexOf('Vec<') >= 0) {
converted = converted.split(',').map((e: string) => e.trim())
converted = converted.map((single: any) =>
isNumType(type)
? single.indexOf('.') >= 0
? Number.parseFloat(single)
: Number.parseInt(single)
: single
)
return [...previousValue, converted]
}

// Deal with a single value
if (isNumType(type)) {
converted =
converted.indexOf('.') >= 0 ? Number.parseFloat(converted) : Number.parseInt(converted)
}
return [...previousValue, converted]
}, [] as any[])
}
const isTypeBalance = (typeName: string) => ['Balance', 'BalanceOf', 'Amount'].includes(typeName)

const isNumType = (type: string) => paramConversion.num.includes(type)

Expand All @@ -104,9 +56,10 @@ const ManualExtrinsic = ({
onSetExtrinsic,
onSetErrorMessage,
extrinsicIndex,
onSelectFromCallData
onSelectFromCallData,
hasErrorMessage
}: Props) => {
const { api } = useApi()
const { api, chainInfo } = useApi()
const [palletRPCs, setPalletRPCs] = useState<any[]>([])
const [callables, setCallables] = useState<any[]>([])
const [paramFields, setParamFields] = useState<ParamField[] | null>(null)
Expand Down Expand Up @@ -137,11 +90,84 @@ const ManualExtrinsic = ({
})
}, [inputParams, paramFields])

const transformParams = useCallback(
(paramFields: ParamField[], inputParams: any[], opts = { emptyAsNull: true }) => {
// if `opts.emptyAsNull` is true, empty param value will be added to res as `null`.
// otherwise, it will not be added
const paramVal = inputParams.map((inputParam) => {
// to cater the js quirk that `null` is a type of `object`.
if (
typeof inputParam === 'object' &&
inputParam !== null &&
typeof inputParam.value === 'string'
) {
return inputParam.value.trim()
} else if (typeof inputParam === 'string') {
return inputParam.trim()
}
return inputParam
})

const params = paramFields.map((field, ind) => ({
...field,
value: paramVal[ind] || null
}))

return params.reduce((previousValue, { type = 'string', value, typeName }) => {
if (value == null || value === '')
return opts.emptyAsNull ? [...previousValue, null] : previousValue

let converted = value

// Deal with a vector
if (type.indexOf('Vec<') >= 0) {
converted = converted.split(',').map((e: string) => e.trim())
converted = converted.map((single: any) =>
isNumType(type)
? single.indexOf('.') >= 0
? Number.parseFloat(single)
: Number.parseInt(single)
: single
)
return [...previousValue, converted]
}

Tbaut marked this conversation as resolved.
Show resolved Hide resolved
// Deal with balance like types where the param need to
// be multiplied by the decimals
if (isTypeBalance(typeName)) {
if (!chainInfo?.tokenDecimals) return previousValue

if (!converted.match('^[0-9]+([.][0-9]+)?$')) {
onSetErrorMessage('Only numbers and "." are accepted.')
return previousValue
}

const bnResult = inputToBn(chainInfo.tokenDecimals, value)

if (bnResult.gte(getGlobalMaxValue(128))) {
onSetErrorMessage('Amount too large')
return previousValue
}

return [...previousValue, bnResult.toString()]
}

// Deal with a single value
if (isNumType(type)) {
converted =
converted.indexOf('.') >= 0 ? Number.parseFloat(converted) : Number.parseInt(converted)
}
return [...previousValue, converted]
}, [] as any[])
},
[chainInfo, onSetErrorMessage]
)

useEffect(() => {
!!paramFields?.length &&
!!inputParams.length &&
setTransformedParams(transformParams(paramFields, inputParams))
}, [inputParams, paramFields])
}, [inputParams, paramFields, transformParams])

const updatePalletRPCs = useCallback(() => {
if (!api) {
Expand Down Expand Up @@ -175,21 +201,19 @@ const ManualExtrinsic = ({
let paramFields: ParamField[] = []
const metaArgs = api.tx[palletRpc][callable].meta.args

console.log('metaArgs', metaArgs)
// console.log('metaArgs', metaArgs)
Tbaut marked this conversation as resolved.
Show resolved Hide resolved
if (metaArgs && metaArgs.length > 0) {
paramFields = metaArgs.map((arg) => {
console.log('getTypeDef', getTypeDef(arg.type.toString()))
const instance = api.registry.createType(arg.type as unknown as 'u32')
console.log('instance', instance)
const raw = getTypeDef(instance.toRawType())
console.log('raw', raw)

arg.typeName.isSome &&
console.log('typeName.unwrap().toString()', arg.typeName.unwrap().toString())
// console.log('getTypeDef', getTypeDef(arg.type.toString()))
// const instance = api.registry.createType(arg.type as unknown as 'u32')
// console.log('instance', instance)
// const raw = getTypeDef(instance.toRawType())
// console.log('raw', raw)
Comment on lines +229 to +233
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we maybe move it to the log method and comment it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After offline discussion we're keeping this around for a bit.


return {
name: arg.name.toString(),
type: arg.type.toString(),
typeName: arg.typeName.unwrap().toString(),
optional: argIsOptional(arg)
}
})
Expand All @@ -209,6 +233,7 @@ const ManualExtrinsic = ({
const onPalletCallableParamChange = useCallback(
(event: SelectChangeEvent<string>, state: string) => {
// reset the params
setTransformedParams(undefined)
setParamFields(null)
onSetErrorMessage('')

Expand Down Expand Up @@ -251,7 +276,7 @@ const ManualExtrinsic = ({
return
}

if (!callable || !palletRpc || !areAllParamsFilled) {
if (!callable || !palletRpc || !areAllParamsFilled || hasErrorMessage) {
return
}

Expand All @@ -272,6 +297,7 @@ const ManualExtrinsic = ({
areAllParamsFilled,
callable,
extrinsicIndex,
hasErrorMessage,
onSetErrorMessage,
onSetExtrinsic,
palletRpc,
Expand Down Expand Up @@ -336,17 +362,24 @@ const ManualExtrinsic = ({
</Select>
</FormControl>
<ul className="paramInputs">
{paramFields?.map((paramField, ind) => (
<li key={`${paramField.name}-${paramField.type}`}>
<TextField
placeholder={paramField.type}
type="text"
label={`${paramField.name}${paramField.optional ? ' (optional)' : ''}`}
value={inputParams[ind] ? inputParams[ind].value : ''}
onChange={(event) => onParamChange(event, { ind, paramField })}
/>
</li>
))}
{paramFields?.map((paramField, ind) => {
return (
<li key={`${paramField.name}-${paramField.type}`}>
<TextField
placeholder={paramField.type}
type="text"
label={`${paramField.name}${paramField.optional ? ' (optional)' : ''}`}
value={inputParams[ind] ? inputParams[ind].value : ''}
onChange={(event) => onParamChange(event, { ind, paramField })}
InputProps={{
endAdornment: isTypeBalance(paramField.typeName) && (
<InputAdornment position="end">{chainInfo?.tokenSymbol || ''}</InputAdornment>
)
}}
/>
</li>
)
})}
</ul>
</Box>
)
Expand Down
3 changes: 2 additions & 1 deletion packages/ui/src/components/modals/Send.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ const Send = ({ onClose, className, onSuccess, onFinalized }: Props) => {
onSetExtrinsic={setExtrinsicToCall}
onSetErrorMessage={setEasyOptionErrorMessage}
onSelectFromCallData={() => setSelectedEasyOption(FROM_CALL_DATA_MENU)}
hasErrorMessage={!!easyOptionErrorMessage}
/>
),
[FROM_CALL_DATA_MENU]: (
Expand All @@ -143,7 +144,7 @@ const Send = ({ onClose, className, onSuccess, onFinalized }: Props) => {
/>
)
}
}, [selectedOrigin, isProxySelected])
}, [selectedOrigin, easyOptionErrorMessage, isProxySelected])

const signCallback = useSigningCallback({
onSuccess,
Expand Down