Skip to content

Conversation

@shamardy
Copy link
Collaborator

@shamardy shamardy commented Jan 30, 2026

Summary

  • Add SafeERC20 to EtomicSwap V1 for USDT and non-standard ERC20 support
  • Remove NFT functionality (ERC721/ERC1155) from V1 contract - keep ETH/ERC20 only
  • Fix erc20Payment() payable modifier bug (function doesn't use msg.value)
  • Add tokenAddress zero-check validation
  • Update OpenZeppelin to 5.4.0
  • Add Hardhat Ignition + CREATE2 deployment infra for deterministic cross-chain addresses
  • Deploy and verify contract on Ethereum mainnet and Sepolia

Fixes GLEECBTC/komodo-defi-framework#408

Deployed Address (all chains)

0x61EEC68Cf64d1b31e41EA713356De2563fB6D3F1

Deployed via CreateX CREATE2 factory — same address on every EVM chain.

Chain Explorer
Ethereum etherscan.io
Sepolia sepolia.etherscan.io

Changes

EtomicSwap.sol

  • Add OpenZeppelin SafeERC20 import and using SafeERC20 for IERC20
  • Replace require(token.transferFrom(...)) with safeTransferFrom
  • Replace require(token.transfer(...)) with safeTransfer
  • Remove payable modifier from erc20Payment() (prevents ETH being stuck)
  • Add require(tokenAddress != address(0)) check
  • Remove all NFT-related functions (ERC721/ERC1155)
  • Bump Solidity to 0.8.33 (latest stable)

Deployment Infrastructure

  • Add Hardhat Ignition module (ignition/modules/EtomicSwapV1.js) with CREATE2 strategy
  • Configure 17 mainnets and 5 testnets in hardhat.config.js
  • Add Etherscan V2 verification (single API key for all supported chains)
  • Add .env.example template
  • Add DEPLOYMENTS.md with deployment record and runbook for remaining chains

Dependencies

  • Update @openzeppelin/contracts from ^5.0.0 to ^5.4.0
  • Update hardhat to ^2.26.0, ethers to ^6.14.0, hardhat-ethers to ^3.1.0
  • Add hardhat-ignition, hardhat-verify, dotenv
  • Move test dependencies (chai, chai-as-promised, ripemd160) to devDependencies

Tests

  • Remove NFT tests from V1 test suite (9 tests removed)
  • Update ethers version in error message check

Why SafeERC20?

USDT's transfer/transferFrom don't return a boolean. The previous code used require(token.transferFrom(...)) which fails because Solidity tries to decode a non-existent return value. SafeERC20 handles this correctly.

Testing

  • All 80 Hardhat tests pass
  • Deployed and tested on Sepolia (ETH payment/spend, payment/refund, invalid secret rejection)
  • Deployed and verified on Ethereum mainnet

Future TODOs (for follow-up PRs)

  • Deploy to remaining 16 EVM chains (see DEPLOYMENTS.md)
  • Fix .transfer() deprecation (uses 2300 gas limit) to support smart contract wallets (Gnosis Safe, etc.)

Related

Add OpenZeppelin's SafeERC20 to handle non-standard ERC20 tokens like USDT
that don't return a boolean from transfer/transferFrom.

Changes to EtomicSwap.sol:
- Add SafeERC20 import and `using SafeERC20 for IERC20`
- Replace transfer/transferFrom with safeTransfer/safeTransferFrom
- Remove `payable` from erc20Payment() (function doesn't use msg.value)
- Add tokenAddress != address(0) validation in erc20Payment()
- Bump Solidity version to 0.8.33 (latest stable)

Changes to hardhat.config.js:
- Update compiler to 0.8.33
- Enable optimizer with 10000 runs

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@shamardy shamardy requested a review from mariocynicys January 30, 2026 12:46
Remove ERC721/ERC1155/ERC165 code from V1 contract since NFT swaps
use separate V2 contracts. This results in a smaller, cleaner contract
focused only on ETH and ERC20 atomic swaps.

Changes:
- Remove NFT imports and interface implementations
- Remove receiverSpendErc721, receiverSpendErc1155 functions
- Remove senderRefundErc721, senderRefundErc1155 functions
- Remove onERC721Received, onERC1155Received, onERC1155BatchReceived
- Remove supportsInterface and isContract helper
- Keep SafeERC20 for USDT compatibility
- Bump package version to 1.1.0

Bytecode reduced from ~13KB to ~5KB.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Update @openzeppelin/contracts from ^5.0.0 to ^5.4.0
- Move test deps (chai, chai-as-promised, ripemd160) to devDependencies
- Remove NFT tests from V1 test suite (NFT code removed from contract)
- Update ethers version in test error message
- Regenerate yarn.lock

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Copy link

@mariocynicys mariocynicys left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks.
Only have those comments.

Q: we will re-deploy v1 contract for ETH for the sake of getting USDT-ERC20 working, but only for ETH? no?
since other chains already have a functioning USDT-EVM20 coin?

Comment on lines 8 to 9
enabled: true,
runs: 10000

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: what is runs: 10000

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The runs parameter tells the optimizer how many times each opcode will execute over the contract's lifetime - it's a trade-off between deployment cost and execution cost.

10000 is a common choice for frequently-used contracts. Higher values have diminishing returns since most inlining decisions are already made.

Ref: Solidity Optimizer Docs

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P.S. I favoured execution cost (what the users pay) vs the deployment cost which is what we will pay to deploy the contract.

contract EtomicSwap {
using SafeERC20 for IERC20;

contract EtomicSwap is ERC165, IERC1155Receiver, IERC721Receiver {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we better keep erc165?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ERC165 is for interface detection - lets other contracts query "do you support interface X?".

We had it because the old contract implemented IERC721Receiver/IERC1155Receiver for NFT support. Standard ERC20 doesn't require ERC165 (it predates it), and ERC721/ERC1155 are the ones that mandate it for their receiver callbacks.

With NFTs removed, there's no interface to advertise. Our contract uses safeTransferFrom to pull tokens (not receive via callback), so no receiver interface is needed.

@shamardy
Copy link
Collaborator Author

shamardy commented Feb 3, 2026

Q: we will re-deploy v1 contract for ETH for the sake of getting USDT-ERC20 working, but only for ETH? no?
since other chains already have a functioning USDT-EVM20 coin?

No need to re-deploy the others as there are no changes in KDF but tests only, so same code works for both contracts old and new. For consistency we can re-deploy others later but it's not a priority. You will see for yourself once I open the KDF PR.

shamardy added a commit to GLEECBTC/komodo-defi-framework that referenced this pull request Feb 3, 2026
- Update swap_contract_bytes and ABI for SafeERC20 V1 contract
  (compiled with OpenZeppelin 5.4.0)
- Add USDT contract bytecode and ABI for testing
- Add docker tests for USDT swaps (taker spend, maker refund)
- Add docker tests for ERC20 decimal handling (6, 8, 18 decimals)
- Update test helpers for USDT deployment and funding

The SafeERC20 changes enable USDT and other non-standard ERC20 tokens
that don't return bool from transfer/transferFrom.

Related: GLEECBTC/etomic-swap#11

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@shamardy
Copy link
Collaborator Author

shamardy commented Feb 3, 2026

KDF PR opened: GLEECBTC/komodo-defi-framework#2711

Add Hardhat Ignition module with CREATE2 strategy via CreateX factory
for deterministic cross-chain deployment. Configure 17 mainnets, 5
testnets, and Etherscan V2 verification. Deploy and verify EtomicSwap V1
(SafeERC20) to Ethereum mainnet and Sepolia at
0x61EEC68Cf64d1b31e41EA713356De2563fB6D3F1.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Copy link

@mariocynicys mariocynicys left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants