Skip to content

Commit

Permalink
implement range checks for integers
Browse files Browse the repository at this point in the history
  • Loading branch information
gsteenkamp89 committed Apr 12, 2024
1 parent f674796 commit 53f5681
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 38 deletions.
20 changes: 6 additions & 14 deletions src/plugins/oSnap/components/Input/MethodParameter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import { ParamType } from '@ethersproject/abi';
import AddressInput from './Address.vue';
import { hexZeroPad } from '@ethersproject/bytes';
import {
InputTypes,
validateArrayInput,
validateInput,
validateTupleInput,
isBytesLikeSafe
} from '../../utils';
import { InputTypes } from '../../types';
const props = defineProps<{
parameter: ParamType;
Expand All @@ -31,34 +31,25 @@ const placeholders = {
bool: 'true'
} as const;
function reduceInt(type: string) {
if (type.includes('int')) {
return 'int';
}
return type;
}
const inputType = computed(() => {
const baseType = props.parameter.baseType;
if (baseType === 'tuple') {
return {
input: 'tuple',
type: props.parameter.components.map(
item => reduceInt(item.baseType) as InputTypes
)
type: props.parameter.components.map(item => item.baseType as InputTypes)
// ["string","int","address"]
} as const;
}
if (baseType === 'array') {
return {
input: 'array',
type: reduceInt(props.parameter.arrayChildren.baseType) as InputTypes
type: props.parameter.arrayChildren.baseType as InputTypes
} as const;
}
return { type: reduceInt(baseType) as InputTypes, input: 'single' } as const;
return { type: baseType as InputTypes, input: 'single' } as const;
});
const isBooleanInput = computed(
Expand All @@ -71,7 +62,8 @@ const isAddressInput = computed(
() => inputType.value.input === 'single' && inputType.value.type === 'address'
);
const isNumberInput = computed(
() => inputType.value.input === 'single' && inputType.value.type === 'int'
() =>
inputType.value.input === 'single' && inputType.value.type.includes('int')
);
const isBytesInput = computed(
() => inputType.value.input === 'single' && inputType.value.type === 'bytes'
Expand Down
14 changes: 14 additions & 0 deletions src/plugins/oSnap/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -533,3 +533,17 @@ export namespace GnosisSafe {
components?: ContractInput[];
}
}

export type InputTypes =
| 'bool'
| 'string'
| 'address'
| Integer
| 'bytes'
| 'bytes32';

export type Integer = `int${number}` | `uint${number}`;

export function isIntegerType(type: InputTypes): type is Integer {
return type.includes('int');
}
89 changes: 65 additions & 24 deletions src/plugins/oSnap/utils/validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,22 @@ import {
} from '@ethersproject/providers';
import memoize from 'lodash/memoize';
import { Contract } from '@ethersproject/contracts';
import { isBigNumberish } from '@ethersproject/bignumber/lib/bignumber';
import {
BigNumber,
isBigNumberish
} from '@ethersproject/bignumber/lib/bignumber';
import { isBytesLike, isHexString } from '@ethersproject/bytes';
import getProvider from '@snapshot-labs/snapshot.js/src/utils/provider';
import { OPTIMISTIC_GOVERNOR_ABI } from '../constants';
import { BaseTransaction, NFT, Token, Transaction, GnosisSafe } from '../types';
import {
BaseTransaction,
NFT,
Token,
Transaction,
GnosisSafe,
InputTypes,
isIntegerType
} from '../types';
import { parseUnits } from '@ethersproject/units';
import { useMemoize } from '@vueuse/core';

Expand Down Expand Up @@ -145,14 +156,6 @@ export const checkIsContract = useMemoize(
await isContractAddress(address, network)
);

export type InputTypes =
| 'bool'
| 'string'
| 'address'
| 'int'
| 'bytes'
| 'bytes32';

export function isBool(value: string): boolean {
if (value === 'true' || value === 'false') {
return true;
Expand All @@ -161,20 +164,58 @@ export function isBool(value: string): boolean {
}

export function validateInput(inputValue: string, type: InputTypes): boolean {
switch (type) {
case 'address':
return isAddress(inputValue);
case 'bytes':
return isBytesLike(inputValue);
case 'bytes32':
return isBytesLike(inputValue);
case 'int':
return isBigNumberish(inputValue);
case 'bool':
return isBool(inputValue);
// skip validation for string
default:
return true;
if (type === 'address') {
return isAddress(inputValue);
}
if (type === 'bytes') {
return isBytesLike(inputValue);
}
if (type === 'bytes32') {
return isBytesLike(inputValue);
}
if (isIntegerType(type)) {
return isValidInt(inputValue, type);
}
if (type === 'bool') {
return isBool(inputValue);
}
return true;
}

type Integer = `int${number}` | `uint${number}`;

function isValidInt(value: string, type: Integer) {
// check if is number like
if (!isBigNumberish(value)) {
return false;
}

const unsigned = type.startsWith('uint');
const signed = type.startsWith('int');
if (!unsigned && !signed) {
throw new Error(
'Invalid type specified. Type must be either an unsigned integer (uint) or a signed integer (int).'
);
}

const bits = parseInt(type.slice(unsigned ? 4 : 3));

if (isNaN(bits) || bits % 8 !== 0 || bits < 8 || bits > 256) {
throw new Error(
'Invalid integer type specified. Bit size must be a multiple of 8 with a max of 256'
);
}

const number = BigNumber.from(value);
// range checks
if (unsigned) {
const max = BigNumber.from(2).pow(bits).sub(1);
return number.gte(0) && number.lte(max);
} else {
const halfRange = BigNumber.from(2).pow(bits - 1);
const min = halfRange.mul(-1);
const max = halfRange.sub(1);
return number.gte(min) && number.lte(max);
}
}

Expand Down

0 comments on commit 53f5681

Please sign in to comment.