Skip to main content

MakeServicePaymentOnEvvm

The MakeServicePaymentOnEvvm abstract contract provides simplified helpers for processing payments through EVVM. It wraps common payment operations with service-friendly defaults.

Overview

Contract Type: Abstract contract
License: EVVM-NONCOMMERCIAL-1.0
Import Path: @evvm/testnet-contracts/library/utils/service/MakeServicePaymentOnEvvm.sol

Key Features

  • Simplified payment calls with service defaults
  • Contract balance transfers (caPay wrapper)
  • EVVM address management for upgrades
  • Lightweight - minimal storage overhead

Contract Structure

abstract contract MakeServicePaymentOnEvvm {
IEvvm public evvm;

constructor(address evvmAddress) {
evvm = IEvvm(evvmAddress);
}

// Payment functions
// Address management
}

State Variables

evvm

IEvvm public evvm;

Description: Public interface to the EVVM Core Contract

Visibility: public - accessible externally

Initialized: In constructor with EVVM contract address

Functions

makePay

function makePay(
address from,
address token,
uint256 amount,
uint256 priorityFee,
uint256 nonce,
bool priorityFlag,
bytes memory signature
) internal virtual

Description: Processes a payment through EVVM with service-specific defaults

Parameters:

  • from: Address sending the payment
  • token: Token address (address(0) for ETH, address(1) for MATE)
  • amount: Amount to transfer
  • priorityFee: Fee for fisher executing transaction
  • nonce: EVVM nonce for payment
  • priorityFlag: true for async nonce, false for sync nonce
  • signature: Payment authorization signature from from address

Default Values (Set Automatically):

  • to: address(this) - recipient is this service contract
  • to_identity: "" - empty identity string
  • executor: address(this) - service contract executes payment

Example:

contract MyService is MakeServicePaymentOnEvvm {
function orderProduct(
address customer,
uint256 price,
uint256 priorityFee,
uint256 paymentNonce,
bool useAsync,
bytes memory paymentSignature
) external {
// Process payment with simplified call
makePay(
customer, // from
address(0), // ETH
price, // amount
priorityFee, // priority fee
paymentNonce, // EVVM nonce
useAsync, // async or sync
paymentSignature // payment signature
);

// Payment complete - service received funds
}
}

makeCaPay

function makeCaPay(
address to,
address token,
uint256 amount
) internal virtual

Description: Transfers tokens from service contract's EVVM balance to another address

Parameters:

  • to: Recipient address
  • token: Token address to transfer
  • amount: Amount to transfer

Requirements:

  • Service contract must have sufficient balance in EVVM
  • Token must be valid EVVM token

Example:

// Withdraw ETH earnings to owner
uint256 ethBalance = evvm.getBalance(address(this), address(0));
makeCaPay(owner, address(0), ethBalance);

// Reward fisher with MATE tokens
makeCaPay(msg.sender, address(1), rewardAmount);

// Transfer tokens to user
makeCaPay(userAddress, tokenAddress, amount);

_changeEvvmAddress

function _changeEvvmAddress(address newEvvmAddress) internal

Description: Updates the EVVM contract address (for proxy upgrades)

Parameters:

  • newEvvmAddress: New EVVM contract address

Use Case: When EVVM contract is upgraded and service needs to point to new implementation

Security: Should be protected with access control

Example:

contract MyService is MakeServicePaymentOnEvvm {
address public owner;

modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}

function updateEvvmAddress(address newAddress) external onlyOwner {
_changeEvvmAddress(newAddress);
}
}

Usage Patterns

Pattern 1: Basic Payment Service

contract PaymentService is MakeServicePaymentOnEvvm {
constructor(address evvmAddress) MakeServicePaymentOnEvvm(evvmAddress) {}

function buyItem(
address buyer,
uint256 itemPrice,
uint256 priorityFee,
uint256 paymentNonce,
bytes memory paymentSig
) external {
// Simplified payment call
makePay(
buyer,
address(0), // ETH
itemPrice,
priorityFee,
paymentNonce,
true, // async nonce
paymentSig
);

// Item purchased, payment received
}
}

Pattern 2: With Withdrawals

contract ServiceWithWithdrawals is MakeServicePaymentOnEvvm {
address public owner;

constructor(address evvmAddress, address _owner)
MakeServicePaymentOnEvvm(evvmAddress)
{
owner = _owner;
}

function withdrawEarnings() external {
require(msg.sender == owner, "Not owner");

uint256 balance = evvm.getBalance(address(this), address(0));
makeCaPay(owner, address(0), balance);
}
}

Pattern 3: Multi-Token Support

contract MultiTokenService is MakeServicePaymentOnEvvm {
function processPayment(
address payer,
address token,
uint256 amount,
uint256 priorityFee,
uint256 nonce,
bytes memory signature
) external {
// Validate token
require(
token == address(0) || token == address(1),
"Unsupported token"
);

// Process payment
makePay(payer, token, amount, priorityFee, nonce, true, signature);
}
}

Pattern 4: Fisher Rewards

contract ServiceWithRewards is MakeServicePaymentOnEvvm {
function action(
address user,
uint256 fee,
uint256 nonce,
bytes memory signature
) external {
// Process payment
makePay(user, address(0), fee, 0, nonce, true, signature);

// Reward fisher if service has funds
uint256 reward = calculateReward();
if (reward > 0) {
makeCaPay(msg.sender, address(1), reward); // MATE tokens
}
}
}

Comparison with Direct EVVM Calls

Using MakeServicePaymentOnEvvm

// Simplified - fewer parameters
makePay(
from,
token,
amount,
priorityFee,
nonce,
priorityFlag,
signature
);

Direct EVVM Call

// More verbose - must specify all parameters
evvm.pay(
from,
address(this), // Must specify recipient
"", // Must specify identity (usually empty)
token,
amount,
priorityFee,
nonce,
priorityFlag,
address(this), // Must specify executor
signature
);

Benefits of MakeServicePaymentOnEvvm:

  • Fewer parameters to manage
  • Service-specific defaults (recipient = this, executor = this)
  • Cleaner, more readable code
  • Less error-prone

Integration with EvvmService

EvvmService provides similar functionality but with additional features:

MakeServicePaymentOnEvvm

abstract contract MakeServicePaymentOnEvvm {
// Basic payment helpers
function makePay(...) internal { }
function makeCaPay(...) internal { }
}

EvvmService (Includes More)

abstract contract EvvmService is AsyncNonceService {
// Payment helpers + additional features
function requestPay(...) internal { } // Similar to makePay
function makeCaPay(...) internal { }

// Additional features
function validateServiceSignature(...) internal { }
function _makeStakeService(...) internal { }
function _makeUnstakeService(...) internal { }
// + Nonce management via AsyncNonceService
}

When to use each:

  • MakeServicePaymentOnEvvm: Lightweight, payment-only services
  • EvvmService: Full-featured services with signatures, nonces, staking

Security Considerations

1. Protect Address Updates

// Good - access control
address public owner;

function updateEvvmAddress(address newAddr) external {
require(msg.sender == owner, "Not owner");
_changeEvvmAddress(newAddr);
}

// Bad - no protection
function updateEvvmAddress(address newAddr) external {
_changeEvvmAddress(newAddr); // Anyone can change!
}

2. Validate Payment Parameters

// Good - validate before payment
require(amount > 0, "Invalid amount");
require(token == address(0) || token == address(1), "Invalid token");
makePay(from, token, amount, ...);

// Bad - no validation
makePay(from, token, amount, ...); // Could be invalid

3. Check Balances Before Withdrawals

// Good - check balance exists
uint256 balance = evvm.getBalance(address(this), token);
require(balance > 0, "No balance");
makeCaPay(to, token, balance);

// Bad - blind transfer (fails if no balance)
makeCaPay(to, token, amount); // Might revert

4. Handle Payment Failures

// Good - try-catch for payment
try this.makePay(from, token, amount, ...) {
// Success
} catch {
// Handle failure
revert("Payment failed");
}

// Note: makePay is internal, so you'd need to wrap it
function _tryPay(...) internal returns (bool) {
try evvm.pay(...) {
return true;
} catch {
return false;
}
}

Gas Considerations

OperationGas CostNotes
makePay~80,000-120,000Depends on EVVM state changes
makeCaPay~40,000-60,000Token transfer in EVVM
_changeEvvmAddress~5,000Simple storage update

Optimization Tips:

  • Batch multiple makeCaPay calls when possible
  • Cache evvm.getBalance() results if used multiple times
  • Validate inputs before expensive makePay calls

Best Practices

1. Document Payment Flow

/**
* @notice Processes order with payment through EVVM
* @dev Payment flow:
* 1. Validate order parameters
* 2. Process payment via makePay (customer -> service)
* 3. Reward fisher if applicable
* 4. Mark order as complete
*/
function processOrder(...) external { }

2. Use Constants for Token Addresses

address constant ETH_ADDRESS = address(0);
address constant MATE_ADDRESS = address(1);

function payWithETH(...) external {
makePay(from, ETH_ADDRESS, amount, ...);
}

3. Create Wrapper Functions

function receiveETHPayment(
address from,
uint256 amount,
uint256 nonce,
bytes memory signature
) internal {
makePay(from, address(0), amount, 0, nonce, true, signature);
}

function receiveMATEPayment(
address from,
uint256 amount,
uint256 nonce,
bytes memory signature
) internal {
makePay(from, address(1), amount, 0, nonce, true, signature);
}

4. Emit Events for Tracking

event PaymentReceived(address indexed from, address token, uint256 amount);
event FundsWithdrawn(address indexed to, address token, uint256 amount);

function processPayment(...) external {
makePay(from, token, amount, ...);
emit PaymentReceived(from, token, amount);
}

function withdraw(address to, address token) external {
uint256 amount = evvm.getBalance(address(this), token);
makeCaPay(to, token, amount);
emit FundsWithdrawn(to, token, amount);
}

See Also