Skip to content

Commit

Permalink
feat: provider.getL1MessageHash (starknet-io#1123)
Browse files Browse the repository at this point in the history
* feat: provider.getL1MessageHash

* docs: add JSDOC example

* fix: remaining conflicts
  • Loading branch information
PhilippeR26 committed May 17, 2024
1 parent 57e4e17 commit 1489cf2
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 13 deletions.
16 changes: 16 additions & 0 deletions __tests__/rpcProvider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
describeIfDevnet,
getTestAccount,
getTestProvider,
describeIfTestnet,
waitNextBlock,
devnetETHtokenAddress,
} from './config/fixtures';
Expand Down Expand Up @@ -430,6 +431,21 @@ describeIfRpc('RPCProvider', () => {
});
});

describeIfTestnet('RPCProvider', () => {
const provider = getTestProvider();

test('getL1MessageHash', async () => {
const l2TransactionHash = '0x28dfc05eb4f261b37ddad451ff22f1d08d4e3c24dc646af0ec69fa20e096819';
const l1MessageHash = await provider.getL1MessageHash(l2TransactionHash);
expect(l1MessageHash).toBe(
'0x55b3f8b6e607fffd9b4d843dfe8f9b5c05822cd94fcad8797deb01d77805532a'
);
await expect(
provider.getL1MessageHash('0x283882a666a418cf88df04cc5f8fc2262af510bba0b637e61b2820a6ab15318')
).rejects.toThrow(/This L2 transaction is not a L1 message./);
await expect(provider.getL1MessageHash('0x123')).rejects.toThrow(/Transaction hash not found/);
});
});
describeIfNotDevnet('waitForBlock', () => {
// As Devnet-rs isn't generating automatically blocks at a periodic time, it's excluded of this test.
const providerStandard = new RpcProvider({ nodeUrl: process.env.TEST_RPC_URL });
Expand Down
25 changes: 13 additions & 12 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
},
"dependencies": {
"@noble/curves": "~1.4.0",
"@noble/hashes": "^1.4.0",
"@scure/base": "~1.1.3",
"@scure/starknet": "~1.0.0",
"abi-wan-kanabi": "^2.2.2",
Expand Down
13 changes: 13 additions & 0 deletions src/provider/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,19 @@ export abstract class ProviderInterface {
*/
public abstract getL1GasPrice(blockIdentifier: BlockIdentifier): Promise<string>;

/**
* Get L1 message hash from L2 transaction hash
* @param {BigNumberish} l2TxHash L2 transaction hash
* @returns {string} Hex string of L1 message hash
* @example
* In Sepolia Testnet :
* ```typescript
* const result = provider.getL1MessageHash('0x28dfc05eb4f261b37ddad451ff22f1d08d4e3c24dc646af0ec69fa20e096819');
* // result = '0x55b3f8b6e607fffd9b4d843dfe8f9b5c05822cd94fcad8797deb01d77805532a'
* ```
*/
public abstract getL1MessageHash(l2TxHash: BigNumberish): Promise<string>;

/**
* Returns the contract class hash in the given block for the contract deployed at the given address
*
Expand Down
30 changes: 29 additions & 1 deletion src/provider/rpc.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import type { SPEC } from 'starknet-types-07';
import { bytesToHex } from '@noble/curves/abstract/utils';
import { keccak_256 } from '@noble/hashes/sha3';
import { RPC06, RPC07, RpcChannel } from '../channel';
import {
AccountInvocations,
Expand Down Expand Up @@ -30,8 +33,11 @@ import { getAbiContractVersion } from '../utils/calldata/cairo';
import { isSierra } from '../utils/contract';
import { RPCResponseParser } from '../utils/responseParser/rpc';
import { GetTransactionReceiptResponse, ReceiptTx } from '../utils/transactionReceipt';
import type { TransactionWithHash } from '../types/provider/spec';
import assert from '../utils/assert';
import { hexToBytes, toHex } from '../utils/num';
import { addHexPrefix, removeHexPrefix } from '../utils/encode';
import { wait } from '../utils/provider';
import { toHex } from '../utils/num';
import { LibraryError } from './errors';
import { ProviderInterface } from './interface';

Expand Down Expand Up @@ -150,6 +156,28 @@ export class RpcProvider implements ProviderInterface {
.then(this.responseParser.parseL1GasPriceResponse);
}

public async getL1MessageHash(l2TxHash: BigNumberish) {
const transaction = (await this.channel.getTransactionByHash(l2TxHash)) as TransactionWithHash;
assert(transaction.type === 'L1_HANDLER', 'This L2 transaction is not a L1 message.');
const { calldata, contract_address, entry_point_selector, nonce } =
transaction as SPEC.L1_HANDLER_TXN;
const params = [
calldata[0],
contract_address,
nonce,
entry_point_selector,
calldata.length - 1,
...calldata.slice(1),
];
const myEncode = addHexPrefix(
params.reduce(
(res: string, par: BigNumberish) => res + removeHexPrefix(toHex(par)).padStart(64, '0'),
''
)
);
return addHexPrefix(bytesToHex(keccak_256(hexToBytes(myEncode))));
}

public async getBlockWithReceipts(blockIdentifier?: BlockIdentifier) {
if (this.channel instanceof RPC06.RpcChannel)
throw new LibraryError('Unsupported method for RPC version');
Expand Down

0 comments on commit 1489cf2

Please sign in to comment.