-
Notifications
You must be signed in to change notification settings - Fork 757
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: new CairoUint256 cairo datatype model
- Loading branch information
Showing
10 changed files
with
354 additions
and
88 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
/* eslint-disable no-new */ | ||
import { | ||
CairoUint256, | ||
UINT_256_HIGH_MAX, | ||
UINT_256_HIGH_MIN, | ||
UINT_256_LOW_MAX, | ||
UINT_256_LOW_MIN, | ||
UINT_256_MAX, | ||
UINT_256_MIN, | ||
} from '../../../src/utils/cairoDataTypes/uint256'; | ||
|
||
describe('CairoUint256 class test', () => { | ||
test('constructor 1 should throw on < UINT_256_MIN', () => { | ||
expect(() => { | ||
new CairoUint256(UINT_256_MIN - 1n); | ||
}).toThrow('bigNumberish is smaller than UINT_256_MIN'); | ||
}); | ||
|
||
test('constructor 1 should throw on > UINT_256_MAX', () => { | ||
expect(() => { | ||
new CairoUint256(UINT_256_MAX + 1n); | ||
}).toThrow('bigNumberish is bigger than UINT_256_MAX'); | ||
}); | ||
|
||
test('constructor 2 (low, high)', () => { | ||
const u256 = new CairoUint256(1000, 1000); | ||
expect(u256.toApiRequest()).toEqual(['1000', '1000']); | ||
}); | ||
|
||
test('constructor 2 should throw out of bounds', () => { | ||
expect(() => { | ||
new CairoUint256(UINT_256_LOW_MIN - 1n, 1000); | ||
}).toThrow('low is our of range UINT_256_LOW_MIN - UINT_256_LOW_MAX'); | ||
}); | ||
|
||
test('constructor 2 should throw out of bounds', () => { | ||
expect(() => { | ||
new CairoUint256(UINT_256_LOW_MAX + 1n, 1000); | ||
}).toThrow('low is our of range UINT_256_LOW_MIN - UINT_256_LOW_MAX'); | ||
}); | ||
|
||
test('constructor 2 should throw out of bounds', () => { | ||
expect(() => { | ||
new CairoUint256(1000, UINT_256_HIGH_MIN - 1n); | ||
}).toThrow('high is our of range UINT_256_HIGH_MIN - UINT_256_HIGH_MAX'); | ||
}); | ||
|
||
test('constructor 2 should throw out of bounds', () => { | ||
expect(() => { | ||
new CairoUint256(1000, UINT_256_HIGH_MAX + 1n); | ||
}).toThrow('high is our of range UINT_256_HIGH_MIN - UINT_256_HIGH_MAX'); | ||
}); | ||
|
||
test('constructor 3 ({low, high})', () => { | ||
const u256 = new CairoUint256({ low: 1000, high: 1000 }); | ||
expect(u256.toApiRequest()).toEqual(['1000', '1000']); | ||
}); | ||
|
||
test('constructor 3 should throw out of bounds', () => { | ||
expect(() => { | ||
new CairoUint256({ low: 1000, high: UINT_256_HIGH_MAX + 1n }); | ||
}).toThrow('high is our of range UINT_256_HIGH_MIN - UINT_256_HIGH_MAX'); | ||
}); | ||
|
||
test('validate should throw on < UINT_256_MIN', () => { | ||
expect(() => { | ||
CairoUint256.validate(UINT_256_MIN - 1n); | ||
}).toThrow('bigNumberish is smaller than UINT_256_MIN'); | ||
}); | ||
|
||
test('validate should throw on > UINT_256_MAX', () => { | ||
expect(() => { | ||
CairoUint256.validate(UINT_256_MAX + 1n); | ||
}).toThrow('bigNumberish is bigger than UINT_256_MAX'); | ||
}); | ||
|
||
test('validate should pass and return bigint', () => { | ||
const validate = CairoUint256.validate(UINT_256_MAX); | ||
expect(typeof validate).toBe('bigint'); | ||
}); | ||
|
||
test('is should return true', () => { | ||
const is = CairoUint256.is(UINT_256_MIN); | ||
expect(is).toBe(true); | ||
}); | ||
|
||
test('is should return false', () => { | ||
const is = CairoUint256.is(UINT_256_MAX + 1n); | ||
expect(is).toBe(false); | ||
}); | ||
|
||
test('constructor 1 should support BigNumberish', () => { | ||
const case1 = new CairoUint256(10n); | ||
const case2 = new CairoUint256(10); | ||
const case3 = new CairoUint256('10'); | ||
const case4 = new CairoUint256('0xA'); | ||
|
||
expect(case1).toEqual(case2); | ||
expect(case3).toEqual(case4); | ||
expect(case1).toEqual(case4); | ||
}); | ||
|
||
test('should convert UINT_256_MAX to Uint256 dec struct', () => { | ||
const u256 = new CairoUint256(UINT_256_MAX); | ||
const u256Hex = u256.toUint256DecimalString(); | ||
expect(u256Hex).toMatchInlineSnapshot(` | ||
Object { | ||
"high": "340282366920938463463374607431768211455", | ||
"low": "340282366920938463463374607431768211455", | ||
} | ||
`); | ||
}); | ||
|
||
test('should convert UINT_256_MAX to Uint256 hex struct', () => { | ||
const u256 = new CairoUint256(UINT_256_MAX); | ||
const u256Decimal = u256.toUint256HexString(); | ||
expect(u256Decimal).toMatchInlineSnapshot(` | ||
Object { | ||
"high": "0xffffffffffffffffffffffffffffffff", | ||
"low": "0xffffffffffffffffffffffffffffffff", | ||
} | ||
`); | ||
}); | ||
|
||
test('isAbiType should return true', () => { | ||
const isAbiType = CairoUint256.isAbiType('core::integer::u256'); | ||
expect(isAbiType).toBe(true); | ||
}); | ||
|
||
test('should convert UINT_256_MAX to BN', () => { | ||
const u256 = new CairoUint256(UINT_256_MAX); | ||
expect(u256.toBigInt()).toEqual(UINT_256_MAX); | ||
}); | ||
|
||
test('should convert UINT_256_MAX to API Request', () => { | ||
const u256 = new CairoUint256(UINT_256_MAX); | ||
expect(u256.toApiRequest()).toEqual([ | ||
'340282366920938463463374607431768211455', | ||
'340282366920938463463374607431768211455', | ||
]); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// TODO Convert to CairoFelt base on CairoUint256 and implement it in the codebase in the backward compatible manner | ||
|
||
import { BigNumberish, isBigInt, isHex, isStringWholeNumber } from '../num'; | ||
import { encodeShortString, isShortString, isText } from '../shortString'; | ||
|
||
/** | ||
* Create felt Cairo type (cairo type helper) | ||
* @returns format: felt-string | ||
*/ | ||
export function CairoFelt(it: BigNumberish): string { | ||
// BN or number | ||
if (isBigInt(it) || (typeof it === 'number' && Number.isInteger(it))) { | ||
return it.toString(); | ||
} | ||
// string text | ||
if (isText(it)) { | ||
if (!isShortString(it as string)) | ||
throw new Error( | ||
`${it} is a long string > 31 chars, felt can store short strings, split it to array of short strings` | ||
); | ||
const encoded = encodeShortString(it as string); | ||
return BigInt(encoded).toString(); | ||
} | ||
// hex string | ||
if (typeof it === 'string' && isHex(it)) { | ||
// toBN().toString | ||
return BigInt(it).toString(); | ||
} | ||
// string number (already converted), or unhandled type | ||
if (typeof it === 'string' && isStringWholeNumber(it)) { | ||
return it; | ||
} | ||
// bool to felt | ||
if (typeof it === 'boolean') { | ||
return `${+it}`; | ||
} | ||
|
||
throw new Error(`${it} can't be computed by felt()`); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
/* eslint-disable no-bitwise */ | ||
/** | ||
* Singular class handling cairo u256 data type | ||
*/ | ||
|
||
import { BigNumberish, Uint256 } from '../../types'; | ||
import { addHexPrefix } from '../encode'; | ||
import { CairoFelt } from './felt'; | ||
|
||
export const UINT_128_MAX = (1n << 128n) - 1n; | ||
export const UINT_256_MAX = (1n << 256n) - 1n; | ||
export const UINT_256_MIN = 0n; | ||
export const UINT_256_LOW_MAX = 340282366920938463463374607431768211455n; | ||
export const UINT_256_HIGH_MAX = 340282366920938463463374607431768211455n; | ||
export const UINT_256_LOW_MIN = 0n; | ||
export const UINT_256_HIGH_MIN = 0n; | ||
|
||
export class CairoUint256 { | ||
public low: bigint; | ||
|
||
public high: bigint; | ||
|
||
static abiSelector = 'core::integer::u256'; | ||
|
||
/** | ||
* Default constructor (Lib usage) | ||
* @param bigNumberish BigNumberish value representing uin256 | ||
*/ | ||
public constructor(bigNumberish: BigNumberish); | ||
/** | ||
* Direct props initialization (Api response) | ||
*/ | ||
public constructor(low: BigNumberish, high: BigNumberish); | ||
/** | ||
* Initialization from Uint256 object | ||
*/ | ||
public constructor(uint256: Uint256); | ||
|
||
public constructor(...arr: any[]) { | ||
if (typeof arr === 'object' && arr.length === 1 && arr[0].low && arr[0].high) { | ||
const props = CairoUint256.validateProps(arr[0].low, arr[0].high); | ||
this.low = props.low; | ||
this.high = props.high; | ||
} else if (arr.length === 1) { | ||
const bigInt = CairoUint256.validate(arr[0]); | ||
this.low = bigInt & UINT_128_MAX; | ||
this.high = bigInt >> 128n; | ||
} else if (arr.length === 2) { | ||
const props = CairoUint256.validateProps(arr[0], arr[1]); | ||
this.low = props.low; | ||
this.high = props.high; | ||
} else { | ||
throw Error('Incorrect constructor parameters'); | ||
} | ||
} | ||
|
||
/** | ||
* Validate if BigNumberish can be represented as Unit256 | ||
*/ | ||
static validate(bigNumberish: BigNumberish) { | ||
const bigInt = BigInt(bigNumberish); | ||
if (bigInt < UINT_256_MIN) throw Error('bigNumberish is smaller than UINT_256_MIN'); | ||
if (bigInt > UINT_256_MAX) throw new Error('bigNumberish is bigger than UINT_256_MAX'); | ||
return bigInt; | ||
} | ||
|
||
static validateProps(low: BigNumberish, high: BigNumberish) { | ||
const bigIntLow = BigInt(low); | ||
const bigIntHigh = BigInt(high); | ||
if (bigIntLow < UINT_256_LOW_MIN || bigIntLow > UINT_256_LOW_MAX) { | ||
throw new Error('low is our of range UINT_256_LOW_MIN - UINT_256_LOW_MAX'); | ||
} | ||
if (bigIntHigh < UINT_256_HIGH_MIN || bigIntHigh > UINT_256_HIGH_MAX) { | ||
throw new Error('high is our of range UINT_256_HIGH_MIN - UINT_256_HIGH_MAX'); | ||
} | ||
return { low: bigIntLow, high: bigIntHigh }; | ||
} | ||
|
||
/** | ||
* Check if BigNumberish can be represented as Unit256 | ||
*/ | ||
static is(bigNumberish: BigNumberish) { | ||
try { | ||
CairoUint256.validate(bigNumberish); | ||
} catch (error) { | ||
return false; | ||
} | ||
return true; | ||
} | ||
|
||
/** | ||
* Check if provided abi type is this data type | ||
*/ | ||
static isAbiType(abiType: string) { | ||
return abiType === CairoUint256.abiSelector; | ||
} | ||
|
||
/** | ||
* Return bigint representation | ||
*/ | ||
toBigInt() { | ||
return (this.high << 128n) + this.low; | ||
} | ||
|
||
/** | ||
* Return Uint256 structure with HexString props | ||
* {low: HexString, high: HexString} | ||
*/ | ||
toUint256HexString() { | ||
return { | ||
low: addHexPrefix(this.low.toString(16)), | ||
high: addHexPrefix(this.high.toString(16)), | ||
}; | ||
} | ||
|
||
/** | ||
* Return Uint256 structure with DecimalString props | ||
* {low: DecString, high: DecString} | ||
*/ | ||
toUint256DecimalString() { | ||
return { | ||
low: this.low.toString(10), | ||
high: this.high.toString(10), | ||
}; | ||
} | ||
|
||
/** | ||
* Return api requests representation witch is felt array | ||
*/ | ||
toApiRequest() { | ||
return [CairoFelt(this.low), CairoFelt(this.high)]; | ||
} | ||
} |
Oops, something went wrong.