To get Plume testnet USDC
Use Circle's CCTP sample app
to transfer USDC cross-chain to your Plume wallet.
To get testnet PLUME
Use the Plume faucet to request free testnet
PLUME for gas fees.
This guide shows you how to transfer USDC on the Plume testnet. You'll write a simple script that checks your balance and sends a test transfer.
Before you begin, make sure you have:
.env
file.To get Plume testnet USDC
Use Circle's CCTP sample app
to transfer USDC cross-chain to your Plume wallet.
To get testnet PLUME
Use the Plume faucet to request free testnet
PLUME for gas fees.
Initialize a new Node.js project and install dependencies:
npm init -y
npm pkg set type=module
npm install viem dotenv
In the project root, create a .env
file with your private key and recipient
address:
PRIVATE_KEY=<YOUR_PRIVATE_KEY>
RECIPIENT_ADDRESS=0x<RECIPIENT_ADDRESS>
Create an index.js
file. You'll add code step by step in the following
sections.
In index.js
, import required modules and define constants for the Plume
testnet and the USDC contract:
import "dotenv/config";
import {
createPublicClient,
createWalletClient,
http,
formatUnits,
parseUnits,
} from "viem";
import { privateKeyToAccount } from "viem/accounts";
const plumeTestnet = {
id: 98867,
name: "Plume Testnet",
nativeCurrency: { name: "PLUME", symbol: "PLUME", decimals: 18 },
rpcUrls: {
default: { http: ["https://testnet-rpc.plume.org"] },
},
blockExplorers: {
default: {
name: "Plume Testnet Explorer",
url: "https://testnet-explorer.plume.org",
},
},
testnet: true,
};
const USDC_ADDRESS = "0xcB5f30e335672893c7eb944B374c196392C19D18";
const USDC_DECIMALS = 6;
const USDC_ABI = [
{
name: "balanceOf",
type: "function",
stateMutability: "view",
inputs: [{ name: "account", type: "address" }],
outputs: [{ name: "", type: "uint256" }],
},
{
name: "transfer",
type: "function",
stateMutability: "nonpayable",
inputs: [
{ name: "to", type: "address" },
{ name: "amount", type: "uint256" },
],
outputs: [{ name: "", type: "bool" }],
},
];
Here's what this section does:
balanceOf
and transfer
functions, since they're all
that's needed for this guide.Next, load your private key and recipient address from .env
:
const PRIVATE_KEY_RAW = process.env.PRIVATE_KEY;
const RECIPIENT = process.env.RECIPIENT_ADDRESS;
if (!PRIVATE_KEY_RAW || !RECIPIENT) {
console.error("Error: Missing PRIVATE_KEY or RECIPIENT_ADDRESS in .env");
process.exit(1);
}
if (!/^0x[a-fA-F0-9]{40}$/.test(RECIPIENT)) {
console.error("Error: Recipient address is invalid");
process.exit(1);
}
const PRIVATE_KEY = PRIVATE_KEY_RAW.startsWith("0x")
? PRIVATE_KEY_RAW
: `0x${PRIVATE_KEY_RAW}`;
This section ensures sensitive values are handled safely:
.env
file so you don't hardcode sensitive information.0x
so Viem can use it.Create a public client for reading blockchain data and a wallet client for sending transactions:
const account = privateKeyToAccount(PRIVATE_KEY);
const publicClient = createPublicClient({
chain: plumeTestnet,
transport: http(),
});
const walletClient = createWalletClient({
account,
chain: plumeTestnet,
transport: http(),
});
These client methods give you read and write access to the Plume testnet:
privateKeyToAccount
creates an account object from your private key.createPublicClient
is used for reading data from the blockchain (no private
key needed).createWalletClient
uses your account to sign and send transactions.Now, write the main logic to check your balance and send a transfer:
(async () => {
try {
// Check balance
const balance = await publicClient.readContract({
address: USDC_ADDRESS,
abi: USDC_ABI,
functionName: "balanceOf",
args: [account.address],
});
const balanceFormatted = Number(formatUnits(balance, USDC_DECIMALS));
const amount = 10; // send 10 USDC
console.log("Sender:", account.address);
console.log("Recipient:", RECIPIENT);
console.log("Balance:", balanceFormatted);
if (amount > balanceFormatted) {
console.error("Error: Insufficient balance");
process.exit(1);
}
const amountInDecimals = parseUnits(amount.toString(), USDC_DECIMALS);
// Transfer
const hash = await walletClient.writeContract({
address: USDC_ADDRESS,
abi: USDC_ABI,
functionName: "transfer",
args: [RECIPIENT, amountInDecimals],
});
console.log("Transfer successful!");
console.log("Tx hash:", hash);
console.log(
"Explorer:",
`${plumeTestnet.blockExplorers.default.url}/tx/${hash}`,
);
} catch (err) {
console.error("Transfer failed:", err.message || err);
process.exit(1);
}
})();
Here's how the transfer process works:
balanceOf
function.formatUnits
.parseUnits
.transfer
function on the USDC contract to send tokens.After running the script:
node index.js
You'll see output similar to the following:
Sender: 0x1A2b...7890
Recipient: 0x9F8f...1234
Balance: 250.0
Transfer successful!
Tx hash: 0xabc123...def456
Explorer: https://testnet-explorer.plume.org/tx/0xabc123...def456
To verify the transfer, copy the transaction hash URL from the Explorer:
line
and open it in your browser. This will take you to the Plume testnet explorer,
where you can view full transaction details.
Tip: If your script doesn't output a full explorer URL, you can manually paste the transaction hash into the Plume testnet explorer.
In this quickstart, you learned how to check balances and transfer USDC on the Plume testnet using Viem and Node.js. Here are the key points to remember:
.env
. Never commit secrets.balanceOf
and transfer
for
simplicity.readContract
and writeContract
handle reads and writes,
and Viem automatically prefixes your private key with 0x
if needed.