Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
247 changes: 141 additions & 106 deletions sdk/evm/connect/guides/javascript/send-transactions/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ keywords: [SDK, JavaScript, send, transaction, transactions, status, estimate, g
toc_max_heading_level: 2
---

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# Send transactions

Handle EVM transactions in your JavaScript dapp.
Expand All @@ -21,107 +24,138 @@ The following are examples of sending a [basic transaction](#send-a-basic-transa

## Send a basic transaction

The basic transaction uses the [`eth_requestAccounts`](../../../reference/json-rpc-api/index.md),
[`eth_sendTransaction`](../../../reference/json-rpc-api/index.md), and
[`eth_getTransactionReceipt`](../../../reference/json-rpc-api/index.md)
RPC methods.
<Tabs defaultValue="viem" values={[
{ label: "viem", value: "viem" },
{ label: "web3.js", value: "web3" },
{ label: "ethers.js", value: "ethers" },
{ label: "Ethereum API", value: "eth_api" },
]}>
<TabItem value="eth_api">

```javascript
async function sendTransaction(recipientAddress, amount) {
try {
// Get current account
const accounts = await ethereum.request({
method: "eth_requestAccounts"
});
const from = accounts[0];

// Convert ETH amount to wei (hex)
const value = `0x${(amount * 1e18).toString(16)}`;

// Prepare transaction
const transaction = {
from,
to: recipientAddress,
value,
// Gas fields are optional - MetaMask will estimate
};

// Send transaction
const txHash = await ethereum.request({
method: "eth_sendTransaction",
params: [transaction],
});

return txHash;
} catch (error) {
if (error.code === 4001) {
throw new Error("Transaction rejected by user");
}
throw error;
}
import { MetaMaskSDK } from '@metamask/sdk'

// Initialize SDK
const MMSDK = new MetaMaskSDK()
const provider = MMSDK.getProvider()

// Get current account
const accounts = await provider.request({
method: 'eth_requestAccounts',
})
const from = accounts[0]

// Convert ETH amount to wei (hex)
const value = `0x${(0.0001 * 1e18).toString(16)}`

// Prepare transaction
const transaction = {
from,
to: '0xRECIPIENT_ADDRESS',
value,
// Gas fields are optional - MetaMask will estimate
}

// Track transaction status
function watchTransaction(txHash) {
return new Promise((resolve, reject) => {
const checkTransaction = async () => {
try {
const tx = await ethereum.request({
method: "eth_getTransactionReceipt",
params: [txHash],
});

if (tx) {
if (tx.status === "0x1") {
resolve(tx);
} else {
reject(new Error("Transaction failed"));
}
} else {
setTimeout(checkTransaction, 2000); // Check every 2 seconds
}
} catch (error) {
reject(error);
}
};

checkTransaction();
});
}
// Send transaction
const txHash = await provider.request({
method: 'eth_sendTransaction',
params: [transaction],
})

const tx = await provider.request({
method: 'eth_getTransactionReceipt',
params: [txHash],
})
```

The following is an example implementation of the basic transaction:

```html
<div class="transaction-form">
<input type="text" id="recipient" placeholder="Recipient Address">
<input type="number" id="amount" placeholder="Amount in ETH">
<button onclick="handleSend()">Send ETH</button>
<div id="status"></div>
</div>

<script>
async function handleSend() {
const recipient = document.getElementById("recipient").value;
const amount = document.getElementById("amount").value;
const status = document.getElementById("status");

try {
status.textContent = "Sending transaction...";
const txHash = await sendTransaction(recipient, amount);
status.textContent = `Transaction sent: ${txHash}`;

// Watch for confirmation
status.textContent = "Waiting for confirmation...";
await watchTransaction(txHash);
status.textContent = "Transaction confirmed!";
} catch (error) {
status.textContent = `Error: ${error.message}`;
}
}
</script>
</TabItem>
<TabItem value="viem">

```tsx
import { MetaMaskSDK } from '@metamask/sdk'
import { createPublicClient, createWalletClient, custom } from 'viem'
import { mainnet } from 'viem/chains'

// Initialize SDK
const MMSDK = new MetaMaskSDK()
const provider = MMSDK.getProvider()

const publicClient = createPublicClient({ chain: mainnet, transport: custom(provider) })
const walletClient = createWalletClient({ chain: mainnet, transport: custom(provider) })

// data for the transaction
const destination = '0xRECIPIENT_ADDRESS'
const amount = parseEther('0.0001')
const address = await walletClient.getAddresses()

// Submit transaction to the blockchain
const hash = await walletClient.sendTransaction({
account: address[0],
to: destination,
value: amount,
})

const receipt = await publicClient.waitForTransactionReceipt({ hash })
```

</TabItem>
<TabItem value="ethers">

```tsx
import { MetaMaskSDK } from '@metamask/sdk'
import { ethers } from 'ethers'
import { BrowserProvider, parseUnits } from 'ethers'

// Initialize SDK
const MMSDK = new MetaMaskSDK()
const provider = MMSDK.getProvider()

const ethersProvider = new ethers.BrowserProvider(provider)
const signer = await ethersProvider.getSigner()

const destination = '0xRECIPIENT_ADDRESS'
const amount = parseUnits('0.0001', 'ether')

// Submit transaction to the blockchain
const tx = await signer.sendTransaction({
to: destination,
value: amount,
})

// Wait for the transaction to be mined
const receipt = await tx.wait()
```

</TabItem>
<TabItem value="web3">

```tsx
import { MetaMaskSDK } from '@metamask/sdk'
import { Web3 } from 'web3'

// Initialize SDK
const MMSDK = new MetaMaskSDK()
const provider = MMSDK.getProvider()

const web3 = new Web3(provider)

// Get user's Ethereum public address
const fromAddress = (await web3.eth.getAccounts())[0]

const destination = '0xRECIPIENT_ADDRESS'
const amount = web3.utils.toWei('0.0001') // Convert 0.0001 ether to wei

// Submit transaction to the blockchain and wait for it to be mined
const receipt = await web3.eth.sendTransaction({
from: fromAddress,
to: destination,
value: amount,
})
```

</TabItem>
</Tabs>

## Send an advanced transaction with gas estimation

To add gas estimation, use the [`eth_estimateGas`](../../../reference/json-rpc-api/index.md)
Expand All @@ -131,15 +165,15 @@ RPC method.
async function estimateGas(transaction) {
try {
const gasEstimate = await ethereum.request({
method: "eth_estimateGas",
params: [transaction]
});
method: 'eth_estimateGas',
params: [transaction],
})

// Add 20% buffer for safety
return BigInt(gasEstimate) * 120n / 100n;
return (BigInt(gasEstimate) * 120n) / 100n
} catch (error) {
console.error("Gas estimation failed:", error);
throw error;
console.error('Gas estimation failed:', error)
throw error
}
}
```
Expand All @@ -166,14 +200,15 @@ Follow these best practices when handling transactions.
- Display **clear loading states** during transactions.
- Show **transaction progress** in real time.
- Provide **detailed transaction information**.

## Common errors

| Error code | Description | Solution |
|------------|-------------|----------|
| `4001` | User rejected transaction | Show a retry option and a clear error message. |
| `-32603` | Insufficient funds | Check the balance before sending a transaction. |
| `-32000` | Gas too low | Increase the gas limit or add a buffer to the estimation. |
| `-32002` | Request already pending | Prevent multiple concurrent transactions. |
| Error code | Description | Solution |
| ---------- | ------------------------- | --------------------------------------------------------- |
| `4001` | User rejected transaction | Show a retry option and a clear error message. |
| `-32603` | Insufficient funds | Check the balance before sending a transaction. |
| `-32000` | Gas too low | Increase the gas limit or add a buffer to the estimation. |
| `-32002` | Request already pending | Prevent multiple concurrent transactions. |

## Next steps

Expand Down