Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions src/MuPay.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity ^0.8.28;
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";

/**
* @title MuPay - A Simple Payment Channel
Expand Down Expand Up @@ -46,6 +47,7 @@ contract MuPay is ReentrancyGuard {
error InsufficientAllowance(uint256 required, uint256 actual);
error AddressIsNotContract(address token);
error AddressIsNotERC20(address token);
error DepositWithPermitNotSupportedForNative();

/**
* @dev Events to log key contract actions.
Expand Down Expand Up @@ -132,6 +134,56 @@ contract MuPay is ReentrancyGuard {
emit ChannelCreated(msg.sender, merchant, token, amount, numberOfTokens, merchantWithdrawAfterBlocks);
}

/**
* @dev Creates a new payment channel using EIP-2612 permit for gasless approval.
* @param payer The address of the user funding the channel.
* @param merchant The merchant receiving payments.
* @param token The ERC-20 token address used for payments.
* @param trustAnchor The final hash value of the hashchain.
* @param amount The total deposit amount for the channel.
* @param numberOfTokens The number of tokens in the hashchain.
* @param merchantWithdrawAfterBlocks The block number after which the merchant can withdraw.
* @param payerWithdrawAfterBlocks The block number after which the payer can reclaim unused funds.
* @param deadline The deadline timestamp for the permit.
* @param v, r, s The EIP-2612 signature parameters.
*/
function createChannelWithPermit(
address payer,
address merchant,
address token,
bytes32 trustAnchor,
uint256 amount,
uint16 numberOfTokens,
uint64 merchantWithdrawAfterBlocks,
uint64 payerWithdrawAfterBlocks,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public nonReentrant {
require(token != address(0), DepositWithPermitNotSupportedForNative());
require(payer != address(0), "Invalid payer address");

_validateChannelParams(merchant, numberOfTokens, merchantWithdrawAfterBlocks, payerWithdrawAfterBlocks);

IERC20Permit(token).permit(payer, address(this), amount, deadline, v, r, s);
// Permit was successful, proceed with channel creation
_createERC20Channel(token, amount); // Handles ERC-20 token validation and transfer
_initChannel(
payer,
merchant,
token,
trustAnchor,
amount,
numberOfTokens,
merchantWithdrawAfterBlocks,
payerWithdrawAfterBlocks
);

// Emit an event to notify the channel has been created
emit ChannelCreated(msg.sender, merchant, token, amount, numberOfTokens, merchantWithdrawAfterBlocks);
}

/**
* @dev Validates the parameters for channel creation.
* @param merchant The merchant address, must not be zero.
Expand Down
41 changes: 41 additions & 0 deletions src/Multisig_2of2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/Messa
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";

contract Multisig is ReentrancyGuard {
using ECDSA for bytes32; // for recover()
Expand Down Expand Up @@ -44,6 +45,7 @@ contract Multisig is ReentrancyGuard {
error StaleNonce(uint256 supplied, uint256 current);
error InvalidChannelSignature(address recovered, address expected);
error ReclaimAfterMustBeAfterExpiration(uint64 expiration, uint64 reclaimAfter);
error DepositWithPermitNotSupportedForNative();

/**
* @dev Events to log key contract actions.
Expand Down Expand Up @@ -76,6 +78,7 @@ contract Multisig is ReentrancyGuard {
* @param token The ERC-20 token address used for payments, or address(0) to use the native currency.
* @param amount The total deposit amount for the channel.
* @param duration The channel lifetime in blocks (from current block).
* @param reclaimDelay The time after which the payer can reclaim the funds.
*/
function createChannel(address payee, address token, uint256 amount, uint64 duration, uint64 reclaimDelay)
external
Expand All @@ -99,6 +102,44 @@ contract Multisig is ReentrancyGuard {
}
}

/**
* @dev Creates a new payment channel between a payer and a payee.
* @param payer The address funding the channel.
* @param payee The address receiving payments.
* @param token The ERC-20 token address used for payments, or address(0) to use the native currency.
* @param duration The channel lifetime in blocks (from current block).
* @param reclaimDelay The time after which the payer can reclaim the funds.
* @param amount The total deposit amount for the channel.
* @param duration The channel lifetime in blocks (from current block).
* @param v, r, s The EIP-2612 signature parameters.
*/
function createChannelWithPermit(
address payer,
address payee,
address token,
uint256 amount,
uint64 duration,
uint64 reclaimDelay,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public nonReentrant {
require(payee != address(0), "Invalid address");
require(
duration < reclaimDelay,
ReclaimAfterMustBeAfterExpiration(
uint64(block.timestamp) + duration, uint64(block.timestamp) + reclaimDelay
)
);
// Validate the permit signature
require(token != address(0), DepositWithPermitNotSupportedForNative());

IERC20Permit(token).permit(payer, address(this), amount, deadline, v, r, s);

_createERC20Channel(payee, token, amount, duration, reclaimDelay);
}

/**
* @dev Handles channel creation when using native currency (ETH).
* @param payee The address receiving ETH payments.
Expand Down
Loading