foundry icon indicating copy to clipboard operation
foundry copied to clipboard

feat(cast): erc20 safe preflight

Open Syzygy106 opened this issue 1 month ago • 8 comments

Motivation

Closes #12402

Enhance UX and prevent accidental transfers with incorrect amounts. Users may misunderstand the token's decimal places and accidentally send far more (or less) tokens than intended.

Also, approve() requires identical feature, so I decided to add the same.

At the other hand, transferFrom() - doesn't need that, because this method is rarely called by users implicitly, more often him calls other smartcontracts (like swapRouter).

Solution

Added interactive confirmation prompts to cast erc20 transfer and cast erc20 approve commands that:

  1. Fetch token metadata (symbol and decimals) from the ERC20 contract

  2. Display human-readable amounts in the confirmation prompt: Example: Confirm transfer of 100 USDC to address 0x666...666 instead of raw 100000000

  3. Provide --yes flag to skip confirmation for non-interactive usage (scripts, CI/CD)

  4. Handle edge cases gracefully: -Falls back to raw amount if decimals/symbol can't be fetched -Shows warning if token metadata is unavailable

Demonstration

Let's create a test token first:

forge create crates/cast/tests/fixtures/TestToken.sol:TestToken   --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80   --rpc-url http://localhost:8545   --broadcast

Then we can test behavior:

./target/debug/cast erc20 transfer   0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512   0x70997970C51812dc3A010C7d01b50e0d17dc79C8   100000000000000000000   --rpc-url http://localhost:8545   --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80

---
Confirm transfer of 100 TEST to address 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 [y/n] 

In case y (yes):

Confirm transfer of 100 TEST to address 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 yes

---
0x6a6b22e5ff88c63d47527d7d8ff35c3d412388ba7392b1bbc6985f30ccf0e87c

In case n (no):

Confirm transfer of 100 TEST to address 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 no

---
Error: Transfer cancelled by user

With skip promt that would be:

./target/debug/cast erc20 transfer \
  0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 \
  0x70997970C51812dc3A010C7d01b50e0d17dc79C8 \
  50000000000000000000 \
  --yes \
  --rpc-url http://localhost:8545 \
  --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80


---

0x3de20e9150169dfe8e7a0c40ecbba21d99587337c3de52d864cd871ed431c0e7

In case where contract address aren't correct/contract doesn't support ERC20 interface for decimals/symbol, we'll get a warning:

./target/debug/cast erc20 transfer \
  0x1234567890123456789012345678901234567890 \
  0x70997970C51812dc3A010C7d01b50e0d17dc79C8 \
  100 \
  --rpc-url http://localhost:8545 \
  --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80


Warning: Warning: Could not fetch token metadata (decimals/symbol). The address may not be a valid ERC20 token contract.
Confirm transfer of 100 TOKEN (raw amount) to address 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 [y/n]

The same behavior has been added to approve():

./target/debug/cast erc20 approve \
  0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 \
  0x70997970C51812dc3A010C7d01b50e0d17dc79C8 \
  75000000000000000000 \
  --rpc-url http://localhost:8545 \
  --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80

Confirm approval for 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 to spend 75 TEST from your account [y/n]

PR Checklist

  • [x] Added Tests
  • [x] Added Documentation
  • [ ] Breaking changes

Syzygy106 avatar Nov 08 '25 13:11 Syzygy106

Also, I would like to update Foundry book, according to a new flag

Syzygy106 avatar Nov 08 '25 13:11 Syzygy106

Hmmm, CI / deny fails. But looks like it isn't my fault

Syzygy106 avatar Nov 18 '25 08:11 Syzygy106

i know you can pipe in yes, but let's also add a --yes/-y as this is pretty common for clis

Hi there, @onbjerg , I actually agree with the --yes flag approach - that was my initial implementation (you can see it in the PR history above - https://github.com/foundry-rs/foundry/pull/12499#discussion_r2527576406).

However, other maintainers had different perspectives:

  1. @grandizzy (the original issue author) suggested removing the --yes flag, saying "don't think we need it?" https://github.com/foundry-rs/foundry/pull/12499#discussion_r2526285635
  2. @0xrusowsky also seemed to prefer the pipe approach https://github.com/foundry-rs/foundry/pull/12499#discussion_r2536408180

Their reasoning was avoiding adding new arguments to keep the CLI simpler and easier to maintain and following Unix philosophy with yes | pipe being a standard pattern.

I'm happy to implement either approach, but before making changes again, I think it would be helpful if the maintainers could align on the preferred solution:

Option 1: --yes/-y flag (more explicit, common in modern CLIs) (I prefer this) Option 2: yes | pipe only (Unix philosophy, no new flags)

@grandizzy @0xrusowsky @onbjerg - could you please discuss and let me know which direction you'd like me to take? I want to make sure we're all aligned before implementing the final version.

Other maintainers are also invited to the conversation @mattsse , @zerosnacks , @DaniPopes

Syzygy106 avatar Nov 20 '25 23:11 Syzygy106

@Syzygy106 sorry for back and forth, we'll discuss and make a final call re how to add this. thank you!

grandizzy avatar Nov 21 '25 15:11 grandizzy

@Syzygy106 sorry, my bad, should have been get full consensus on it. Let's stick with your initial impl, that is adding the --yes/-y arg. Thank you

grandizzy avatar Nov 21 '25 19:11 grandizzy

@Syzygy106 sorry, my bad, should have been get full consensus on it. Let's stick with your initial impl, that is adding the --yes/-y arg. Thank you

Yep, no problem!

Syzygy106 avatar Nov 22 '25 00:11 Syzygy106

@onbjerg , @grandizzy , done

Syzygy106 avatar Nov 22 '25 03:11 Syzygy106

@onbjerg , @grandizzy , done

@grandizzy, could you please review the PR when you have a moment?

Syzygy106 avatar Nov 29 '25 04:11 Syzygy106