diff --git a/.changeset/eighty-weeks-sort.md b/.changeset/eighty-weeks-sort.md new file mode 100644 index 00000000..8a7f1560 --- /dev/null +++ b/.changeset/eighty-weeks-sort.md @@ -0,0 +1,5 @@ +--- +"op-viem": patch +--- + +writeUnsafeDepositTransaction -> writeDepositTransaction diff --git a/site/.vitepress/config.mts b/site/.vitepress/config.mts index 396fd205..ac03fff8 100644 --- a/site/.vitepress/config.mts +++ b/site/.vitepress/config.mts @@ -126,8 +126,8 @@ export default defineConfig({ link: '/docs/actions/wallet/L1/writeSendMessage', }, { - text: 'writeUnsafeDepositTransaction', - link: '/docs/actions/wallet/L1/writeUnsafeDepositTransaction', + text: 'writeDepositTransaction', + link: '/docs/actions/wallet/L1/writeDepositTransaction', }, ], }, diff --git a/site/docs/actions/wallet/L1/writeUnsafeDepositTransaction.md b/site/docs/actions/wallet/L1/writeDepositTransaction.md similarity index 83% rename from site/docs/actions/wallet/L1/writeUnsafeDepositTransaction.md rename to site/docs/actions/wallet/L1/writeDepositTransaction.md index 24b87e34..42833f3f 100644 --- a/site/docs/actions/wallet/L1/writeUnsafeDepositTransaction.md +++ b/site/docs/actions/wallet/L1/writeDepositTransaction.md @@ -1,12 +1,10 @@ -# writeUnsafeDepositTransaction +# writeDepositTransaction Excutes a [depositTransaction](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L1/OptimismPortal.sol#L374) call to the [`OptimismPortal`](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L1/OptimismPortal.sol) contract. -::: danger +Unlike [writeSendMessage](docs/actions/wallet/L1/writeSendMessage), using this call does not offer replayability on L2 in the case the L2 tx fails. But this call has the advantage that, if the caller is an EOA, msg.sender of the L2 tx will be the caller address. Allowing users to fully tranasact on L2 from L1, which is a critical security property. -Interacting directly the portal offers no replayability. For example, if you are sending ETH and your L2 transaction fails––because the gas limit is too low or something else goes wrong––your ETH will be in the `OptimismPortal` on L1 but you'll have nothing on L2: i.e. your ETH will be stuck indefinitely. You can read more about replays here and deposit transactions [here](https://community.optimism.io/docs/protocol/deposit-flow/#replaying-messages). - -::: +If the caller is not an EOA, e.g. if the caller is a smart contract wallet, msg.sender on L2 will be [alias](https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L1/OptimismPortal.sol#L407) of the caller address. ::: warning @@ -42,7 +40,7 @@ const gas = await l2PublicClient.estimateGas({ args.gasLimit = gas -const hash = await opStackL1WalletClient.writeUnsafeDepositTransaction({ +const hash = await opStackL1WalletClient.writeDepositTransaction({ args, l2Chain: base, value: 1n, @@ -110,7 +108,7 @@ A [Transaction Hash](https://viem.sh/docs/glossary/terms#hash). - The calldata of the L2 transaction ```ts -await walletClient.writeUnsafeDepositTransaction({ +await walletClient.writeDepositTransaction({ args: { // [!code focus:7] to: account.address, value: 1n, @@ -129,7 +127,7 @@ await walletClient.writeUnsafeDepositTransaction({ The destination L2 chain of the deposit transaction. `l2Chain.opStackConfig.l1.chainId` must match `chain.id` (from `client.chain` or `chain` passed explicitly as an arg). The address at `l2Chain.opStackConfig.l1.contracts.optimismPortal.address` will be used for the contract call. If this is argument not passed or if no such contract definition exists, [optimismPortalAddress](#optimismPortalAddress) must be passed explicitly. ```ts -await walletClient.writeUnsafeDepositTransaction({ +await walletClient.writeDepositTransaction({ args, l2Chain: base, // [!code focus:1] }) @@ -142,7 +140,7 @@ await walletClient.writeUnsafeDepositTransaction({ The `OptimismPortal` contract where the depositTransaction call should be made. ```ts -await walletClient.writeUnsafeDepositTransaction({ +await walletClient.writeDepositTransaction({ args, optimismPortalAddress: portal, // [!code focus:1] }) @@ -155,7 +153,7 @@ await walletClient.writeUnsafeDepositTransaction({ Value in wei sent with this transaction. This value will be credited to the balance of the caller address on L2 _before_ the L2 transaction created by this transaction is made. ```ts -await walletClient.writeUnsafeDepositTransaction({ +await walletClient.writeDepositTransaction({ args, optimismPortalAddress: portal, value: parseEther(1), // [!code focus:1] diff --git a/site/docs/actions/wallet/L1/writeSendMessage.md b/site/docs/actions/wallet/L1/writeSendMessage.md index 7ed75bda..c11e48b5 100644 --- a/site/docs/actions/wallet/L1/writeSendMessage.md +++ b/site/docs/actions/wallet/L1/writeSendMessage.md @@ -120,7 +120,7 @@ await walletClient.writeSendMessage({ Value in wei sent with this transaction. This value will be credited to the balance of the caller address on L2 _before_ the L2 transaction created by this transaction is made. ```ts -await walletClient.writeUnsafeDepositTransaction({ +await walletClient.writeDepositTransaction({ args, optimismPortalAddress: portal, value: parseEther(1), // [!code focus:1] diff --git a/src/_test/live.test.ts b/src/_test/live.test.ts index 43530910..7841acd1 100644 --- a/src/_test/live.test.ts +++ b/src/_test/live.test.ts @@ -3,7 +3,7 @@ import { privateKeyToAccount } from 'viem/accounts' import { estimateGas } from 'viem/actions' import { goerli } from 'viem/chains' import { test } from 'vitest' -import type { DepositTransactionParameters } from '../actions/wallet/L1/writeUnsafeDepositTransaction.js' +import type { DepositTransactionParameters } from '../actions/wallet/L1/writeDepositTransaction.js' import { baseGoerli } from '../chains/baseGoerli.js' import { publicL1OpStackActions } from '../decorators/publicL1OpStackActions.js' import { walletL1OpStackActions } from '../decorators/walletL1OpStackActions.js' @@ -45,7 +45,7 @@ test('correctly retrieves L2 hash', async () => { args.gasLimit = gas - const depositHash = await walletClient.writeUnsafeDepositTransaction({ + const depositHash = await walletClient.writeDepositTransaction({ l2Chain: baseGoerli, args, value: 1n, diff --git a/src/actions/index.ts b/src/actions/index.ts index 1e1b57fa..3b10c12e 100644 --- a/src/actions/index.ts +++ b/src/actions/index.ts @@ -59,6 +59,11 @@ export { } from './public/L2/simulateWithdrawETH.js' export { writeDepositERC20, type WriteDepositERC20Parameters } from './wallet/L1/writeDepositERC20.js' export { writeDepositETH, type WriteDepositETHParameters } from './wallet/L1/writeDepositETH.js' +export { + type DepositTransactionParameters, + writeDepositTransaction, + type WriteDepositTransactionParameters, +} from './wallet/L1/writeDepositTransaction.js' export { writeFinalizeWithdrawalTranasction, type WriteFinalizeWithdrawalTransactionParameters, @@ -73,11 +78,6 @@ export { writeSendMessage, type WriteSendMessageParameters, } from './wallet/L1/writeSendMessage.js' -export { - type DepositTransactionParameters, - writeUnsafeDepositTransaction, - type WriteUnsafeDepositTransactionParameters, -} from './wallet/L1/writeUnsafeDepositTransaction.js' export { writeOpStackL2, type WriteOpStackL2Parameters } from './wallet/L2/writeOpStackL2.js' export { writeWithdrawERC20, type WriteWithdrawERC20Parameters } from './wallet/L2/writeWithdrawERC20.js' export { writeWithdrawETH, type WriteWithdrawETHParameters } from './wallet/L2/writeWithdrawETH.js' diff --git a/src/actions/wallet/L1/writeUnsafeDepositTransaction.test.ts b/src/actions/wallet/L1/writeDepositTransaction.test.ts similarity index 89% rename from src/actions/wallet/L1/writeUnsafeDepositTransaction.test.ts rename to src/actions/wallet/L1/writeDepositTransaction.test.ts index bb10a1b7..8ae6cc67 100644 --- a/src/actions/wallet/L1/writeUnsafeDepositTransaction.test.ts +++ b/src/actions/wallet/L1/writeDepositTransaction.test.ts @@ -7,11 +7,11 @@ import { publicClient, rollupPublicClient, rollupWalletClient, testClient, walle import { base } from '../../../chains/index.js' import { type TransactionDepositedEvent } from '../../../types/depositTransaction.js' import type { OpStackChain } from '../../../types/opStackChain.js' -import { type DepositTransactionParameters, writeUnsafeDepositTransaction } from './writeUnsafeDepositTransaction.js' +import { type DepositTransactionParameters, writeDepositTransaction } from './writeDepositTransaction.js' test('default', async () => { expect( - await writeUnsafeDepositTransaction(walletClient, { + await writeDepositTransaction(walletClient, { args: { to: '0x0c54fccd2e384b4bb6f2e405bf5cbc15a017aafb', value: 1n, @@ -44,7 +44,7 @@ test('sends transaction to correct infered address', async () => { args.gasLimit = gas - const hash = await writeUnsafeDepositTransaction(walletClient, { + const hash = await writeDepositTransaction(walletClient, { args, value: 1n, l2Chain: base, @@ -61,7 +61,7 @@ test('sends transaction to correct infered address', async () => { test('sends transaction to correct explicit address', async () => { const portal: Address = '0xbEb5Fc579115071764c7423A4f12eDde41f106Ed' - const hash = await writeUnsafeDepositTransaction(walletClient, { + const hash = await writeDepositTransaction(walletClient, { args: { to: '0x0c54fccd2e384b4bb6f2e405bf5cbc15a017aafb', value: 1n, @@ -86,7 +86,7 @@ test('creates correct deposit transaction', async () => { data: '0x', isCreation: false, } - const hash = await writeUnsafeDepositTransaction(walletClient, { + const hash = await writeDepositTransaction(walletClient, { args, value: args.value!, l2Chain: base, @@ -122,7 +122,7 @@ test('correctly passes arugments', async () => { isCreation: false, } - const hash = await writeUnsafeDepositTransaction(walletClient, { + const hash = await writeDepositTransaction(walletClient, { args, l2Chain: base, account: accounts[0].address, @@ -148,7 +148,7 @@ test('uses defaults for data, isCreation, and value', async () => { gasLimit: 25000n, } - const hash = await writeUnsafeDepositTransaction(walletClient, { + const hash = await writeDepositTransaction(walletClient, { args, l2Chain: base, account: accounts[0].address, @@ -170,7 +170,7 @@ test('uses defaults for data, isCreation, and value', async () => { test('errors if l2Chain and optimismPortalAddress both not passed', async () => { expect(() => // @ts-expect-error - writeUnsafeDepositTransaction(walletClient, { + writeDepositTransaction(walletClient, { args: { to: '0x0c54fccd2e384b4bb6f2e405bf5cbc15a017aafb', gasLimit: 25000n, @@ -193,7 +193,7 @@ test('errors if chain.id does not match l1.chainId', async () => { } as const satisfies OpStackChain expect(() => - writeUnsafeDepositTransaction(walletClient, { + writeDepositTransaction(walletClient, { args: { to: '0x0c54fccd2e384b4bb6f2e405bf5cbc15a017aafb', gasLimit: 25000n, @@ -218,7 +218,7 @@ test('works if override chain id matches l1.id', async () => { } as const satisfies OpStackChain expect( - await writeUnsafeDepositTransaction(walletClient, { + await writeDepositTransaction(walletClient, { args: { to: '0x0c54fccd2e384b4bb6f2e405bf5cbc15a017aafb', gasLimit: 25000n, diff --git a/src/actions/wallet/L1/writeUnsafeDepositTransaction.ts b/src/actions/wallet/L1/writeDepositTransaction.ts similarity index 69% rename from src/actions/wallet/L1/writeUnsafeDepositTransaction.ts rename to src/actions/wallet/L1/writeDepositTransaction.ts index 33b859db..542d49bd 100644 --- a/src/actions/wallet/L1/writeUnsafeDepositTransaction.ts +++ b/src/actions/wallet/L1/writeDepositTransaction.ts @@ -16,7 +16,7 @@ export type DepositTransactionParameters = { data?: Hex } -export type WriteUnsafeDepositTransactionParameters< +export type WriteDepositTransactionParameters< TChain extends Chain | undefined = Chain, TAccount extends Account | undefined = Account | undefined, TChainOverride extends Chain | undefined = Chain | undefined, @@ -32,14 +32,21 @@ export type WriteUnsafeDepositTransactionParameters< > /** - * Calls depositTransaction directly on the OptimismPortal contract. - * Marked 'unsafe' becaused does not offer replayability incase the - * L2 tx fails. + * Calls depositTransaction on the OptimismPortal contract. * - * @param parameters - {@link WriteUnsafeDepositTransactionParameters} + * Unlike writeSendMessage, does not offer replayability on L2 incase the L2 tx fails. + * But has the advantage that, if the caller is an EOA, msg.sender of the L2 tx + * will be the caller address. Allowing users to fully tranasact on L2 from L1, which + * is a critical security property. + * + * If the caller is not an EOA, e.g. if the caller is a smart contract wallet, + * msg.sender on L2 will be alias of the caller address + * https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/src/L1/OptimismPortal.sol#L407 + * + * @param parameters - {@link WriteDepositTransactionParameters} * @returns A [Transaction Hash](https://viem.sh/docs/glossary/terms.html#hash). {@link WriteContractReturnType} */ -export async function writeUnsafeDepositTransaction< +export async function writeDepositTransaction< TChain extends Chain | undefined, TAccount extends Account | undefined, TChainOverride extends Chain | undefined = undefined, @@ -49,7 +56,7 @@ export async function writeUnsafeDepositTransaction< args: { to, value = 0n, gasLimit, isCreation = false, data = '0x' }, optimismPortalAddress, ...rest - }: WriteUnsafeDepositTransactionParameters< + }: WriteDepositTransactionParameters< TChain, TAccount, TChainOverride diff --git a/src/decorators/walletL1OpStackActions.ts b/src/decorators/walletL1OpStackActions.ts index 4300a6cf..daff082f 100644 --- a/src/decorators/walletL1OpStackActions.ts +++ b/src/decorators/walletL1OpStackActions.ts @@ -2,6 +2,10 @@ import type { Account, Chain, Transport, WriteContractReturnType } from 'viem' import type { WalletClient } from 'viem' import { writeDepositERC20, type WriteDepositERC20Parameters } from '../actions/wallet/L1/writeDepositERC20.js' import { writeDepositETH, type WriteDepositETHParameters } from '../actions/wallet/L1/writeDepositETH.js' +import { + writeDepositTransaction, + type WriteDepositTransactionParameters, +} from '../actions/wallet/L1/writeDepositTransaction.js' import { writeFinalizeWithdrawalTranasction, type WriteFinalizeWithdrawalTransactionParameters, @@ -10,10 +14,6 @@ import { writeProveWithdrawalTransaction, type WriteProveWithdrawalTransactionParameters, } from '../actions/wallet/L1/writeProveWithdrawalTransaction.js' -import { - writeUnsafeDepositTransaction, - type WriteUnsafeDepositTransactionParameters, -} from '../actions/wallet/L1/writeUnsafeDepositTransaction.js' export type WalletL1OpStackActions< TChain extends Chain | undefined = Chain | undefined, @@ -29,10 +29,10 @@ export type WalletL1OpStackActions< >( args: WriteDepositERC20Parameters, ) => Promise - writeUnsafeDepositTransaction: < + writeDepositTransaction: < TChainOverride extends Chain | undefined = Chain | undefined, >( - args: WriteUnsafeDepositTransactionParameters< + args: WriteDepositTransactionParameters< TChain, TAccount, TChainOverride @@ -66,7 +66,7 @@ export function walletL1OpStackActions< client: WalletClient, ): WalletL1OpStackActions { return { - writeUnsafeDepositTransaction: (args) => writeUnsafeDepositTransaction(client, args), + writeDepositTransaction: (args) => writeDepositTransaction(client, args), writeDepositETH: (args) => writeDepositETH(client, args), writeDepositERC20: (args) => writeDepositERC20(client, args), writeProveWithdrawalTransaction: (args) => writeProveWithdrawalTransaction(client, args),