Skip to content

Commit 7a8ee04

Browse files
authored
Merge pull request #940 from starknet-io/fix/negative-uint-param
Feat CairoUint256
2 parents 40c0b33 + 3c45968 commit 7a8ee04

File tree

11 files changed

+412
-89
lines changed

11 files changed

+412
-89
lines changed
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
/* eslint-disable no-new */
2+
import { Uint256 } from '../../../src';
3+
import {
4+
CairoUint256,
5+
UINT_256_HIGH_MAX,
6+
UINT_256_HIGH_MIN,
7+
UINT_256_LOW_MAX,
8+
UINT_256_LOW_MIN,
9+
UINT_256_MAX,
10+
UINT_256_MIN,
11+
} from '../../../src/utils/cairoDataTypes/uint256';
12+
13+
describe('CairoUint256 class test', () => {
14+
test('constructor 1 should throw on < UINT_256_MIN', () => {
15+
expect(() => {
16+
new CairoUint256(UINT_256_MIN - 1n);
17+
}).toThrow('bigNumberish is smaller than UINT_256_MIN');
18+
});
19+
20+
test('constructor 1 should throw on > UINT_256_MAX', () => {
21+
expect(() => {
22+
new CairoUint256(UINT_256_MAX + 1n);
23+
}).toThrow('bigNumberish is bigger than UINT_256_MAX');
24+
});
25+
26+
test('constructor 2 (low, high)', () => {
27+
const u256 = new CairoUint256(1000, 1000);
28+
expect(u256.toApiRequest()).toEqual(['1000', '1000']);
29+
});
30+
31+
test('constructor 2 should throw out of bounds', () => {
32+
expect(() => {
33+
new CairoUint256(UINT_256_LOW_MIN - 1n, 1000);
34+
}).toThrow('low is out of range UINT_256_LOW_MIN - UINT_256_LOW_MAX');
35+
});
36+
37+
test('constructor 2 should throw out of bounds', () => {
38+
expect(() => {
39+
new CairoUint256(UINT_256_LOW_MAX + 1n, 1000);
40+
}).toThrow('low is out of range UINT_256_LOW_MIN - UINT_256_LOW_MAX');
41+
});
42+
43+
test('constructor 2 should throw out of bounds', () => {
44+
expect(() => {
45+
new CairoUint256(1000, UINT_256_HIGH_MIN - 1n);
46+
}).toThrow('high is out of range UINT_256_HIGH_MIN - UINT_256_HIGH_MAX');
47+
});
48+
49+
test('constructor 2 should throw out of bounds', () => {
50+
expect(() => {
51+
new CairoUint256(1000, UINT_256_HIGH_MAX + 1n);
52+
}).toThrow('high is out of range UINT_256_HIGH_MIN - UINT_256_HIGH_MAX');
53+
});
54+
55+
test('constructor 3 ({low, high})', () => {
56+
const u256 = new CairoUint256({ low: 1000, high: 1000 });
57+
expect(u256.toApiRequest()).toEqual(['1000', '1000']);
58+
});
59+
60+
test('constructor 3 should throw out of bounds', () => {
61+
expect(() => {
62+
new CairoUint256({ low: 1000, high: UINT_256_HIGH_MAX + 1n });
63+
}).toThrow('high is out of range UINT_256_HIGH_MIN - UINT_256_HIGH_MAX');
64+
});
65+
66+
test('validate should throw on < UINT_256_MIN', () => {
67+
expect(() => {
68+
CairoUint256.validate(UINT_256_MIN - 1n);
69+
}).toThrow('bigNumberish is smaller than UINT_256_MIN');
70+
});
71+
72+
test('validate should throw on > UINT_256_MAX', () => {
73+
expect(() => {
74+
CairoUint256.validate(UINT_256_MAX + 1n);
75+
}).toThrow('bigNumberish is bigger than UINT_256_MAX');
76+
});
77+
78+
test('validate should pass and return bigint', () => {
79+
const validate = CairoUint256.validate(UINT_256_MAX);
80+
expect(typeof validate).toBe('bigint');
81+
});
82+
83+
test('is should return true', () => {
84+
const is = CairoUint256.is(UINT_256_MIN);
85+
expect(is).toBe(true);
86+
});
87+
88+
test('is should return false', () => {
89+
const is = CairoUint256.is(UINT_256_MAX + 1n);
90+
expect(is).toBe(false);
91+
});
92+
93+
test('constructor 1 should support BigNumberish', () => {
94+
const case1 = new CairoUint256(10n);
95+
const case2 = new CairoUint256(10);
96+
const case3 = new CairoUint256('10');
97+
const case4 = new CairoUint256('0xA');
98+
99+
expect(case1).toEqual(case2);
100+
expect(case3).toEqual(case4);
101+
expect(case1).toEqual(case4);
102+
});
103+
104+
test('constructor 2 should support Uint256 {low, high}', () => {
105+
const cases: Uint256[] = [];
106+
cases[cases.length] = new CairoUint256({ low: 0, high: 0 });
107+
cases[cases.length] = new CairoUint256({ low: '0', high: '0' });
108+
cases[cases.length] = new CairoUint256({ low: 0n, high: 0n });
109+
cases[cases.length] = new CairoUint256({ low: '0x0', high: '0x0' });
110+
111+
const cases2: Uint256[] = [];
112+
cases2[cases2.length] = new CairoUint256({ low: 10000, high: 10000 });
113+
cases2[cases2.length] = new CairoUint256({ low: '10000', high: '10000' });
114+
cases2[cases2.length] = new CairoUint256({ low: 10000n, high: 10000n });
115+
cases2[cases2.length] = new CairoUint256({ low: '0x2710', high: '0x2710' });
116+
117+
expect(
118+
cases.every((it) => {
119+
return it.low === 0n && it.high === 0n;
120+
})
121+
).toEqual(true);
122+
123+
expect(
124+
cases2.every((it) => {
125+
return it.low === 10000n && it.high === 10000n;
126+
})
127+
).toEqual(true);
128+
});
129+
130+
test('should convert UINT_256_MAX to Uint256 dec struct', () => {
131+
const u256 = new CairoUint256(UINT_256_MAX);
132+
const u256Hex = u256.toUint256DecimalString();
133+
expect(u256Hex).toMatchInlineSnapshot(`
134+
Object {
135+
"high": "340282366920938463463374607431768211455",
136+
"low": "340282366920938463463374607431768211455",
137+
}
138+
`);
139+
});
140+
141+
test('should convert UINT_256_MAX to Uint256 hex struct', () => {
142+
const u256 = new CairoUint256(UINT_256_MAX);
143+
const u256Decimal = u256.toUint256HexString();
144+
expect(u256Decimal).toMatchInlineSnapshot(`
145+
Object {
146+
"high": "0xffffffffffffffffffffffffffffffff",
147+
"low": "0xffffffffffffffffffffffffffffffff",
148+
}
149+
`);
150+
});
151+
152+
test('isAbiType should return true', () => {
153+
const isAbiType = CairoUint256.isAbiType('core::integer::u256');
154+
expect(isAbiType).toBe(true);
155+
});
156+
157+
test('should convert UINT_256_MAX to BN', () => {
158+
const u256 = new CairoUint256(UINT_256_MAX);
159+
expect(u256.toBigInt()).toEqual(UINT_256_MAX);
160+
});
161+
162+
test('should convert UINT_256_MAX to API Request', () => {
163+
const u256 = new CairoUint256(UINT_256_MAX);
164+
expect(u256.toApiRequest()).toEqual([
165+
'340282366920938463463374607431768211455',
166+
'340282366920938463463374607431768211455',
167+
]);
168+
});
169+
});

__tests__/utils/uint256.test.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,30 @@
1-
import { UINT_128_MAX, UINT_256_MAX, bnToUint256, uint256ToBN } from '../../src/utils/uint256';
1+
import { cairo, uint256 as u256 } from '../../src';
2+
3+
const { bnToUint256, UINT_128_MAX, UINT_256_MAX, uint256ToBN } = u256;
24

35
describe('cairo uint256', () => {
6+
test('bnToUint256 should not convert -1 from BN to uint256 hex-string struct', () => {
7+
expect(() => {
8+
u256.bnToUint256(-1n);
9+
}).toThrow('bigNumberish is smaller than UINT_256_MIN');
10+
});
11+
12+
test('uint256 should not convert -1 to uint256 dec struct', () => {
13+
expect(() => {
14+
cairo.uint256(-1n);
15+
}).toThrow('bigNumberish is smaller than UINT_256_MIN');
16+
});
17+
18+
test('uint256 should convert 1000 to uint256 dec struct', () => {
19+
const uint256 = cairo.uint256(1000n);
20+
expect(uint256).toMatchInlineSnapshot(`
21+
Object {
22+
"high": "0",
23+
"low": "1000",
24+
}
25+
`);
26+
});
27+
428
test('should convert 0 from BN to uint256 struct', () => {
529
const uint256 = bnToUint256(0n);
630
expect(uint256).toMatchInlineSnapshot(`
@@ -98,7 +122,7 @@ describe('cairo uint256', () => {
98122

99123
test('should throw if BN over uint256 range', () => {
100124
expect(() => bnToUint256(UINT_256_MAX + 1n)).toThrowErrorMatchingInlineSnapshot(
101-
`"Number is too large"`
125+
`"bigNumberish is bigger than UINT_256_MAX"`
102126
);
103127
});
104128
});

package-lock.json

Lines changed: 7 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/utils/cairoDataTypes/felt.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// TODO Convert to CairoFelt base on CairoUint256 and implement it in the codebase in the backward compatible manner
2+
3+
import { BigNumberish, isBigInt, isHex, isStringWholeNumber } from '../num';
4+
import { encodeShortString, isShortString, isText } from '../shortString';
5+
6+
/**
7+
* Create felt Cairo type (cairo type helper)
8+
* @returns format: felt-string
9+
*/
10+
export function CairoFelt(it: BigNumberish): string {
11+
// BN or number
12+
if (isBigInt(it) || (typeof it === 'number' && Number.isInteger(it))) {
13+
return it.toString();
14+
}
15+
// string text
16+
if (isText(it)) {
17+
if (!isShortString(it as string))
18+
throw new Error(
19+
`${it} is a long string > 31 chars, felt can store short strings, split it to array of short strings`
20+
);
21+
const encoded = encodeShortString(it as string);
22+
return BigInt(encoded).toString();
23+
}
24+
// hex string
25+
if (typeof it === 'string' && isHex(it)) {
26+
// toBN().toString
27+
return BigInt(it).toString();
28+
}
29+
// string number (already converted), or unhandled type
30+
if (typeof it === 'string' && isStringWholeNumber(it)) {
31+
return it;
32+
}
33+
// bool to felt
34+
if (typeof it === 'boolean') {
35+
return `${+it}`;
36+
}
37+
38+
throw new Error(`${it} can't be computed by felt()`);
39+
}

0 commit comments

Comments
 (0)