From f707e161a439c13a987bfde401f2ee3973ee45b5 Mon Sep 17 00:00:00 2001 From: Mikhail Sherstennikov Date: Thu, 18 Jul 2024 15:28:29 +0300 Subject: [PATCH 1/3] remove gas from messages + change on fee credit #100 --- .changeset/loud-phones-happen.md | 5 + examples/bounce.ts | 2 +- examples/deployInternalMessage.ts | 2 +- examples/syncCall.ts | 2 +- examples/tokenMint.ts | 4 +- src/clients/PublicClient.test.ts | 200 +++++++++++++++++++++--- src/clients/PublicClient.ts | 22 +-- src/clients/types/ISendMessage.ts | 1 - src/contracts/WalletV1/WalletV1.test.ts | 2 +- src/contracts/WalletV1/WalletV1.ts | 12 +- src/contracts/WalletV1/types/index.ts | 4 +- src/integrations/bounce.test.ts | 4 +- src/integrations/calling.test.ts | 4 +- src/integrations/deploy.test.ts | 3 +- src/integrations/tokens.test.ts | 7 +- src/types/ProcessedMessage.ts | 1 - src/types/RPCMessage.ts | 1 - test/vitest.integration.config.ts | 2 +- 18 files changed, 221 insertions(+), 57 deletions(-) create mode 100644 .changeset/loud-phones-happen.md diff --git a/.changeset/loud-phones-happen.md b/.changeset/loud-phones-happen.md new file mode 100644 index 00000000..5e004c61 --- /dev/null +++ b/.changeset/loud-phones-happen.md @@ -0,0 +1,5 @@ +--- +"@nilfoundation/niljs": minor +--- + +Breaking changes! Fee credit instead of gas diff --git a/examples/bounce.ts b/examples/bounce.ts index 49694f08..70af0f6b 100644 --- a/examples/bounce.ts +++ b/examples/bounce.ts @@ -67,7 +67,7 @@ const hash = await wallet.sendMessage({ to: anotherWallet.getAddressHex(), value: 10_000_000n, bounceTo: bounceAddress, - gas: 100_000n, + gas: 100_000n * 10n, data: encodeFunctionData({ abi: WalletV1.abi, functionName: "syncCall", diff --git a/examples/deployInternalMessage.ts b/examples/deployInternalMessage.ts index 60ebfbdf..080b3ad1 100644 --- a/examples/deployInternalMessage.ts +++ b/examples/deployInternalMessage.ts @@ -61,7 +61,7 @@ const { address, hash } = await wallet.deployContract({ abi: abi, args: [bytesToHex(pubkey), walletAddress], value: 10000000n, - gas: 1000000n, + gas: 1000000n * 10n, salt: 400n, shardId: 1, }); diff --git a/examples/syncCall.ts b/examples/syncCall.ts index d767bb5d..cc4137ec 100644 --- a/examples/syncCall.ts +++ b/examples/syncCall.ts @@ -51,7 +51,7 @@ const anotherAddress = WalletV1.calculateWalletAddress({ await wallet.syncSendMessage({ to: anotherAddress, value: 10n, - gas: 100_000n, + gas: 100_000n * 10n, }); while (true) { diff --git a/examples/tokenMint.ts b/examples/tokenMint.ts index 1468e17d..792509cb 100644 --- a/examples/tokenMint.ts +++ b/examples/tokenMint.ts @@ -46,7 +46,7 @@ console.log("walletAddress", walletAddress); const hashMessage = await wallet.sendMessage({ to: MINTER_ADDRESS, - gas: 1_000_000n, + gas: 1_000_000n * 10n, value: 100_000_000n, data: encodeFunctionData({ abi: MINTER_ABI, @@ -72,7 +72,7 @@ const anotherAddress = WalletV1.calculateWalletAddress({ const sendHash = await wallet.sendMessage({ to: anotherAddress, value: 10_000_000n, - gas: 100_000n, + gas: 100_000n * 10n, tokens: [ { id: n, diff --git a/src/clients/PublicClient.test.ts b/src/clients/PublicClient.test.ts index cd9842cd..b646bf96 100644 --- a/src/clients/PublicClient.test.ts +++ b/src/clients/PublicClient.test.ts @@ -1,102 +1,249 @@ import { defaultAddress } from "../../test/mocks/address.js"; -import { testEnv } from "../../test/testEnv.js"; -import { HttpTransport, addHexPrefix } from "../index.js"; +import { addHexPrefix } from "../index.js"; +import { MockTransport } from "../transport/MockTransport.js"; import { PublicClient } from "./PublicClient.js"; -const client = new PublicClient({ - transport: new HttpTransport({ - endpoint: testEnv.endpoint, - }), - shardId: 1, -}); - test("getBlockByHash", async ({ expect }) => { - const block = await client.getBlockByHash( + const fn = vi.fn(); + fn.mockReturnValue({}); + const client = new PublicClient({ + transport: new MockTransport(fn), + shardId: 1, + }); + await client.getBlockByHash( "0x158c4be17b52b92dc03cef7e8cd9cec64c6413175df3cce9f6ae1fb0d12106fa", ); + expect(fn).toHaveBeenCalledOnce(); - expect(block).toBeDefined(); + expect(fn).toHaveBeenLastCalledWith({ + method: "eth_getBlockByHash", + params: [ + 1, + "0x158c4be17b52b92dc03cef7e8cd9cec64c6413175df3cce9f6ae1fb0d12106fa", + false, + ], + }); }); test("getBlockByNumber", async ({ expect }) => { - const block = await client.getBlockByNumber("0x1b4"); - - expect(block).toBeDefined(); + const fn = vi.fn(); + fn.mockReturnValue({}); + const client = new PublicClient({ + transport: new MockTransport(fn), + shardId: 1, + }); + await client.getBlockByNumber("0x1b4"); + expect(fn).toHaveBeenCalledOnce(); + expect(fn).toHaveBeenLastCalledWith({ + method: "eth_getBlockByNumber", + params: [1, "0x1b4", false], + }); }); test("getBlockMessageCountByNumber", async ({ expect }) => { + const fn = vi.fn(); + fn.mockReturnValue(1); + const client = new PublicClient({ + transport: new MockTransport(fn), + shardId: 1, + }); const count = await client.getBlockMessageCountByNumber("0x1b4", 1); expect(count).toBeDefined(); + expect(fn).toHaveBeenCalledOnce(); + expect(fn).toHaveBeenLastCalledWith({ + method: "eth_getBlockTransactionCountByNumber", + params: [1, "0x1b4"], + }); }); test("getBlockMessageCountByHash", async ({ expect }) => { + const fn = vi.fn(); + fn.mockReturnValue(1); + const client = new PublicClient({ + transport: new MockTransport(fn), + shardId: 1, + }); const count = await client.getBlockMessageCountByHash( "0x158c4be17b52b92dc03cef7e8cd9cec64c6413175df3cce9f6ae1fb0d12106fa", ); expect(count).toBeDefined(); + expect(fn).toHaveBeenCalledOnce(); + expect(fn).toHaveBeenLastCalledWith({ + method: "eth_getBlockTransactionCountByHash", + params: [ + 1, + "0x158c4be17b52b92dc03cef7e8cd9cec64c6413175df3cce9f6ae1fb0d12106fa", + ], + }); }); test("getCode", async ({ expect }) => { - const code = await client.getCode(addHexPrefix(defaultAddress), "0x1b4"); + const fn = vi.fn(); + fn.mockReturnValue("0x"); + const client = new PublicClient({ + transport: new MockTransport(fn), + shardId: 1, + }); + const code = await client.getCode(addHexPrefix(defaultAddress), "latest"); - expect(code).toBeDefined(); + expect(code).toHaveLength(0); + expect(fn).toHaveBeenCalledOnce(); + expect(fn).toHaveBeenLastCalledWith({ + method: "eth_getCode", + params: [addHexPrefix(defaultAddress), "latest"], + }); }); test("getMessageCount", async ({ expect }) => { + const fn = vi.fn(); + fn.mockReturnValue("0x1"); + const client = new PublicClient({ + transport: new MockTransport(fn), + shardId: 1, + }); const count = await client.getMessageCount( addHexPrefix(defaultAddress), "latest", ); - - expect(count).toBeDefined(); + expect(count).toBe(1); + expect(fn).toHaveBeenCalledOnce(); + expect(fn).toHaveBeenLastCalledWith({ + method: "eth_getTransactionCount", + params: [addHexPrefix(defaultAddress), "latest"], + }); }); test("getBalance", async ({ expect }) => { - const balance = await client.getBalance( - addHexPrefix(defaultAddress), - "latest", - ); + const fn = vi.fn(); + fn.mockReturnValue("0x100"); + const client = new PublicClient({ + transport: new MockTransport(fn), + shardId: 1, + }); + const balance = await client.getBalance(addHexPrefix(defaultAddress)); expect(balance).toBeDefined(); + expect(balance).toBe(256n); + expect(fn).toHaveBeenCalledOnce(); + expect(fn).toHaveBeenLastCalledWith({ + method: "eth_getBalance", + params: [addHexPrefix(defaultAddress), "latest"], + }); + + await client.getBalance(addHexPrefix(defaultAddress), "pending"); + + expect(fn).toHaveBeenLastCalledWith({ + method: "eth_getBalance", + params: [addHexPrefix(defaultAddress), "pending"], + }); }); test("getMessageByHash", async ({ expect }) => { + const fn = vi.fn(); + fn.mockReturnValue({ + value: "0x100", + gasLimit: 100, + gasUsed: "0x100", + seqno: "0x100", + index: "0x0", + }); + const client = new PublicClient({ + transport: new MockTransport(fn), + shardId: 1, + }); const message = await client.getMessageByHash( "0x158c4be17b52b92dc03cef7e8cd9cec64c6413175df3cce9f6ae1fb0d12106fa", ); expect(message).toBeDefined(); + expect(fn).toHaveBeenCalledOnce(); + expect(fn).toHaveBeenLastCalledWith({ + method: "eth_getInMessageByHash", + params: [ + 1, + "0x158c4be17b52b92dc03cef7e8cd9cec64c6413175df3cce9f6ae1fb0d12106fa", + ], + }); }); test("getMessageReceiptByHash", async ({ expect }) => { + const fn = vi.fn(); + fn.mockReturnValue({}); + const client = new PublicClient({ + transport: new MockTransport(fn), + shardId: 1, + }); const receipt = await client.getMessageReceiptByHash( "0x158c4be17b52b92dc03cef7e8cd9cec64c6413175df3cce9f6ae1fb0d12106fa", ); expect(receipt).toBeDefined(); + expect(fn).toHaveBeenCalledOnce(); + expect(fn).toHaveBeenLastCalledWith({ + method: "eth_getInMessageReceipt", + params: [ + 1, + "0x158c4be17b52b92dc03cef7e8cd9cec64c6413175df3cce9f6ae1fb0d12106fa", + ], + }); }); test("getGasPrice", async ({ expect }) => { - const gasPrice = await client.getGasPrice(); - + const fn = vi.fn(); + fn.mockReturnValue("0x100"); + const client = new PublicClient({ + transport: new MockTransport(fn), + shardId: 1, + }); + const gasPrice = await client.getGasPrice(1); expect(gasPrice).toBeDefined(); + expect(gasPrice).toBe(256n); + expect(fn).toHaveBeenCalledOnce(); + expect(fn).toHaveBeenLastCalledWith({ + method: "eth_gasPrice", + params: [1], + }); }); test("estimateGasLimit", async ({ expect }) => { + const fn = vi.fn(); + fn.mockReturnValue("0x100"); + const client = new PublicClient({ + transport: new MockTransport(fn), + shardId: 1, + }); const gasLimit = await client.estimateGasLimit(); expect(gasLimit).toBeDefined(); }); test("chainId", async ({ expect }) => { + const fn = vi.fn(); + fn.mockReturnValue("0x1"); + const client = new PublicClient({ + transport: new MockTransport(fn), + shardId: 1, + }); const chainId = await client.chainId(); expect(chainId).toBeDefined(); + expect(chainId).toBe(1); + expect(fn).toHaveBeenCalledOnce(); + expect(fn).toHaveBeenLastCalledWith({ + method: "eth_chainId", + params: [], + }); }); test("getCurrencies", async ({ expect }) => { + const fn = vi.fn(); + fn.mockReturnValue({}); + const client = new PublicClient({ + transport: new MockTransport(fn), + shardId: 1, + }); const currencies = await client.getCurrencies( addHexPrefix(defaultAddress), "latest", @@ -104,4 +251,9 @@ test("getCurrencies", async ({ expect }) => { expect(currencies).toBeDefined(); expect(currencies).toBeInstanceOf(Object); + expect(fn).toHaveBeenCalledOnce(); + expect(fn).toHaveBeenLastCalledWith({ + method: "eth_getCurrencies", + params: [addHexPrefix(defaultAddress), "latest"], + }); }); diff --git a/src/clients/PublicClient.ts b/src/clients/PublicClient.ts index e55dcb20..60844e89 100644 --- a/src/clients/PublicClient.ts +++ b/src/clients/PublicClient.ts @@ -184,10 +184,10 @@ class PublicClient extends BaseClient { * * const code = await client.getCode(Uint8Array.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]), 'latest'); */ - public async getCode(address: IAddress, blockNumberOrHash: Hex | BlockTag) { + public async getCode(address: IAddress, blockNumberOrHash?: Hex | BlockTag) { const res = await this.request<`0x${string}`>({ method: "eth_getCode", - params: [address, blockNumberOrHash], + params: [address, blockNumberOrHash ?? "latest"], }); return hexToBytes(res); @@ -210,11 +210,11 @@ class PublicClient extends BaseClient { */ public async getMessageCount( address: IAddress, - blockNumberOrHash: Hex | BlockTag, + blockNumberOrHash?: Hex | BlockTag, ) { const res = await this.request({ method: "eth_getTransactionCount", - params: [address, blockNumberOrHash], + params: [address, blockNumberOrHash ?? "latest"], }); return hexToNumber(res); @@ -236,11 +236,11 @@ class PublicClient extends BaseClient { */ public async getBalance( address: IAddress, - blockNumberOrHash: Hex | BlockTag, + blockNumberOrHash?: Hex | BlockTag, ) { const res = await this.request<`0x${string}`>({ method: "eth_getBalance", - params: [addHexPrefix(address), blockNumberOrHash], + params: [addHexPrefix(address), blockNumberOrHash ?? "latest"], }); return hexToBigInt(res); @@ -276,7 +276,6 @@ class PublicClient extends BaseClient { value: BigInt(res.value), gasLimit: BigInt(res.gasLimit), gasUsed: hexToBigInt(res.gasUsed), - gasPrice: BigInt(res.gasPrice), seqno: hexToBigInt(res.seqno), index: res.index ? hexToNumber(res.index) : 0, }; @@ -342,10 +341,13 @@ class PublicClient extends BaseClient { * Returns the gas price in wei. * @returns The gas price. */ - public async getGasPrice(): Promise { - const stubGasPrice = BigInt(1); + public async getGasPrice(shardId: number): Promise { + const price = await this.request<`0x${string}`>({ + method: "eth_gasPrice", + params: [shardId], + }); - return stubGasPrice; + return hexToBigInt(price); } /** diff --git a/src/clients/types/ISendMessage.ts b/src/clients/types/ISendMessage.ts index 2c577dfd..3124c6f8 100644 --- a/src/clients/types/ISendMessage.ts +++ b/src/clients/types/ISendMessage.ts @@ -6,7 +6,6 @@ type ISendMessage = { value: bigint; from?: string; seqno?: number; - gasPrice?: bigint; gasLimit?: bigint; data?: Uint8Array; }; diff --git a/src/contracts/WalletV1/WalletV1.test.ts b/src/contracts/WalletV1/WalletV1.test.ts index fe27b636..3eed5784 100644 --- a/src/contracts/WalletV1/WalletV1.test.ts +++ b/src/contracts/WalletV1/WalletV1.test.ts @@ -121,7 +121,7 @@ test("Deploy through wallet", async ({ expect }) => { salt: 100n, shardId: 1, value: 100n, - gas: 100_000n, + gas: 100_000n * 10n, }); expect(fn.mock.calls).toHaveLength(1); expect(fn.mock.calls[0][0].method).toBe("eth_sendRawTransaction"); diff --git a/src/contracts/WalletV1/WalletV1.ts b/src/contracts/WalletV1/WalletV1.ts index a3d41b42..26be1e19 100644 --- a/src/contracts/WalletV1/WalletV1.ts +++ b/src/contracts/WalletV1/WalletV1.ts @@ -315,7 +315,7 @@ export class WalletV1 { * @param {SendMessageParams} param0.data The message bytecode. * @param {SendMessageParams} param0.deploy The flag that determines whether the message is a deploy message. * @param {SendMessageParams} param0.seqno The message sequence number. - * @param {SendMessageParams} param0.gas The message gas. + * @param {SendMessageParams} param0.feeCredit The message fee credit for processing message on receiving shard. * @param {SendMessageParams} param0.value The message value. * @param {SendMessageParams} param0.chainId The message chain id. * @returns {unknown} The message hash. @@ -338,7 +338,7 @@ export class WalletV1 { data, deploy, seqno, - gas, + feeCredit, value, tokens, chainId, @@ -359,7 +359,7 @@ export class WalletV1 { hexTo, hexRefundTo, hexBounceTo, - gas, + feeCredit, !!deploy, tokens ?? [], value ?? 0n, @@ -404,7 +404,7 @@ export class WalletV1 { * @param {DeployParams} param0.args The arbitrary arguments for deployment. * @param {DeployParams} param0.salt The arbitrary data for changing the contract address. * @param {DeployParams} param0.value The deployment message value. - * @param {DeployParams} param0.gas The deployment message gas. + * @param {DeployParams} param0.feeCredit The deployment message fee credit. * @param {DeployParams} param0.seqno The deployment message seqno. * @param {DeployParams} param0.chainId The deployment message chain id. * @returns {unknown} The object containing the deployment message hash and the contract address. @@ -416,7 +416,7 @@ export class WalletV1 { args, salt, value, - gas, + feeCredit, seqno, chainId, }: DeployParams) { @@ -449,7 +449,7 @@ export class WalletV1 { data, value: value ?? 0n, deploy: true, - gas, + feeCredit, seqno, chainId, }); diff --git a/src/contracts/WalletV1/types/index.ts b/src/contracts/WalletV1/types/index.ts index 38fc159d..f9032cd5 100644 --- a/src/contracts/WalletV1/types/index.ts +++ b/src/contracts/WalletV1/types/index.ts @@ -50,7 +50,7 @@ export type SendMessageParams = { bounceTo?: Address | Uint8Array; data?: Uint8Array | Hex; value?: bigint; - gas: bigint; + feeCredit: bigint; tokens?: Token[]; deploy?: boolean; seqno?: number; @@ -93,7 +93,7 @@ export type DeployParams = { args?: unknown[]; salt: Uint8Array | bigint; shardId: number; - gas: bigint; + feeCredit: bigint; value?: bigint; seqno?: number; chainId?: number; diff --git a/src/integrations/bounce.test.ts b/src/integrations/bounce.test.ts index b329e042..4094f700 100644 --- a/src/integrations/bounce.test.ts +++ b/src/integrations/bounce.test.ts @@ -24,6 +24,8 @@ test("bounce", async () => { privateKey: generateRandomPrivateKey(), }); + const gasPrice = await client.getGasPrice(1); + const pubkey = await signer.getPublicKey(); const wallet = new WalletV1({ @@ -64,7 +66,7 @@ test("bounce", async () => { to: anotherWallet.getAddressHex(), value: 10_000_000n, bounceTo: bounceAddress, - gas: 100_000n, + feeCredit: 100_000n * gasPrice, data: encodeFunctionData({ abi: WalletV1.abi, functionName: "syncCall", diff --git a/src/integrations/calling.test.ts b/src/integrations/calling.test.ts index 5ba7760f..32afb807 100644 --- a/src/integrations/calling.test.ts +++ b/src/integrations/calling.test.ts @@ -47,10 +47,12 @@ test("Async call to another shard send value", async () => { salt: 200n, }); + const gasPriceOnShard2 = await client.getGasPrice(2); + const hash = await wallet.sendMessage({ to: anotherAddress, value: 50_000_000n, - gas: 100_000n, + feeCredit: 100_000n * gasPriceOnShard2, }); const receipts = await waitTillCompleted(client, 1, hash); diff --git a/src/integrations/deploy.test.ts b/src/integrations/deploy.test.ts index 9357f6e9..b78a64fd 100644 --- a/src/integrations/deploy.test.ts +++ b/src/integrations/deploy.test.ts @@ -50,6 +50,7 @@ test("Deploy through wallet", async ({ expect }) => { const walletCode = await client.getCode(walletAddress, "latest"); expect(walletCode).toBeDefined(); expect(walletCode.length).toBeGreaterThan(10); + const gasPrice = await client.getGasPrice(1); const abi = [ { @@ -67,7 +68,7 @@ test("Deploy through wallet", async ({ expect }) => { abi: abi, args: [bytesToHex(pubkey), walletAddress], value: 10000000n, - gas: 1000000n, + feeCredit: 1000000n * gasPrice, salt: 400n, shardId: 1, }); diff --git a/src/integrations/tokens.test.ts b/src/integrations/tokens.test.ts index 796039f6..96a6473f 100644 --- a/src/integrations/tokens.test.ts +++ b/src/integrations/tokens.test.ts @@ -25,6 +25,9 @@ const client = new PublicClient({ test("mint and transfer tokens", async () => { const faucet = new Faucet(client); + const gasPriceOnShard1 = await client.getGasPrice(1); + const gasPriceOnShard2 = await client.getGasPrice(2); + const signer = new LocalECDSAKeySigner({ privateKey: generateRandomPrivateKey(), }); @@ -47,7 +50,7 @@ test("mint and transfer tokens", async () => { const hashMessage = await wallet.sendMessage({ to: MINTER_ADDRESS, - gas: 1_000_000n, + feeCredit: 1_000_000n * gasPriceOnShard1, value: 100_000_000n, data: encodeFunctionData({ abi: MINTER_ABI, @@ -78,7 +81,7 @@ test("mint and transfer tokens", async () => { const sendHash = await wallet.sendMessage({ to: anotherAddress, value: 10_000_000n, - gas: 100_000n, + feeCredit: 100_000n * gasPriceOnShard2, tokens: [ { id: n, diff --git a/src/types/ProcessedMessage.ts b/src/types/ProcessedMessage.ts index da15faea..281de4db 100644 --- a/src/types/ProcessedMessage.ts +++ b/src/types/ProcessedMessage.ts @@ -8,7 +8,6 @@ export type ProcessedMessage = { blockNumber: number; from: Address; gasUsed: bigint; - gasPrice: bigint; gasLimit: bigint; hash: Hex; seqno: bigint; diff --git a/src/types/RPCMessage.ts b/src/types/RPCMessage.ts index 5036d4f1..7a2c137a 100644 --- a/src/types/RPCMessage.ts +++ b/src/types/RPCMessage.ts @@ -7,7 +7,6 @@ export type RPCMessage = { blockNumber: number; from: Hex; gasUsed: Hex; - gasPrice: string; gasLimit: string; hash: Hex; seqno: Hex; diff --git a/test/vitest.integration.config.ts b/test/vitest.integration.config.ts index 1c37a264..0a7578d6 100644 --- a/test/vitest.integration.config.ts +++ b/test/vitest.integration.config.ts @@ -6,7 +6,7 @@ export default defineConfig({ environment: "node", include: ["src/integrations/*.test.ts"], hookTimeout: 20_000, - testTimeout: 20_000, + testTimeout: 40_000, globals: true, coverage: { reportsDirectory: "./test/coverage", From 05bef227d07120db100ac2ccccaf22e25845c302 Mon Sep 17 00:00:00 2001 From: klond90 Date: Thu, 18 Jul 2024 18:48:56 +0400 Subject: [PATCH 2/3] add test on receipt with new version #100 --- src/clients/PublicClient.ts | 27 ++++++++++++++-- src/integrations/receipt.test.ts | 53 ++++++++++++++++++++++++++++++++ src/types/IReceipt.ts | 20 +++++++++--- src/utils/receipt.ts | 4 +-- 4 files changed, 94 insertions(+), 10 deletions(-) create mode 100644 src/integrations/receipt.test.ts diff --git a/src/clients/PublicClient.ts b/src/clients/PublicClient.ts index 60844e89..be891a7a 100644 --- a/src/clients/PublicClient.ts +++ b/src/clients/PublicClient.ts @@ -10,7 +10,7 @@ import { type Hex, assertIsValidShardId } from "../index.js"; import type { IAddress } from "../signers/types/IAddress.js"; import type { Block, BlockTag } from "../types/Block.js"; import type { CallArgs } from "../types/CallArgs.js"; -import type { IReceipt } from "../types/IReceipt.js"; +import type { IReceipt, ProcessedReceipt } from "../types/IReceipt.js"; import type { ProcessedMessage } from "../types/ProcessedMessage.js"; import type { RPCMessage } from "../types/RPCMessage.js"; import { addHexPrefix } from "../utils/hex.js"; @@ -295,8 +295,25 @@ class PublicClient extends BaseClient { * * const receipt = await client.getMessageReceiptByHash(1, Uint8Array.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])); */ - public async getMessageReceiptByHash(hash: Hex, shardId = this.shardId) { + public async getMessageReceiptByHash( + hash: Hex, + shardId = this.shardId, + ): Promise { assertIsValidShardId(shardId); + const mapReceipt = (receipt: IReceipt): ProcessedReceipt => { + return { + ...receipt, + gasUsed: BigInt(receipt.gasUsed), + gasPrice: BigInt(receipt.gasPrice), + outputReceipts: + receipt.outputReceipts?.map((x) => { + if (x === null) { + return null; + } + return mapReceipt(x); + }) ?? null, + }; + }; const res = await this.request({ method: "eth_getInMessageReceipt", @@ -308,7 +325,11 @@ class PublicClient extends BaseClient { ], }); - return res; + if (res === null) { + return null; + } + + return mapReceipt(res); } /** diff --git a/src/integrations/receipt.test.ts b/src/integrations/receipt.test.ts new file mode 100644 index 00000000..0c255993 --- /dev/null +++ b/src/integrations/receipt.test.ts @@ -0,0 +1,53 @@ +import { testEnv } from "../../test/testEnv.js"; +import { Faucet, WalletV1 } from "../contracts/index.js"; +import { + HttpTransport, + PublicClient, + convertEthToWei, + waitTillCompleted, +} from "../index.js"; +import { LocalECDSAKeySigner } from "../signers/LocalECDSAKeySigner.js"; +import { generateRandomPrivateKey } from "../signers/privateKey.js"; + +const client = new PublicClient({ + transport: new HttpTransport({ + endpoint: testEnv.endpoint, + }), + shardId: 1, +}); + +test("Receipt test", async ({ expect }) => { + const faucet = new Faucet(client); + + const signer = new LocalECDSAKeySigner({ + privateKey: generateRandomPrivateKey(), + }); + + const pubkey = await signer.getPublicKey(); + + const wallet = new WalletV1({ + pubkey: pubkey, + salt: 100n, + shardId: 1, + client, + signer, + }); + const walletAddress = await wallet.getAddressHex(); + + expect(walletAddress).toBeDefined(); + + const faucetHash = await faucet.withdrawToWithRetry( + walletAddress, + convertEthToWei(0.1), + ); + + const receipts = await waitTillCompleted(client, 1, faucetHash); + + expect(receipts).toBeDefined(); + for (const receipt of receipts) { + expect(receipt).toBeDefined(); + expect(receipt.gasPrice).toBeDefined(); + expect(receipt.gasUsed).toBeDefined(); + expect(receipt.gasPrice).toBeTypeOf("bigint"); + } +}); diff --git a/src/types/IReceipt.ts b/src/types/IReceipt.ts index f07bb813..ba6952e7 100644 --- a/src/types/IReceipt.ts +++ b/src/types/IReceipt.ts @@ -6,17 +6,27 @@ import type { ILog } from "./ILog.js"; */ type IReceipt = { success: boolean; - gasUsed: number; + gasUsed: string; + gasPrice: string; bloom: string; logs: ILog[]; messageHash: Hex; contractAddress: string; blockHash: string; - blockNumber: bigint; - msgIndex: bigint; + blockNumber: number; + msgIndex: number; outMessages: Hex[] | null; - outputReceipts: IReceipt[] | null; + outputReceipts: (IReceipt | null)[] | null; shardId: number; }; -export type { IReceipt }; +type ProcessedReceipt = Omit< + IReceipt, + "gasUsed" | "gasPrice" | "outputReceipts" +> & { + gasUsed: bigint; + gasPrice: bigint; + outputReceipts: (ProcessedReceipt | null)[] | null; +}; + +export type { IReceipt, ProcessedReceipt }; diff --git a/src/utils/receipt.ts b/src/utils/receipt.ts index 35cf4409..3bea611f 100644 --- a/src/utils/receipt.ts +++ b/src/utils/receipt.ts @@ -1,6 +1,6 @@ import type { PublicClient } from "../clients/PublicClient.js"; import type { Hex } from "../types/Hex.js"; -import type { IReceipt } from "../types/IReceipt.js"; +import type { ProcessedReceipt } from "../types/IReceipt.js"; /** * Makes it so that the client waits until the processing of the message whose hash is passed. @@ -18,7 +18,7 @@ export const waitTillCompleted = async ( shardId: number, hash: Hex, ) => { - const receipts: IReceipt[] = []; + const receipts: ProcessedReceipt[] = []; const hashes: [number, Hex][] = [[shardId, hash]]; let cur = 0; while (cur !== hashes.length) { From 76226e8d14295132ce6547db672e27ebd7be946d Mon Sep 17 00:00:00 2001 From: klond90 Date: Thu, 18 Jul 2024 18:54:17 +0400 Subject: [PATCH 3/3] remove unused #100 --- src/clients/types/IDeployContractData.ts | 11 ----- src/clients/types/IDeployContractOption.ts | 10 ---- src/clients/types/ISendMessage.ts | 13 ------ src/contracts/Faucet/Faucet.ts | 3 +- src/utils/assert.ts | 54 ---------------------- src/utils/receipt.ts | 2 +- 6 files changed, 2 insertions(+), 91 deletions(-) delete mode 100644 src/clients/types/IDeployContractData.ts delete mode 100644 src/clients/types/IDeployContractOption.ts delete mode 100644 src/clients/types/ISendMessage.ts diff --git a/src/clients/types/IDeployContractData.ts b/src/clients/types/IDeployContractData.ts deleted file mode 100644 index 87531f50..00000000 --- a/src/clients/types/IDeployContractData.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { IDeployData } from "./IDeployData.js"; -import type { ISendMessage } from "./ISendMessage.js"; - -/** - * The data needed to send a deploy contract message. - */ -type IDeployContractData = { - deployData: IDeployData; -} & Omit; - -export type { IDeployContractData }; diff --git a/src/clients/types/IDeployContractOption.ts b/src/clients/types/IDeployContractOption.ts deleted file mode 100644 index 97245a1d..00000000 --- a/src/clients/types/IDeployContractOption.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { IMessage } from "../../index.js"; -import type { IDeployData } from "../../types/IDeployData.js"; - -/** - * The option for deploying a contract. - */ -type IDeployContractOption = IDeployData & - Pick; - -export type { IDeployContractOption }; diff --git a/src/clients/types/ISendMessage.ts b/src/clients/types/ISendMessage.ts deleted file mode 100644 index 3124c6f8..00000000 --- a/src/clients/types/ISendMessage.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * The data structure for the send message request. - */ -type ISendMessage = { - to: string; - value: bigint; - from?: string; - seqno?: number; - gasLimit?: bigint; - data?: Uint8Array; -}; - -export type { ISendMessage }; diff --git a/src/contracts/Faucet/Faucet.ts b/src/contracts/Faucet/Faucet.ts index 356d0a5d..8eb9c1ba 100644 --- a/src/contracts/Faucet/Faucet.ts +++ b/src/contracts/Faucet/Faucet.ts @@ -2,7 +2,6 @@ import { type Hex, bytesToHex, encodeFunctionData } from "viem"; import type { PublicClient } from "../../clients/PublicClient.js"; import { ExternalMessageEnvelope } from "../../encoding/externalMessage.js"; import { hexToBytes } from "../../index.js"; -import type { IReceipt } from "../../types/IReceipt.js"; import { getShardIdFromAddress } from "../../utils/address.js"; import { waitTillCompleted } from "../../utils/receipt.js"; import FaucetAbi from "./Faucet.abi.json"; @@ -109,7 +108,7 @@ export class Faucet { const encodedMessage = message.encode(); await this.client.sendRawMessage(bytesToHex(encodedMessage)); const hash = bytesToHex(message.hash()); - const receipts: IReceipt[] = await Promise.race([ + const receipts = await Promise.race([ new Promise<[]>((resolve) => setTimeout(() => resolve([]), 10000)), waitTillCompleted( this.client, diff --git a/src/utils/assert.ts b/src/utils/assert.ts index 6f75c446..12dd7ac0 100644 --- a/src/utils/assert.ts +++ b/src/utils/assert.ts @@ -1,7 +1,6 @@ import invariant from "tiny-invariant"; import { masterShardId } from "../clients/constants.js"; import type { IDeployData } from "../clients/types/IDeployData.js"; -import type { ISendMessage } from "../clients/types/ISendMessage.js"; import { type Hex, InvalidShardIdError } from "../index.js"; import type { IPrivateKey } from "../signers/index.js"; import type { Block } from "../types/Block.js"; @@ -51,58 +50,6 @@ const assertIsValidPrivateKey = ( ); }; -/** - * Checks if the data to send message is valid. If the message is valid, it returns nothing. - * @throws Will throw an error if the value is not a valid data to send message. - * @param sendMessage - The data to validate. - * @param message - The message to throw if the data is invalid. - */ -const assertIsValidSendMessageData = ( - sendMessage: ISendMessage, - message?: string, -) => { - const { gasPrice, gasLimit, to, from, seqno, value } = sendMessage; - invariant( - typeof to === "string" && isAddress(to), - message ?? `Expected a valid 'to' address but got ${to}`, - ); - - invariant( - typeof value === "bigint" && value >= 0, - message ?? `Expected a valid 'value' but got ${value}`, - ); - - if (from !== undefined) { - invariant( - typeof from === "string" && isAddress(from), - message ?? `Expected a valid 'from' address but got ${from}`, - ); - } - - if (gasPrice !== undefined) { - invariant( - (typeof gasPrice === "number" || typeof gasPrice === "bigint") && - gasPrice > 0, - message ?? `Expected a valid 'gasPrice' but got ${gasPrice}`, - ); - } - - if (gasLimit !== undefined) { - invariant( - (typeof gasLimit === "number" || typeof gasLimit === "bigint") && - gasLimit > 0, - message ?? `Expected a valid 'gasLimit' but got ${gasLimit}`, - ); - } - - if (seqno !== undefined) { - invariant( - seqno >= 0, - message ?? `Expected a valid 'seqno' but got ${seqno}`, - ); - } -}; - /** * Checks if the data to deploy contract is valid. If the data is valid, it returns nothing. * @throws Will throw an error if the value is not a valid data to deploy contract. @@ -177,7 +124,6 @@ export { assertIsBuffer, assertIsHexString, assertIsValidPrivateKey, - assertIsValidSendMessageData, assertIsAddress, assertIsValidBlock, assertIsValidShardId, diff --git a/src/utils/receipt.ts b/src/utils/receipt.ts index 3bea611f..170caac7 100644 --- a/src/utils/receipt.ts +++ b/src/utils/receipt.ts @@ -38,7 +38,7 @@ export const waitTillCompleted = async ( receipts.push(receipt); if (receipt.outputReceipts) { for (const r of receipt.outputReceipts) { - hashes.push([r.shardId, r.messageHash]); + if (r !== null) hashes.push([r.shardId, r.messageHash]); } } }