Skip to content

Commit 1489cf2

Browse files
authored
feat: provider.getL1MessageHash (#1123)
* feat: provider.getL1MessageHash * docs: add JSDOC example * fix: remaining conflicts
1 parent 57e4e17 commit 1489cf2

File tree

5 files changed

+72
-13
lines changed

5 files changed

+72
-13
lines changed

__tests__/rpcProvider.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
describeIfDevnet,
3131
getTestAccount,
3232
getTestProvider,
33+
describeIfTestnet,
3334
waitNextBlock,
3435
devnetETHtokenAddress,
3536
} from './config/fixtures';
@@ -430,6 +431,21 @@ describeIfRpc('RPCProvider', () => {
430431
});
431432
});
432433

434+
describeIfTestnet('RPCProvider', () => {
435+
const provider = getTestProvider();
436+
437+
test('getL1MessageHash', async () => {
438+
const l2TransactionHash = '0x28dfc05eb4f261b37ddad451ff22f1d08d4e3c24dc646af0ec69fa20e096819';
439+
const l1MessageHash = await provider.getL1MessageHash(l2TransactionHash);
440+
expect(l1MessageHash).toBe(
441+
'0x55b3f8b6e607fffd9b4d843dfe8f9b5c05822cd94fcad8797deb01d77805532a'
442+
);
443+
await expect(
444+
provider.getL1MessageHash('0x283882a666a418cf88df04cc5f8fc2262af510bba0b637e61b2820a6ab15318')
445+
).rejects.toThrow(/This L2 transaction is not a L1 message./);
446+
await expect(provider.getL1MessageHash('0x123')).rejects.toThrow(/Transaction hash not found/);
447+
});
448+
});
433449
describeIfNotDevnet('waitForBlock', () => {
434450
// As Devnet-rs isn't generating automatically blocks at a periodic time, it's excluded of this test.
435451
const providerStandard = new RpcProvider({ nodeUrl: process.env.TEST_RPC_URL });

package-lock.json

Lines changed: 13 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@
9595
},
9696
"dependencies": {
9797
"@noble/curves": "~1.4.0",
98+
"@noble/hashes": "^1.4.0",
9899
"@scure/base": "~1.1.3",
99100
"@scure/starknet": "~1.0.0",
100101
"abi-wan-kanabi": "^2.2.2",

src/provider/interface.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,19 @@ export abstract class ProviderInterface {
8686
*/
8787
public abstract getL1GasPrice(blockIdentifier: BlockIdentifier): Promise<string>;
8888

89+
/**
90+
* Get L1 message hash from L2 transaction hash
91+
* @param {BigNumberish} l2TxHash L2 transaction hash
92+
* @returns {string} Hex string of L1 message hash
93+
* @example
94+
* In Sepolia Testnet :
95+
* ```typescript
96+
* const result = provider.getL1MessageHash('0x28dfc05eb4f261b37ddad451ff22f1d08d4e3c24dc646af0ec69fa20e096819');
97+
* // result = '0x55b3f8b6e607fffd9b4d843dfe8f9b5c05822cd94fcad8797deb01d77805532a'
98+
* ```
99+
*/
100+
public abstract getL1MessageHash(l2TxHash: BigNumberish): Promise<string>;
101+
89102
/**
90103
* Returns the contract class hash in the given block for the contract deployed at the given address
91104
*

src/provider/rpc.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import type { SPEC } from 'starknet-types-07';
2+
import { bytesToHex } from '@noble/curves/abstract/utils';
3+
import { keccak_256 } from '@noble/hashes/sha3';
14
import { RPC06, RPC07, RpcChannel } from '../channel';
25
import {
36
AccountInvocations,
@@ -30,8 +33,11 @@ import { getAbiContractVersion } from '../utils/calldata/cairo';
3033
import { isSierra } from '../utils/contract';
3134
import { RPCResponseParser } from '../utils/responseParser/rpc';
3235
import { GetTransactionReceiptResponse, ReceiptTx } from '../utils/transactionReceipt';
36+
import type { TransactionWithHash } from '../types/provider/spec';
37+
import assert from '../utils/assert';
38+
import { hexToBytes, toHex } from '../utils/num';
39+
import { addHexPrefix, removeHexPrefix } from '../utils/encode';
3340
import { wait } from '../utils/provider';
34-
import { toHex } from '../utils/num';
3541
import { LibraryError } from './errors';
3642
import { ProviderInterface } from './interface';
3743

@@ -150,6 +156,28 @@ export class RpcProvider implements ProviderInterface {
150156
.then(this.responseParser.parseL1GasPriceResponse);
151157
}
152158

159+
public async getL1MessageHash(l2TxHash: BigNumberish) {
160+
const transaction = (await this.channel.getTransactionByHash(l2TxHash)) as TransactionWithHash;
161+
assert(transaction.type === 'L1_HANDLER', 'This L2 transaction is not a L1 message.');
162+
const { calldata, contract_address, entry_point_selector, nonce } =
163+
transaction as SPEC.L1_HANDLER_TXN;
164+
const params = [
165+
calldata[0],
166+
contract_address,
167+
nonce,
168+
entry_point_selector,
169+
calldata.length - 1,
170+
...calldata.slice(1),
171+
];
172+
const myEncode = addHexPrefix(
173+
params.reduce(
174+
(res: string, par: BigNumberish) => res + removeHexPrefix(toHex(par)).padStart(64, '0'),
175+
''
176+
)
177+
);
178+
return addHexPrefix(bytesToHex(keccak_256(hexToBytes(myEncode))));
179+
}
180+
153181
public async getBlockWithReceipts(blockIdentifier?: BlockIdentifier) {
154182
if (this.channel instanceof RPC06.RpcChannel)
155183
throw new LibraryError('Unsupported method for RPC version');

0 commit comments

Comments
 (0)