Skip to content

Konkuk-25-FinalProject/Donet-Contract

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Donet 스마트 컨트랙트

블록체인 기반 기부 플랫폼을 위한 스마트 컨트랙트 모음입니다. CampaignFactory는 UUPS 프록시로 배포되고, 각 캠페인은 EIP-1167 클론 패턴과 OpenZeppelin 업그레이드 라이브러리를 활용해 안전·가스 효율적으로 실행됩니다.

📋 목차

🎯 주요 기능

CampaignFactory

  • ✅ UUPS 업그레이드 프록시 기반으로 배포되어 장기 운영 중에도 안전하게 기능 확장 가능
  • ✅ 단일 Campaign 구현체를 배포하고 EIP-1167 클론으로 각 캠페인을 가스 효율적으로 생성
  • ✅ Basis Point(만 분율) 기반 플랫폼 수수료율/수령자 관리 (기본 2.5%, 최대 10%)
  • allowedTokens 화이트리스트와 setTokenAllowanceBatch 지원으로 허용 자산을 중앙에서 제어
  • campaignById, campaignsByBeneficiary, 페이지네이션 가능한 getCampaigns로 백엔드 연동 최적화
  • getCampaignDetails, version 등 뷰 함수 제공으로 온체인 상태 조회 간소화
  • ✅ 배포 스크립트가 자동으로 deployments/<network>.json을 생성해 주소/설정 이력을 추적

Campaign

  • ✅ 네이티브 ETH와 단일 ERC-20 토큰 중 하나만 허용하여 기부 경로를 명확히 분기
  • ✅ 기부자별 contributionsdonors 배열을 관리해 개별/배치 환불(refundAll) 모두 지원
  • platformFeeRate·platformFeeRecipient를 캠페인별 스냅샷으로 저장하여 출금 시 자동 정산
  • ReentrancyGuardUpgradeable, SafeERC20, 체크-이펙트-상호작용, Pull over Push 패턴으로 안전성 확보
  • canRefund, canPayout, isExpired, balance 등 뷰 함수로 프론트엔드 UX 개선
  • RefundBatchProcessed 등 상세 이벤트로 모금/환불 상황을 실시간 추적 가능

🏗 컨트랙트 구조

contracts/
├── CampaignFactory.sol  # 캠페인 생성·관리, UUPS 프록시
└── Campaign.sol         # 개별 캠페인 로직 (EIP-1167 클론)

CampaignFactory

캠페인 생성 전용 컨트랙트로, 관리자(Owner)만 호출 가능합니다.

주요 상태 값

  • campaignImplementation: 클론에 사용되는 Campaign 구현체 주소
  • platformFeeRate / platformFeeRecipient: 플랫폼 수수료 구성
  • allowedTokens: 허용된 토큰 화이트리스트 (기본으로 ETH 허용)
  • campaigns, campaignById, campaignsByBeneficiary: 조회 및 백엔드 매핑 용도

핵심 함수

  • initialize(uint256,address): 프록시 초기화 (수수료율 ≤ 1000 = 10%)
  • createCampaign(...): 검증된 파라미터로 새로운 클론 생성
  • updateCampaignImplementation(address): 새로운 Campaign 구현체 등록
  • updatePlatformFeeRate(uint256) / updatePlatformFeeRecipient(address): 운영 정책 변경
  • setTokenAllowance(address,bool)setTokenAllowanceBatch(...): 토큰 화이트리스트 관리
  • getCampaigns, getCampaignsByBeneficiary, getCampaignDetails, getCampaignAddress, version
  • _authorizeUpgrade: UUPS 업그레이드 권한을 owner로 제한

Campaign

클론으로 배포되는 개별 캠페인 인스턴스입니다.

상태 변수

  • beneficiary, goal, deadline, raised, acceptedToken, paidOut
  • platformFeeRate, platformFeeRecipient
  • contributions, donors, donorRegistered, refundCursor

주요 함수

  • initialize(...): 프록시/클론 초기화
  • donateNative(), donate(uint256): ETH/토큰 기부
  • payout(): 목표 달성 시 누구나 호출 가능한 출금
  • refund(): 기부자 개별 환불
  • refundAll(uint256 batchSize): 관리자가 배치 환불 진행
  • isGoalReached(), balance(), isExpired(), canRefund(), canPayout(): 조회 함수

이벤트

  • CampaignCreated, TokenAllowanceUpdated, PlatformFeeRateUpdated, PlatformFeeRecipientUpdated, CampaignImplementationUpdated (Factory)
  • Donated, GoalReached, PaidOut, Refunded, RefundBatchProcessed (Campaign)

🚀 설치 및 설정

1. 의존성 설치

npm install

2. 환경 변수 설정

.env 파일을 생성하고 다음 값을 입력하세요.

# RPC URLs
SEPOLIA_RPC_URL=https://eth-sepolia.g.alchemy.com/v2/YOUR_API_KEY
MAINNET_RPC_URL=https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY

# Private Key (배포용 계정)
PRIVATE_KEY=your_private_key_here

# Etherscan API Key (컨트랙트 검증용)
ETHERSCAN_API_KEY=your_etherscan_api_key_here

3. 컴파일

npm run compile

📦 배포

로컬 네트워크 (테스트)

  1. Hardhat 노드 실행:
    npx hardhat node
  2. 다른 터미널에서 배포:
    npm run deploy:localhost

Sepolia 테스트넷

npm run deploy:sepolia

메인넷

npx hardhat run scripts/deploy.js --network mainnet

배포 결과 관리

scripts/deploy.js는 네트워크별 주소 및 설정을 deployments/<network>.json에 저장합니다.

{
  "network": "sepolia",
  "contracts": {
    "CampaignFactoryProxy": "0x...",
    "CampaignFactoryImplementation": "0x...",
    "CampaignImplementation": "0x..."
  },
  "config": {
    "platformFeeRate": 250,
    "platformFeeRecipient": "0x..."
  }
}

이 파일을 백엔드/프론트엔드에서 참조하면 일관된 주소를 유지할 수 있습니다.

💻 사용법

1. 캠페인 생성 (백엔드)

const factory = await ethers.getContractAt("CampaignFactory", factoryAddress);
const tx = await factory.createCampaign(
  campaignId,
  beneficiary,
  goal,
  deadline,
  acceptedToken // address(0) => ETH
);
const receipt = await tx.wait();
const event = receipt.logs.find((log) => log.fragment?.name === "CampaignCreated");
const campaignAddress = event.args.campaignAddress;

2. 허용 토큰 관리

await factory.setTokenAllowanceBatch(
  [ethers.ZeroAddress, usdcAddress],
  [true, true] // ETH + USDC 허용
);

3. 기부하기

ETH 기부

const campaign = await ethers.getContractAt("Campaign", campaignAddress);
await campaign.donateNative({ value: ethers.parseEther("1") });

ERC-20 기부

const token = await ethers.getContractAt("IERC20", tokenAddress);
await token.approve(campaignAddress, amount);
const campaign = await ethers.getContractAt("Campaign", campaignAddress);
await campaign.donate(amount);

4. 출금하기 (목표 달성 시)

const campaign = await ethers.getContractAt("Campaign", campaignAddress);
if (await campaign.canPayout()) {
  await campaign.payout();
}

5. 환불하기 (마감 실패 시)

개별 환불

if (await campaign.canRefund()) {
  await campaign.refund(); // 호출자 본인 환불
}

배치 환불

// batchSize 만큼 donors 배열을 순회하며 환불 처리
await campaign.refundAll(50);

6. 상태 조회

const details = await factory.getCampaignDetails(campaignAddress);
const [
  beneficiary,
  goal,
  deadline,
  raised,
  acceptedToken,
  paidOut,
  isGoalReached,
  canRefund,
  canPayout
] = details;

const campaign = await ethers.getContractAt("Campaign", campaignAddress);
const currentBalance = await campaign.balance();

🛠 예시 스크립트

  • scripts/deploy.js: UUPS 프록시 배포 + deployments/<network>.json 생성
  • scripts/create-campaign-example.js: 최신 배포 정보를 읽어 샘플 캠페인을 생성
    npx hardhat run scripts/create-campaign-example.js --network localhost
  • scripts/donate-example.js: 지정 캠페인에 ETH 기부
    npx hardhat run scripts/donate-example.js --network localhost -- <캠페인주소> 1

♻ 업그레이드

  1. 컨트랙트 수정 후 컴파일
    npm run compile
  2. 업그레이드 안전성 검증
    npm run validate-upgrade -- --network sepolia
  3. UUPS 업그레이드 실행
    npm run upgrade:sepolia
  4. 스크립트가 자동으로 새로운 구현체 주소와 버전을 deployments/<network>.json에 반영합니다.

추가로, updateCampaignImplementation을 이용하면 Factory 자체 업그레이드 없이도 새로운 Campaign 구현체를 등록할 수 있습니다.

🔗 API 연동

백엔드와의 연동 흐름:

1. 캠페인 생성 플로우

사용자 → 백엔드 → 스마트 컨트랙트
         ↓
    검증 & DB 저장
         ↓
    Factory.createCampaign()
         ↓
    캠페인 주소 반환
         ↓
    DB에 주소 저장

2. 기부 플로우

사용자 → 프론트엔드 → 스마트 컨트랙트
                    ↓
                Campaign.donate()
                    ↓
                이벤트 발생
                    ↓
    백엔드 ← 이벤트 리스닝
      ↓
   DB 업데이트

3. 주요 이벤트

CampaignFactory

event CampaignCreated(
    uint256 indexed campaignId,
    address indexed campaignAddress,
    address indexed beneficiary,
    uint256 goal,
    uint256 deadline,
    address acceptedToken,
    uint256 timestamp
);

event CampaignImplementationUpdated(address oldImplementation, address newImplementation);
event PlatformFeeRateUpdated(uint256 oldRate, uint256 newRate);
event PlatformFeeRecipientUpdated(address oldRecipient, address newRecipient);
event TokenAllowanceUpdated(address indexed token, bool allowed);

Campaign

event Donated(address indexed donor, uint256 amount, uint256 timestamp);
event GoalReached(uint256 total, uint256 timestamp);
event PaidOut(address indexed beneficiary, uint256 amount, uint256 platformFee, uint256 timestamp);
event Refunded(address indexed donor, uint256 amount, uint256 timestamp);
event RefundBatchProcessed(uint256 processedCount, uint256 nextCursor, uint256 timestamp);

🔐 보안 고려사항

구현된 보안 패턴

  1. ReentrancyGuardUpgradeable: 재진입 공격 방지
  2. 체크-이펙트-상호작용: 외부 호출 전에 상태 확정
  3. Pull over Push: 수혜자·기부자가 직접 호출하는 안전한 송금 방식
  4. SafeERC20: ERC-20 토큰 처리를 안전하게 래핑
  5. Ownable + UUPS: 관리자 전용 권한 및 업그레이드 통제
  6. 커스텀 에러: 가스 절감과 명확한 실패 원인 제공

권장사항

  • ✅ 배포 전 충분한 테스트와 감사(Audit) 진행
  • ✅ 멀티시그 지갑으로 Factory 소유권 관리
  • upgrade.js 실행 전 validate-upgrade.js로 저장소 충돌 검증
  • ✅ 이벤트 리스너를 운영해 실시간 모니터링 및 환불 배치 자동화
  • ✅ 필요한 경우 refundAll을 백엔드 잡으로 호출해 기부자 UX 개선

📊 가스 최적화

  • EIP-1167 클론으로 캠페인 생성 비용 ~90% 절감
  • 플랫폼 수수료를 Basis Point로 저장해 부동소수점 연산 회피
  • 배치 환불(refundAll)로 단일 트랜잭션 당 처리량 제어
  • View 함수(getCampaigns, getCampaignDetails)로 오프체인 조회 비용 최소화
  • 업그레이드 대비 __gap 슬롯 확보로 재배포 없이 확장 가능

🧪 테스트

npm test

Donet-Contract

About

Donet Smart Contract

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published