Skip to content

Commit 0b1fc77

Browse files
authored
Merge pull request #885 from starknet-io/develop-to-beta
Develop to beta
2 parents d61636c + 60fc76f commit 0b1fc77

File tree

10 files changed

+129
-28
lines changed

10 files changed

+129
-28
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
## [5.24.5](https://github.com/starknet-io/starknet.js/compare/v5.24.4...v5.24.5) (2023-12-10)
2+
3+
### Bug Fixes
4+
5+
- apply bound for contract address from hash calculation ([6d8c291](https://github.com/starknet-io/starknet.js/commit/6d8c291bce130d7b00ae6d81aff071c4986f04af))
6+
- **Calldata.compile:** do not split long `entrypoint` names before calling `getSelectorFromName` ([89715da](https://github.com/starknet-io/starknet.js/commit/89715da3fdb4b497cc5771eb83a88460007740b6))
7+
- prioritize error states in waitForTransaction evaluation ([ac54404](https://github.com/starknet-io/starknet.js/commit/ac544045e2079b68042d850a09b203fc5536c0d0))
8+
19
# [6.0.0-beta.4](https://github.com/starknet-io/starknet.js/compare/v6.0.0-beta.3...v6.0.0-beta.4) (2023-12-08)
210

311
### Bug Fixes

__tests__/config/fixtures.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,8 @@ export function getTestProvider(isProvider: boolean = true): ProviderInterface |
5757
if (process.env.IS_LOCALHOST_DEVNET === 'true') {
5858
// accelerate the tests when running locally
5959
const originalWaitForTransaction = provider.waitForTransaction.bind(provider);
60-
provider.waitForTransaction = (
61-
txHash: string,
62-
{ retryInterval }: waitForTransactionOptions = {}
63-
) => {
64-
return originalWaitForTransaction(txHash, { retryInterval: retryInterval || 1000 });
60+
provider.waitForTransaction = (txHash: string, options: waitForTransactionOptions = {}) => {
61+
return originalWaitForTransaction(txHash, { retryInterval: 1000, ...options });
6562
};
6663
}
6764

__tests__/rpcProvider.test.ts

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
import { getStarkKey, utils } from '@scure/starknet';
22

3-
import { Account, Contract, GetBlockResponse, stark } from '../src';
3+
import {
4+
Account,
5+
CallData,
6+
Contract,
7+
GetBlockResponse,
8+
RPC,
9+
TransactionExecutionStatus,
10+
stark,
11+
waitForTransactionOptions,
12+
} from '../src';
413
import { StarknetChainId } from '../src/constants';
5-
import { CallData } from '../src/utils/calldata';
614
import { felt, uint256 } from '../src/utils/calldata/cairo';
715
import { toHexString } from '../src/utils/num';
816
import {
@@ -103,6 +111,66 @@ describeIfRpc('RPCProvider', () => {
103111
});
104112
});
105113

114+
describe('waitForTransaction', () => {
115+
const receipt = {};
116+
const transactionStatusSpy = jest.spyOn(rpcProvider as any, 'getTransactionStatus');
117+
const transactionReceiptSpy = jest.spyOn(rpcProvider as any, 'getTransactionReceipt');
118+
119+
const generateOptions = (o: waitForTransactionOptions) => ({ retryInterval: 10, ...o });
120+
const generateTransactionStatus = (
121+
finality_status: RPC.SPEC.TXN_STATUS,
122+
execution_status?: RPC.SPEC.TXN_EXECUTION_STATUS
123+
): RPC.TransactionStatus => ({
124+
finality_status,
125+
execution_status,
126+
});
127+
const response = {
128+
successful: generateTransactionStatus('ACCEPTED_ON_L1', 'SUCCEEDED'),
129+
reverted: generateTransactionStatus('ACCEPTED_ON_L2', 'REVERTED'),
130+
rejected: generateTransactionStatus('REJECTED'),
131+
};
132+
133+
beforeAll(() => {
134+
transactionStatusSpy.mockResolvedValue(null);
135+
transactionReceiptSpy.mockResolvedValue(receipt);
136+
});
137+
138+
afterAll(() => {
139+
transactionStatusSpy.mockRestore();
140+
transactionReceiptSpy.mockRestore();
141+
});
142+
143+
test('successful - default', async () => {
144+
transactionStatusSpy.mockResolvedValueOnce(response.successful);
145+
await expect(rpcProvider.waitForTransaction(0)).resolves.toBe(receipt);
146+
});
147+
148+
test('reverted - default', async () => {
149+
transactionStatusSpy.mockResolvedValueOnce(response.reverted);
150+
await expect(rpcProvider.waitForTransaction(0)).resolves.toBe(receipt);
151+
});
152+
153+
test('rejected - default', async () => {
154+
transactionStatusSpy.mockResolvedValueOnce(response.rejected);
155+
await expect(rpcProvider.waitForTransaction(0)).rejects.toThrow(
156+
`${undefined}: ${RPC.ETransactionStatus.REJECTED}`
157+
);
158+
});
159+
160+
test('reverted - as error state', async () => {
161+
transactionStatusSpy.mockResolvedValueOnce(response.reverted);
162+
const options = generateOptions({ errorStates: [TransactionExecutionStatus.REVERTED] });
163+
await expect(rpcProvider.waitForTransaction(0, options)).rejects.toThrow(
164+
`${RPC.ETransactionExecutionStatus.REVERTED}: ${RPC.ETransactionStatus.ACCEPTED_ON_L2}`
165+
);
166+
});
167+
168+
test('no error state; timed-out', async () => {
169+
const options = generateOptions({ errorStates: [] });
170+
await expect(rpcProvider.waitForTransaction(0, options)).rejects.toThrow(/timed-out/);
171+
});
172+
});
173+
106174
describe('RPC methods', () => {
107175
let latestBlock: GetBlockResponse;
108176

__tests__/utils/address.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { constants, num } from '../../src';
12
import {
23
addAddressPadding,
34
getChecksumAddress,
@@ -17,6 +18,11 @@ describe('validateAndParseAddress', () => {
1718

1819
return expect(validateAndParseAddress(addr)).toEqual(`${addAddressPadding(addr)}`);
1920
});
21+
22+
test('should fail for out of bound address', () => {
23+
const addr = num.toHex(constants.ADDR_BOUND + 1n);
24+
expect(() => validateAndParseAddress(addr)).toThrow(/^Message not signable/);
25+
});
2026
});
2127

2228
describe('address checksums', () => {

__tests__/utils/utils.test.ts

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import * as starkCurve from '@scure/starknet';
2+
13
import { constants, ec, hash, num, stark } from '../../src';
24
import { Block } from '../../src/utils/provider';
35

@@ -78,18 +80,15 @@ describe('estimatedFeeToMaxFee()', () => {
7880
});
7981

8082
describe('calculateContractAddressFromHash()', () => {
81-
// This test just show how to use calculateContractAddressFromHash for new devs
82-
83+
const ethAddress = '0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7';
84+
const daiAddress = '0x03e85bfbb8e2a42b7bead9e88e9a1b19dbccf661471061807292120462396ec9';
85+
const factoryAddress = '0x249827618A01858A72B7D04339C47195A324D20D6037033DFE2829F98AFF4FC';
86+
const classHash = '0x55187E68C60664A947048E0C9E5322F9BF55F7D435ECDCF17ED75724E77368F';
87+
// Any type of salt can be used. It depends on the dApp what kind of salt it wants to use.
88+
const salt = ec.starkCurve.pedersen(ethAddress, daiAddress);
89+
90+
// This test just shows how to use calculateContractAddressFromHash for new devs
8391
test('calculated contract address should match the snapshot', () => {
84-
const ethAddress = '0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7';
85-
86-
const daiAddress = '0x03e85bfbb8e2a42b7bead9e88e9a1b19dbccf661471061807292120462396ec9';
87-
const factoryAddress = '0x249827618A01858A72B7D04339C47195A324D20D6037033DFE2829F98AFF4FC';
88-
const classHash = '0x55187E68C60664A947048E0C9E5322F9BF55F7D435ECDCF17ED75724E77368F';
89-
90-
// Any type of salt can be used. It depends on the dApp what kind of salt it wants to use.
91-
const salt = ec.starkCurve.pedersen(ethAddress, daiAddress);
92-
9392
const res = hash.calculateContractAddressFromHash(
9493
salt,
9594
classHash,
@@ -101,6 +100,20 @@ describe('calculateContractAddressFromHash()', () => {
101100
`"0x36dc8dcb3440596472ddde11facacc45d0cd250df764ae7c3d1a360c853c324"`
102101
);
103102
});
103+
104+
test('output should be bound', () => {
105+
const starkCurveSpy = jest.spyOn(starkCurve, 'pedersen');
106+
starkCurveSpy.mockReturnValue(num.toHex(constants.ADDR_BOUND + 1n));
107+
const res = hash.calculateContractAddressFromHash(
108+
salt,
109+
classHash,
110+
[ethAddress, daiAddress, factoryAddress],
111+
factoryAddress
112+
);
113+
expect(starkCurveSpy).toHaveBeenCalled();
114+
expect(BigInt(res)).toBeLessThan(constants.ADDR_BOUND);
115+
starkCurveSpy.mockRestore();
116+
});
104117
});
105118

106119
describe('new Block()', () => {

src/channel/rpc_0_6.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,9 @@ export class RpcChannel {
240240
const retryInterval = options?.retryInterval ?? 5000;
241241
const errorStates: any = options?.errorStates ?? [
242242
RPC.ETransactionStatus.REJECTED,
243-
RPC.ETransactionExecutionStatus.REVERTED,
243+
// TODO: commented out to preserve the long-standing behavior of "reverted" not being treated as an error by default
244+
// should decide which behavior to keep in the future
245+
// RPC.ETransactionExecutionStatus.REVERTED,
244246
];
245247
const successStates: any = options?.successStates ?? [
246248
RPC.ETransactionExecutionStatus.SUCCEEDED,
@@ -266,14 +268,17 @@ export class RpcChannel {
266268
throw error;
267269
}
268270

269-
if (successStates.includes(executionStatus) || successStates.includes(finalityStatus)) {
270-
onchain = true;
271-
} else if (errorStates.includes(executionStatus) || errorStates.includes(finalityStatus)) {
271+
if (errorStates.includes(executionStatus) || errorStates.includes(finalityStatus)) {
272272
const message = `${executionStatus}: ${finalityStatus}`;
273273
const error = new Error(message) as Error & { response: RPC.TransactionStatus };
274274
error.response = txStatus;
275275
isErrorState = true;
276276
throw error;
277+
} else if (
278+
successStates.includes(executionStatus) ||
279+
successStates.includes(finalityStatus)
280+
) {
281+
onchain = true;
277282
}
278283
} catch (error) {
279284
if (error instanceof Error && isErrorState) {

src/constants.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@ export { ETransactionVersion as TRANSACTION_VERSION };
1616

1717
export const ZERO = 0n;
1818
export const MASK_250 = 2n ** 250n - 1n; // 2 ** 250 - 1
19-
export const MASK_251 = 2n ** 251n;
2019
export const API_VERSION = ZERO;
2120

21+
// based on: https://github.com/starkware-libs/cairo-lang/blob/v0.12.3/src/starkware/starknet/common/storage.cairo#L3
22+
export const MAX_STORAGE_ITEM_SIZE = 256n;
23+
export const ADDR_BOUND = 2n ** 251n - MAX_STORAGE_ITEM_SIZE;
24+
2225
export enum BaseUrl {
2326
SN_MAIN = 'https://alpha-mainnet.starknet.io',
2427
SN_GOERLI = 'https://alpha4.starknet.io',

src/utils/address.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable no-bitwise */
22
import { hexToBytes } from '@noble/curves/abstract/utils';
33

4-
import { MASK_251, ZERO } from '../constants';
4+
import { ADDR_BOUND, ZERO } from '../constants';
55
import { BigNumberish } from '../types';
66
import { addHexPrefix, removeHexPrefix } from './encode';
77
import { keccakBn } from './hash';
@@ -12,7 +12,7 @@ export function addAddressPadding(address: BigNumberish): string {
1212
}
1313

1414
export function validateAndParseAddress(address: BigNumberish): string {
15-
assertInRange(address, ZERO, MASK_251, 'Starknet Address');
15+
assertInRange(address, ZERO, ADDR_BOUND - 1n, 'Starknet Address');
1616

1717
const result = addAddressPadding(address);
1818

src/utils/calldata/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,8 @@ export class CallData {
164164
const oe = Array.isArray(o) ? [o.length.toString(), ...o] : o;
165165
return Object.entries(oe).flatMap(([k, v]) => {
166166
let value = v;
167-
if (isLongText(value)) value = splitLongString(value);
168167
if (k === 'entrypoint') value = getSelectorFromName(value);
168+
else if (isLongText(value)) value = splitLongString(value);
169169
const kk = Array.isArray(oe) && k === '0' ? '$$len' : k;
170170
if (isBigInt(value)) return [[`${prefix}${kk}`, felt(value)]];
171171
if (Object(value) === value) {

src/utils/hash/classHash.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import { poseidonHashMany } from '@scure/starknet';
66

7-
import { API_VERSION } from '../../constants';
7+
import { ADDR_BOUND, API_VERSION } from '../../constants';
88
import {
99
BigNumberish,
1010
Builtins,
@@ -49,13 +49,14 @@ export function calculateContractAddressFromHash(
4949

5050
const CONTRACT_ADDRESS_PREFIX = felt('0x535441524b4e45545f434f4e54524143545f41444452455353'); // Equivalent to 'STARKNET_CONTRACT_ADDRESS'
5151

52-
return computeHashOnElements([
52+
const hash = computeHashOnElements([
5353
CONTRACT_ADDRESS_PREFIX,
5454
deployerAddress,
5555
salt,
5656
classHash,
5757
constructorCalldataHash,
5858
]);
59+
return toHex(BigInt(hash) % ADDR_BOUND);
5960
}
6061

6162
function nullSkipReplacer(key: string, value: any) {

0 commit comments

Comments
 (0)