Memo contract to add a memo to a USDC transfer on Arc Testnet. This
tutorial shows the full flow with viem, ethers, Python, and curl. You will
encode the inner USDC transfer, submit it with Memo.memo(...), decode the
receipt, and query past memo events by memoId. To learn how the Memo
contract preserves your wallet as the sender and orders its events, see
Transaction memos.
Prerequisites
Before you begin, ensure that you’ve:- Installed Node.js v22+ for the TypeScript examples, or Python 3.10+ for the Python example.
- Created an Arc Testnet wallet.
- Funded the wallet with testnet USDC from the Circle Faucet.
- Chosen a recipient address on Arc Testnet.
Step 1. Set up the project
Create a new project and install the dependencies for the client library you want to use:.env file:
Shell
.env
YOUR_PRIVATE_KEY with the 0x-prefixed private key for the funded
wallet. Replace RECIPIENT_ADDRESS with the wallet that should receive USDC.
Step 2. Review the contract address and ABI
Arc Testnet uses the following predeployed transaction memo contracts:| Contract | Address |
|---|---|
Memo | 0x5294E9927c3306DcBaDb03fe70b92e01cCede505 |
USDC | 0x3600000000000000000000000000000000000000 |
memo-abi.json in your project:
memo-abi.json
Step 3. Configure the client connection
Create the script file for your client library. The first chunk reads the wallet and recipient configuration from.env, sets the contract addresses, loads the
Memo ABI, and creates the clients that sign and send requests.
- Viem
- Ethers
- Python
The example imports the The public client reads chain state, and the wallet client signs and submits
transactions from your funded account.
arcTestnet chain definition from viem/chains, which
requires viem v2.38 or later.Create viem-memo.ts:TypeScript
Step 4. Encode the transfer and memo values
Add the values that define the memo transfer. The encoded ERC-20transfer call
becomes the inner call that Memo.memo(...) forwards to USDC. The memoId is a
32-byte identifier your application uses to look the memo up later, and the memo
bytes carry the metadata itself.
- Viem
- Ethers
- Python
Append to
viem-memo.ts:TypeScript
1 USDC, expressed with six decimals. The script also
hashes the transfer calldata so a later step can match it against the
callDataHash field of the emitted Memo event.
Step 5. Confirm the Memo contract is deployed
Before you send the transaction, check that the Memo address has deployed
bytecode. If eth_getCode returns 0x, the contract is not available on the
RPC endpoint you are using, and the script stops with an error.
- Viem
- Ethers
- Python
Append to
viem-memo.ts:TypeScript
Step 6. Send the memo transaction
CallMemo.memo(...) with the four arguments from Step 4: the USDC contract as
the target, the encoded transfer calldata, the memoId, and the memo bytes.
Then wait for the receipt and confirm the transaction succeeded.
- Viem
- Ethers
- Python
Append to
viem-memo.ts:TypeScript
Step 7. Decode and verify the memo events
A successful memo transfer emits oneBeforeMemo event and one Memo event
alongside the USDC Transfer. Decode the receipt logs and verify that the
Memo event carries the sender, target, calldata hash, memoId, and memo bytes
the script sent.
- Viem
- Ethers
- Python
Append to
viem-memo.ts:TypeScript
Step 8. Query memo events by memoId
memoId is an indexed event field, so you can find the memo again later without
the transaction hash. Query the Memo logs for the memoId the script sent and
confirm exactly one match.
- Viem
- Ethers
- Python
Append to
viem-memo.ts:TypeScript
Step 9. Run the script
Run the completed script for your client library:BeforeMemo and Memo event data,
and one historical log match for the same memoId:
Step 10. Check JSON-RPC directly with curl
Use curl for read-only JSON-RPC checks, such as verifying deployed bytecode, reading a transaction receipt, or queryingMemo logs. Use a client library
such as viem, ethers, or web3.py to sign and submit the transaction itself.