ccxt
Version:
209 lines (206 loc) • 10.9 kB
JavaScript
// ----------------------------------------------------------------------------
// PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
// https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
// EDIT THE CORRESPONDENT .ts FILE INSTEAD
/**
* Validate cairo contract method arguments
* Flow: Determine type from abi and than validate against parameter
*/
import { Literal, Uint, } from '../../types/index.js';
import assert from '../assert.js';
import { CairoUint256 } from '../cairoDataTypes/uint256.js';
import { CairoUint512 } from '../cairoDataTypes/uint512.js';
import { isBigInt, isBoolean, isHex, isNumber, toBigInt } from '../num.js';
import { isLongText, isString } from '../shortString.js';
import { getArrayType, isLen, isTypeArray, isTypeBool, isTypeByteArray, isTypeBytes31, isTypeEnum, isTypeFelt, isTypeLiteral, isTypeOption, isTypeResult, isTypeStruct, isTypeTuple, isTypeUint, } from './cairo.js';
const validateFelt = (parameter, input) => {
assert(isString(parameter) || isNumber(parameter) || isBigInt(parameter), `Validate: arg ${input.name} should be a felt typed as (String, Number or BigInt)`);
if (isString(parameter) && !isHex(parameter))
return; // shortstring
const param = BigInt(parameter.toString(10));
assert(
// from : https://github.com/starkware-libs/starknet-specs/blob/29bab650be6b1847c92d4461d4c33008b5e50b1a/api/starknet_api_openrpc.json#L1266
param >= 0n && param <= 2n ** 252n - 1n, `Validate: arg ${input.name} cairo typed ${input.type} should be in range [0, 2^252-1]`);
};
const validateBytes31 = (parameter, input) => {
assert(isString(parameter), `Validate: arg ${input.name} should be a string.`);
assert(parameter.length < 32, `Validate: arg ${input.name} cairo typed ${input.type} should be a string of less than 32 characters.`);
};
const validateByteArray = (parameter, input) => {
assert(isString(parameter), `Validate: arg ${input.name} should be a string.`);
};
const validateUint = (parameter, input) => {
if (isNumber(parameter)) {
assert(parameter <= Number.MAX_SAFE_INTEGER, `Validation: Parameter is to large to be typed as Number use (BigInt or String)`);
}
assert(isString(parameter) ||
isNumber(parameter) ||
isBigInt(parameter) ||
(typeof parameter === 'object' && 'low' in parameter && 'high' in parameter) ||
(typeof parameter === 'object' &&
['limb0', 'limb1', 'limb2', 'limb3'].every((key) => key in parameter)), `Validate: arg ${input.name} of cairo type ${input.type} should be type (String, Number or BigInt), but is ${typeof parameter} ${parameter}.`);
let param;
switch (input.type) {
case Uint.u256:
param = new CairoUint256(parameter).toBigInt();
break;
case Uint.u512:
param = new CairoUint512(parameter).toBigInt();
break;
default:
param = toBigInt(parameter);
}
switch (input.type) {
case Uint.u8:
assert(param >= 0n && param <= 255n, `Validate: arg ${input.name} cairo typed ${input.type} should be in range [0 - 255]`);
break;
case Uint.u16:
assert(param >= 0n && param <= 65535n, `Validate: arg ${input.name} cairo typed ${input.type} should be in range [0, 65535]`);
break;
case Uint.u32:
assert(param >= 0n && param <= 4294967295n, `Validate: arg ${input.name} cairo typed ${input.type} should be in range [0, 4294967295]`);
break;
case Uint.u64:
assert(param >= 0n && param <= 2n ** 64n - 1n, `Validate: arg ${input.name} cairo typed ${input.type} should be in range [0, 2^64-1]`);
break;
case Uint.u128:
assert(param >= 0n && param <= 2n ** 128n - 1n, `Validate: arg ${input.name} cairo typed ${input.type} should be in range [0, 2^128-1]`);
break;
case Uint.u256:
assert(param >= 0n && param <= 2n ** 256n - 1n, `Validate: arg ${input.name} is ${input.type} 0 - 2^256-1`);
break;
case Uint.u512:
assert(CairoUint512.is(param), `Validate: arg ${input.name} is ${input.type} 0 - 2^512-1`);
break;
case Literal.ClassHash:
assert(
// from : https://github.com/starkware-libs/starknet-specs/blob/29bab650be6b1847c92d4461d4c33008b5e50b1a/api/starknet_api_openrpc.json#L1670
param >= 0n && param <= 2n ** 252n - 1n, `Validate: arg ${input.name} cairo typed ${input.type} should be in range [0, 2^252-1]`);
break;
case Literal.ContractAddress:
assert(
// from : https://github.com/starkware-libs/starknet-specs/blob/29bab650be6b1847c92d4461d4c33008b5e50b1a/api/starknet_api_openrpc.json#L1245
param >= 0n && param <= 2n ** 252n - 1n, `Validate: arg ${input.name} cairo typed ${input.type} should be in range [0, 2^252-1]`);
break;
case Literal.Secp256k1Point: {
assert(param >= 0n && param <= 2n ** 512n - 1n, `Validate: arg ${input.name} must be ${input.type} : a 512 bits number.`);
break;
}
default:
break;
}
};
const validateBool = (parameter, input) => {
assert(isBoolean(parameter), `Validate: arg ${input.name} of cairo type ${input.type} should be type (Boolean)`);
};
const validateStruct = (parameter, input, structs) => {
// c1v2 uint256 or u512 in struct
if (input.type === Uint.u256 || input.type === Uint.u512) {
validateUint(parameter, input);
return;
}
if (input.type === 'core::starknet::eth_address::EthAddress') {
assert(typeof parameter !== 'object', `EthAddress type is waiting a BigNumberish. Got ${parameter}`);
const param = BigInt(parameter.toString(10));
assert(
// from : https://github.com/starkware-libs/starknet-specs/blob/29bab650be6b1847c92d4461d4c33008b5e50b1a/api/starknet_api_openrpc.json#L1259
param >= 0n && param <= 2n ** 160n - 1n, `Validate: arg ${input.name} cairo typed ${input.type} should be in range [0, 2^160-1]`);
return;
}
assert(typeof parameter === 'object' && !Array.isArray(parameter), `Validate: arg ${input.name} is cairo type struct (${input.type}), and should be defined as js object (not array)`);
// shallow struct validation, only first depth level
structs[input.type].members.forEach(({ name }) => {
assert(Object.keys(parameter).includes(name), `Validate: arg ${input.name} should have a property ${name}`);
});
};
const validateEnum = (parameter, input) => {
assert(typeof parameter === 'object' && !Array.isArray(parameter), `Validate: arg ${input.name} is cairo type Enum (${input.type}), and should be defined as js object (not array)`);
const methodsKeys = Object.getOwnPropertyNames(Object.getPrototypeOf(parameter));
const keys = [...Object.getOwnPropertyNames(parameter), ...methodsKeys];
if (isTypeOption(input.type) && keys.includes('isSome') && keys.includes('isNone')) {
return; // Option Enum
}
if (isTypeResult(input.type) && keys.includes('isOk') && keys.includes('isErr')) {
return; // Result Enum
}
if (keys.includes('variant') && keys.includes('activeVariant')) {
return; // Custom Enum
}
throw new Error(`Validate Enum: argument ${input.name}, type ${input.type}, value received ${parameter}, is not an Enum.`);
};
const validateTuple = (parameter, input) => {
assert(typeof parameter === 'object' && !Array.isArray(parameter), `Validate: arg ${input.name} should be a tuple (defined as object)`);
// todo: skip tuple structural validation for now
};
const validateArray = (parameter, input, structs, enums) => {
const baseType = getArrayType(input.type);
// Long text (special case when parameter is not an array but long text)
if (isTypeFelt(baseType) && isLongText(parameter)) {
return;
}
assert(Array.isArray(parameter), `Validate: arg ${input.name} should be an Array`);
switch (true) {
case isTypeFelt(baseType):
parameter.forEach((param) => validateFelt(param, input));
break;
case isTypeTuple(baseType):
parameter.forEach((it) => validateTuple(it, { name: input.name, type: baseType }));
break;
case isTypeArray(baseType):
parameter.forEach((param) => validateArray(param, { name: '', type: baseType }, structs, enums));
break;
case isTypeStruct(baseType, structs):
parameter.forEach((it) => validateStruct(it, { name: input.name, type: baseType }, structs));
break;
case isTypeEnum(baseType, enums):
parameter.forEach((it) => validateEnum(it, { name: input.name, type: baseType }));
break;
case isTypeUint(baseType) || isTypeLiteral(baseType):
parameter.forEach((param) => validateUint(param, { name: '', type: baseType }));
break;
case isTypeBool(baseType):
parameter.forEach((param) => validateBool(param, input));
break;
default:
throw new Error(`Validate Unhandled: argument ${input.name}, type ${input.type}, value ${parameter}`);
}
};
export default function validateFields(abiMethod, args, structs, enums) {
abiMethod.inputs.reduce((acc, input) => {
const parameter = args[acc];
switch (true) {
case isLen(input.name):
return acc;
case isTypeFelt(input.type):
validateFelt(parameter, input);
break;
case isTypeBytes31(input.type):
validateBytes31(parameter, input);
break;
case isTypeUint(input.type) || isTypeLiteral(input.type):
validateUint(parameter, input);
break;
case isTypeBool(input.type):
validateBool(parameter, input);
break;
case isTypeByteArray(input.type):
validateByteArray(parameter, input);
break;
case isTypeArray(input.type):
validateArray(parameter, input, structs, enums);
break;
case isTypeStruct(input.type, structs):
validateStruct(parameter, input, structs);
break;
case isTypeEnum(input.type, enums):
validateEnum(parameter, input);
break;
case isTypeTuple(input.type):
validateTuple(parameter, input);
break;
default:
throw new Error(`Validate Unhandled: argument ${input.name}, type ${input.type}, value ${parameter}`);
}
return acc + 1;
}, 0);
}