The truffle project helps one get started in writing contracts, but after the bootstrap and deploying a contract to the testnet, I realized there was less support on how to interact with the contract. Alchemy has a good example in their docs, but I wanted to use the web3.js library maintained by ChainSafe.

The key parts were:

  1. Getting the compiled json abi from trufflesuite and creating a web3.eth.Contract instance
  2. Creating an account with web3.eth.accounts.privateKeyToAccount
  3. Creating a transaction with the function call encoded by encodeABI
  4. and finally signing the transaction before sending it.
import Web3 from "web3";

const yourApiKey = "..."
const web3 = new Web3("https://eth-sepolia.g.alchemy.com/v2/" + yourApiKey);

const privateKey = 'yourPrivateKey'
const senderAccount = web3.eth.accounts.privateKeyToAccount(privateKey);

const contractAddress = "0xd76E31314D760b51493278C72c22d280C6ba6C4b" # a deployed contract on Sepolia

import SimpleStorage from './build/ethereum-contracts/SimpleStorage.json' assert { type: 'json' }
const contract = new web3.eth.Contract(SimpleStorage.abi, contractAddress)
const paymeTransaction = contract.methods.payme();

const transactionObject = {
  from: senderAccount.address,
  to: contractAddress,
  data: paymeTransaction.encodeABI(),
  value: Web3.utils.toWei('0.001', 'ether'),
  gas: 200000,
  maxPriorityFeePerGas: 100000,
  maxFeePerGas: 200000
};

senderAccount.signTransaction(transactionObject)
  .then((signedTx) => {
    return web3.eth.sendSignedTransaction(signedTx.rawTransaction);
  })
  .then((receipt) => {
    console.log('Transaction successful:', receipt);
  })
  .catch((error) => {
    console.error('Transaction failed:', error);
  });

In the browser environment, somehow the privateKey would be part of a default list of accounts, and the higher level transaction.call() can be used, but I’m unfamiliar with that flow currently.