PullPayment 는 스마트 컨트랙트에서 여러 사용자에게 지불해야 할 금액을 관리하면서, 재진입 공격을 방지하고 가스비 문제를 해결하기 위해 사용됩니다.
PullPayment는 사용자에게 직접 돈을 보내는 방식(Push-Payment)이 아닌, 사용자가 스스로 요청하여 지불을 받을 수 있도록 설계되었습니다.
주로 사용되는 상황
- 경매: 경매에서 낙찰되지 않은 사용자가 입찰금을 환불받아야 하는 경우.
- 마켓플레이스: 판매자와 구매자 간 결제를 중재하는 경우.
- 펀드 배분: 다수의 수익자에게 분배할 금액이 있을 때.
- 보안: 외부 호출로 인해 발생할 수 있는 재진입 공격을 방지해야 할 때.
코드 예시
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/security/PullPayment.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract Auction is PullPayment, Ownable {
uint256 public highestBid;
address public highestBidder;
// Function to place a bid
function bid() external payable {
require(msg.value > highestBid, "Bid must be higher than current highest bid");
// Refund the previous highest bidder using PullPayment
if (highestBidder != address(0)) {
_asyncTransfer(highestBidder, highestBid);
}
highestBid = msg.value;
highestBidder = msg.sender;
}
// Function to withdraw funds (for testing purposes)
function withdrawPayments() external {
withdrawPayments(payable(msg.sender));
}
}
함수 설명
- _asyncTransfer(address recipient, uint256 amount)
- 설명 : 특정 수신인에게 비동기적으로 지불 금액을 기록합니다. 실질적으로 금액을 즉시 전송하지 않고, PullPayment 컨트랙트의 내부 payments 매핑에 금액을 기록합니다.
- 작동방식:
- payments[recipient] += amount;
- 수신인의 총 금액이 누적됩니다.
- withdrawPayments(address payable payee)
- 설명: 특정 사용자가 자신의 누적된 지불금을 인출할 수 있습니다.
- 작동방식:
- payments[payee] 값을 확인.
- 값이 0보다 크면, 해당 금액을 payee 에게 전송.
- 성공적으로 전송 후 payments[payee] = 0; 으로 초기화
- payments(address payee)
- 설명: 특정 사용자(payee)가 받을 수 있는 잔액을 반환합니다.
- 작동방식:
- 단순히 payments 매핑에서 값을 조회.
작동 흐름
- 비동기 전송 기록: _asyncTransfer를 사용하여 지불금을 payments에 기록
- 사용자 요청: 사용자는 withdrawPayments를 호출하여 자신의 금액ㅇ르 요청.
- 안전한 전송: 재진입 공격 방지를 위해 상태를 먼저 업데이트(payments[payee] = 0). 이후 payee.transfer(amount)로 금액 전송
장점
- 재진입 공격 방지: 직접적인 transfer가 아니라 withdraw 방식으로 외부 호출의 영향을 줄임.
- 효율적인 지불 관리: 여러 사용자의 결제 요청을 개별적으로 처리
- 가스비 절약: 모든 지불을 한 번에 처리하지 않고 필요한 사용자만 요청하게 함.
레퍼런스
'Solidity' 카테고리의 다른 글
| Ethereum 에서 사용할 수 있는 주요 오라클 서비스 (2) | 2025.01.03 |
|---|---|
| 외부 오라클을 사용한 난수 생성 방법 (5) | 2025.01.03 |
| delegate call 이란? (1) | 2025.01.03 |
| Solidity에서의 예외처리: require vs. assert (0) | 2025.01.02 |
| 스마트 컨트랙트 개발 시의 보안 취약점 (1) | 2025.01.02 |