탈중앙화 금융(DeFi)은 중개자를 제거하고 스마트 컨트랙트를 활용해 거래를 자동으로 처리함으로써 대출 방식을 근본적으로 바꿔놓았습니다. 과거에는 은행과 서류 작업이 필요했던 일들이 이제는 몇 번의 클릭만으로, 그것도 모두 온체인에서 이루어집니다.
Linea는 제로 지식 롤업(zk-rollup)을 사용해 트랜잭션을 일괄 처리하고 압축함으로써, 이더리움 메인넷 대비 향상된 확장성과 낮은 가스 비용으로 DeFi 애플리케이션을 구축할 수 있도록 개발자를 지원합니다.
이 가이드에서는 Linea 위에 간단한 대출 스마트 컨트랙트를 구축하고 배포하는 과정을 단계별로 살펴봅니다. 담보 관리, 대출 실행, 상환 규칙 설정 방법을 배우게 되며, 이는 모두 DeFi 대출 시스템의 핵심 기능입니다.
가이드를 마치면 작동하는 스마트 컨트랙트와 함께 DeFi 대출이 내부적으로 어떻게 동작하는지에 대한 탄탄한 이해를 갖추게 됩니다. 블록체인 개발이 처음이든, 실력을 한 단계 높이고 싶든, 이 단계별 가이드가 Linea에서 개발을 시작하는 데 필요한 도구를 제공할 것입니다.
사전 요구 사항
Solidity에 대한 기본적인 이해가 필요합니다.
PC에 Nodejs와 Foundry가 설치되어 있어야 합니다.
컨트랙트 개요
이 컨트랙트는 다음 기능을 제공합니다:
담보 예치: 사용자는 컨트랙트에 담보를 예치할 수 있으며, 세부 정보는 스마트 컨트랙트에 안전하게 저장됩니다.
대출: 사용자는 예치한 담보를 기반으로 대출을 받을 수 있습니다.
상환: 사용자는 대출금을 상환하여 담보를 잠금 해제할 수 있습니다.
담보 출금: 사용자는 활성 대출로 인해 잠기지 않은 담보를 출금할 수 있습니다.
프로젝트 설정
이 프로젝트에는 Foundry 프레임워크를 사용합니다. Foundry가 설치된 상태에서 아래 명령어를 실행하여 새 프로젝트를 시작합니다:
forge init lending
Vscode 또는 선호하는 코드 에디터에서 market 폴더를 열고, scripts/counter.s.sol, src/counter.sol, test/counter.t.sol 파일을 삭제합니다.
모든 의존성 설치
forge install foundry-rs/forge-std --no-commit && forge install OpenZeppelin/openzeppelin-contracts --no-commit대출 스마트 컨트랙트 전체 코드
pragma solidity ^0.8.13;
import "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import "lib/openzeppelin-contracts/contracts/access/Ownable.sol";
contract LendingBorrowing is Ownable {
struct Loan {
uint256 amount;
uint256 collateral;
bool isActive;
}
IERC20 public immutable collateralToken;
IERC20 public immutable lendingToken;
uint256 public collateralFactor;
mapping(address => uint256) public collateralBalances;
mapping(address => Loan) public loans;
event CollateralDeposited(address indexed user, uint256 amount);
event CollateralWithdrawn(address indexed user, uint256 amount);
event LoanTaken(address indexed user, uint256 amount);
event LoanRepaid(address indexed user, uint256 amount);
constructor(IERC20 _collateralToken, IERC20 _lendingToken, uint256 _collateralFactor) Ownable(msg.sender) {
require(_collateralFactor <= 100, "Collateral factor must be <= 100");
collateralToken = _collateralToken;
lendingToken = _lendingToken;
collateralFactor = _collateralFactor;
}
function setCollateralFactor(uint256 _newFactor) external onlyOwner {
require(_newFactor <= 100, "Collateral factor must be <= 100");
collateralFactor = _newFactor;
}
function depositCollateral(uint256 _amount) external {
require(_amount > 0, "Amount must be greater than zero");
collateralBalances[msg.sender] += _amount;
collateralToken.transferFrom(msg.sender, address(this), _amount);
emit CollateralDeposited(msg.sender, _amount);
}
function withdrawCollateral(uint256 _amount) external {
require(_amount > 0, "Amount must be greater than zero");
require(collateralBalances[msg.sender] >= _amount, "Insufficient collateral");
uint256 maxWithdrawable = collateralBalances[msg.sender] - _loanRequiredCollateral(msg.sender);
require(_amount <= maxWithdrawable, "Cannot withdraw collateral locked for a loan");
collateralBalances[msg.sender] -= _amount;
collateralToken.transfer(msg.sender, _amount);
emit CollateralWithdrawn(msg.sender, _amount);
}
function takeLoan(uint256 _amount) external {
require(_amount > 0, "Amount must be greater than zero");
require(loans[msg.sender].isActive == false, "Existing loan must be repaid first");
uint256 maxLoan = (collateralBalances[msg.sender] * collateralFactor) / 100;
require(_amount <= maxLoan, "Loan exceeds collateral limit");
loans[msg.sender] = Loan({
amount: _amount,
collateral: collateralBalances[msg.sender],
isActive: true
});
lendingToken.transfer(msg.sender, _amount);
emit LoanTaken(msg.sender, _amount);
}
function repayLoan(uint256 _amount) external {
require(_amount > 0, "Amount must be greater than zero");
Loan storage userLoan = loans[msg.sender];
require(userLoan.isActive, "No active loan");
require(_amount <= userLoan.amount, "Repay amount exceeds loan");
lendingToken.transferFrom(msg.sender, address(this), _amount);
userLoan.amount -= _amount;
if (userLoan.amount == 0) {
userLoan.isActive = false;
}
emit LoanRepaid(msg.sender, _amount);
}
function _loanRequiredCollateral(address _user) internal view returns (uint256) {
Loan memory userLoan = loans[_user];
if (!userLoan.isActive) return 0;
return (userLoan.amount * 100) / collateralFactor;
}
function getLoanDetails(address _user) external view returns (uint256 amount, uint256 collateral, bool isActive) {
Loan memory userLoan = loans[_user];
return (userLoan.amount, userLoan.collateral, userLoan.isActive);
}
}
코드 개요
Loan 구조체
struct Loan {
uint256 amount;
uint256 collateral;
bool isActive;
}
Loan 구조체는 사용자가 실행한 개별 대출의 세부 정보를 저장하기 위한 청사진 역할을 합니다. 구조체 내 각 필드는 대출 상태와 조건에 관한 핵심 정보를 나타냅니다. 아래는 각 필드에 대한 설명입니다:
Amount: 사용자가 빌린 토큰의 양을 저장합니다. 이 값은 사용자가 얼마를 상환해야 하는지 추적하고, 상환 의무를 결정하며, 대출 조건을 검증하는 데 사용됩니다.
Collateral: 대출을 담보하기 위해 사용자가 예치한 담보의 가치를 저장합니다. 대출자의 자금을 보호하고, 담보 비율에 따라 최대 대출 가능 금액을 결정하는 데 도움을 줍니다.
IsActive: 대출이 현재 활성 상태인지 여부를 나타냅니다. 이 필드는 대출 상태를 추적하고, 기존 대출이 완전히 상환되기 전까지 사용자가 새 대출을 받지 못하도록 방지합니다.
상태 변수
IERC20 public immutable collateralToken;
IERC20 public immutable lendingToken;
uint256 public collateralFactor;
위 코드 스니펫은 세 가지 핵심 상태 변수를 정의하며, 각각 대출 플랫폼의 기능을 구현하고 보안을 유지하는 데 특정 역할을 담당합니다. 아래는 각 변수에 대한 설명입니다:
CollateralToken: 사용자가 대출 담보로 예치하는 토큰을 나타냅니다. 예를 들어, USDC가 담보로 사용되는 경우, 이 변수는 USDC 컨트랙트의 블록체인 주소를 저장합니다.
LendingToken: 플랫폼이 대출자에게 빌려주는 토큰을 나타냅니다. USDT가 대출 토큰인 경우, USDT 컨트랙트의 블록체인 주소를 저장합니다.
CollateralFactor: 사용자가 담보 가치의 몇 퍼센트까지 빌릴 수 있는지를 정의합니다. 100이 담보 가치의 100%를 나타내는 정수로 표현됩니다. 예를 들어, collateralFactor가 50이면 사용자는 담보 가치의 최대 50%까지 빌릴 수 있습니다. 이는 대출 시스템의 보안을 유지하기 위해 대출이 초과 담보 상태를 유지하도록 보장합니다. 사용자가 $1,000 상당의 담보를 예치하고 collateralFactor가 50%라면, 최대 대출 가능 금액은 $500입니다.
매핑
mapping(address => uint256) public collateralBalances;
mapping(address => Loan) public loans;
CollateralBalances 매핑: 각 사용자가 예치한 담보 금액을 추적합니다. 모든 사용자는 자신의 주소와 연결된 고유한 담보 잔액을 가집니다. 이 매핑은 사용자가 대출을 받거나 자금을 출금하기에 충분한 담보를 보유하고 있는지 검증하는 데 도움을 줍니다.
Loans 매핑: 사용자의 주소를 대출 정보와 연결하여 각 사용자의 활성 대출 세부 정보를 추적합니다. 이를 통해 사용자가 활성 대출이 있는 동안 새 대출을 받지 못하도록 방지합니다.
이벤트
event CollateralDeposited(address indexed user, uint256 amount);
event CollateralWithdrawn(address indexed user, uint256 amount);
event LoanTaken(address indexed user, uint256 amount);
event LoanRepaid(address indexed user, uint256 amount);
CollateralDeposited 이벤트 – 사용자가 컨트랙트에 담보를 예치할 때 발생합니다. 사용자의 주소와 예치 금액을 기록합니다.
CollateralWithdrawn 이벤트 – 사용자가 컨트랙트에서 담보를 출금할 때 발생합니다. 사용자의 주소와 출금 금액을 기록합니다.
LoanTaken 이벤트 – 사용자가 대출을 실행할 때 발생합니다. 사용자의 주소와 대출 금액을 기록합니다.
LoanRepaid 이벤트 – 사용자가 대출을 상환할 때 발생합니다. 사용자의 주소와 상환 금액을 기록합니다.
생성자
constructor(IERC20 _collateralToken, IERC20 _lendingToken, uint256 _collateralFactor) Ownable(msg.sender) {
require(_collateralFactor <= 100, "Collateral factor must be <= 100");
collateralToken = _collateralToken;
lendingToken = _lendingToken;
collateralFactor = _collateralFactor;
}
생성자는 컨트랙트 배포 시 단 한 번만 실행되는 Solidity의 특수 함수입니다. 생성자의 목적은 다음과 같습니다:
컨트랙트의 초기 설정을 구성합니다.
핵심 상태 변수에 값을 할당합니다.
Ownable을 통해 소유권 및 접근 제어를 설정합니다.
생성자는 _collateralToken, _lendingToken, _collateralFactor 세 가지 인수를 받습니다.
_collateralToken – 대출 담보로 사용되는 토큰입니다.
_lendingToken – 사용자가 시스템에서 빌리는 토큰입니다.
_collateralFactor – 최대 대출 한도를 결정하는 담보 비율로, 100%를 초과하지 않도록 보장합니다.
초기화 시 생성자는 다음을 수행합니다:
_collateralToken을 담보 자산으로 지정합니다.
_lendingToken을 대출 토큰으로 설정합니다.
_collateralFactor를 구성하여 담보 대 대출 비율을 설정합니다.
또한, **Ownable(msg.sender)**는 컨트랙트 배포자가 소유자가 되도록 보장하여, 필요 시 collateralFactor 수정과 같은 관리 권한을 부여합니다.
담보 비율 설정
function setCollateralFactor(uint256 _newFactor) external onlyOwner {
require(_newFactor <= 100, "Collateral factor must be <= 100");
collateralFactor = _newFactor;
}
이 함수는 컨트랙트 소유자가 담보와 대출 가능 자금 간의 비율을 제어하는 담보 비율을 수정할 수 있도록 하는 중요한 관리 기능입니다. 담보 비율은 사용자가 담보를 기반으로 빌릴 수 있는 최대 금액을 결정하는 데 사용됩니다. 이 비율을 업데이트할 수 있도록 함으로써, 컨트랙트는 변화하는 시장 상황이나 리스크 프로파일에 적응할 수 있습니다.
이 함수는 external로 표시되어 컨트랙트 외부에서만 호출할 수 있습니다. onlyOwner 수정자는 컨트랙트 소유자(일반적으로 배포자)만 이 함수를 호출할 수 있도록 하여 엄격한 접근 제어를 제공합니다.
이 함수는 새로운 담보 비율이 0에서 100 사이의 유효한 백분율인지 확인하여 비현실적이거나 안전하지 않은 값이 설정되는 것을 방지합니다. 예를 들어, 100을 초과하는 비율은 사용자가 담보 가치보다 더 많이 빌릴 수 있게 허용할 수 있습니다.
이 함수는 _newFactor로 전달된 새 값으로 collateralFactor 상태 변수를 업데이트합니다.
담보 예치
function depositCollateral(uint256 _amount) external {
require(_amount > 0, "Amount must be greater than zero");
collateralBalances[msg.sender] += _amount;
collateralToken.transferFrom(msg.sender, address(this), _amount);
emit CollateralDeposited(msg.sender, _amount);
}
이 함수는 사용자가 컨트랙트에 담보를 예치할 수 있도록 하는 핵심 기능입니다. 사용자가 대출을 받기 전에 반드시 거쳐야 하는 단계이며, 담보가 컨트랙트 내에서 적절히 기록되고 저장되도록 보장합니다.
이 함수는 사용자가 0보다 큰 유효한 금액을 제공하는지 검증하고, 예치된 금액만큼 사용자의 담보 잔액을 증가시킵니다. transferFrom을 사용하여 사용자로부터 컨트랙트로 토큰을 전송하며, 이를 위해 사용자는 이 함수를 호출하기 전에 컨트랙트가 자신의 토큰을 사용할 수 있도록 승인해야 합니다.
이 함수는 다음을 가능하게 합니다:
담보 예치: 사용자는 미리 정의된 토큰(collateralToken)의 지정된 금액을 컨트랙트에 예치합니다. 이 담보는 사용자가 받는 대출의 보증 역할을 합니다.
담보 잔액 추적: 예치된 금액은 collateralBalances 매핑에서 추적되는 사용자의 담보 잔액에 추가됩니다.
대출 활성화: 담보는 사용자가 얼마나 빌릴 수 있는지 계산하는 기준이 됩니다.
담보 출금
function withdrawCollateral(uint256 _amount) external {
require(_amount > 0, "Amount must be greater than zero");
require(collateralBalances[msg.sender] >= _amount, "Insufficient collateral");
uint256 maxWithdrawable = collateralBalances[msg.sender] - _loanRequiredCollateral(msg.sender);
require(_amount <= maxWithdrawable, "Cannot withdraw collateral locked for a loan");
collateralBalances[msg.sender] -= _amount;
collateralToken.transfer(msg.sender, _amount);
emit CollateralWithdrawn(msg.sender, _amount);
}
이 함수는 사용자가 예치한 담보의 일부 또는 전부를 출금할 수 있도록 하되, 활성 대출의 보증으로 잠긴 담보는 출금할 수 없도록 보장합니다.
이 함수는 출금 금액이 0보다 큰지 검증하고, 사용자가 요청한 출금을 충당할 충분한 가용 담보를 보유하고 있는지 확인합니다. 활성 대출에 대해 잠긴 담보 비율을 계산하고, 출금 금액만큼 사용자의 담보 잔액을 감소시키며, 요청된 토큰을 컨트랙트에서 사용자에게 전송합니다.
요약하면, 이 함수는 다음을 가능하게 합니다:
담보 출금: 사용자가 컨트랙트에서 가용 담보를 출금할 수 있습니다.
대출 보호: 활성 대출을 보증하는 데 필요한 담보는 출금할 수 없도록 보장합니다.
상태 관리: 성공적인 출금 후 사용자의 담보 잔액을 업데이트합니다.
대출 실행
function takeLoan(uint256 _amount) external {
require(_amount > 0, "Amount must be greater than zero");
require(loans[msg.sender].isActive == false, "Existing loan must be repaid first");
uint256 maxLoan = (collateralBalances[msg.sender] * collateralFactor) / 100;
require(_amount <= maxLoan, "Loan exceeds collateral limit");
loans[msg.sender] = Loan({
amount: _amount,
collateral: collateralBalances[msg.sender],
isActive: true
});
lendingToken.transfer(msg.sender, _amount);
emit LoanTaken(msg.sender, _amount);
}
이 함수는 사용자가 예치한 담보를 기반으로 컨트랙트에서 토큰을 빌릴 수 있도록 합니다. 대출 금액은 사용자의 예치 담보와 컨트랙트의 담보 비율에 의해 제한되어, 대출이 안전한 한도 내에서 이루어지도록 보장합니다. 이 함수는 시스템 보안을 유지하면서 대출을 촉진하는 핵심 역할을 합니다.
이 함수는 다음을 보장합니다:
대출 금액이 0보다 큽니다.
사용자에게 활성 대출이 없어, 동시에 여러 대출을 받는 것을 방지합니다.
요청된 대출이 담보를 기반으로 한 최대 대출 한도를 초과하지 않습니다.
loans 매핑이 사용자의 대출 세부 정보로 업데이트됩니다.
대출된 토큰이 컨트랙트에서 사용자에게 전송됩니다.
요약하면, 이 함수는 다음을 가능하게 합니다:
대출 실행: 사용자가 예치한 담보를 기반으로 토큰을 빌릴 수 있습니다.
담보 관리: 대출 금액이 사용자의 대출 한도 내에 유지되도록 보장합니다.
대출 추적: 과도한 대출을 방지하고 시스템 무결성을 보장하기 위해 사용자 대출 기록을 유지합니다.
대출 상환
function repayLoan(uint256 _amount) external {
require(_amount > 0, "Amount must be greater than zero");
Loan storage userLoan = loans[msg.sender];
require(userLoan.isActive, "No active loan");
require(_amount <= userLoan.amount, "Repay amount exceeds loan");
lendingToken.transferFrom(msg.sender, address(this), _amount);
userLoan.amount -= _amount;
if (userLoan.amount == 0) {
userLoan.isActive = false;
}
emit LoanRepaid(msg.sender, _amount);
}
이 함수는 사용자가 활성 대출을 상환할 수 있도록 하며, 부분 상환 또는 전액 상환 모두 가능합니다. 대출 상태를 업데이트하고 상환이 올바르게 처리되도록 하여 시스템의 무결성을 유지합니다.
이 함수는 다음 검사와 작업을 수행합니다:
잘못된 작업을 방지하기 위해 상환 금액이 0보다 큰지 확인합니다.
사용자의 대출 세부 정보를 가져오고 진행하기 전에 활성 대출이 있는지 확인합니다.
상환 금액이 남은 대출 잔액을 초과하지 않도록 보장합니다.
지정된 금액에 대한 사전 승인을 요구하며, 사용자로부터 컨트랙트로 상환 금액을 전송합니다.
상환 금액만큼 대출 잔액을 감소시킵니다.
대출이 완전히 상환되면, 함수는 대출을 비활성으로 표시합니다.
요약하면, 이 함수는 다음을 가능하게 합니다:
대출 상환: 대출자가 빌린 금액을 컨트랙트에 반환할 수 있습니다.
대출 추적: 상환 후 대출 잔액과 상태를 업데이트합니다.
투명성: 온체인에서 상환 트랜잭션을 기록하기 위해 이벤트를 발생시킵니다.
테스트
src 폴더에 Token.sol 파일을 생성하고 아래 코드를 파일에 추가합니다.
pragma solidity ^0.8.0;
import {ERC20} from "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
contract Token is ERC20 {
constructor(string memory name, string memory symbol) ERC20(name, symbol){}
function mint(address user, uint256 amount) public {
_mint(user, amount);
}
}
test 폴더에 lending.t.sol 파일을 생성하고 아래 코드를 파일에 추가합니다.
pragma solidity ^0.8.13;
import {Test, console} from "forge-std/Test.sol";
import { LendingBorrowing} from "../src/Lending.sol";
import { Token } from "../src/Token.sol";
contract LendingTest is Test {
LendingBorrowing public lending;
Token public collateral;
Token public lendingToken;
address public user1 = makeAddr("user1");
address public user2 = makeAddr("user2");
address public user3 = makeAddr("user3");
function setUp() public {
collateral = new Token("collateralToken", "ct");
lendingToken = new Token("lendingToken", "lt");
lending = new LendingBorrowing(collateral, lendingToken, 80);
collateral.mint(user1, 100 ether);
lendingToken.mint(address(lending), 1000 ether);
}
function test_DepositCollateral() public {
vm.startPrank(user1);
collateral.approve(address(lending), 100 ether);
lending.depositCollateral(50 ether);
assert(collateral.balanceOf(user1) == 50 ether);
assert(collateral.balanceOf(address(lending)) == 50 ether);
}
function test_TakeLoan() public {
vm.startPrank(user1);
collateral.approve(address(lending), 100 ether);
lending.depositCollateral(50 ether);
vm.expectRevert();
lending.takeLoan(100 ether);
vm.expectRevert();
lending.takeLoan(49 ether);
lending.takeLoan(40 ether);
assert(lendingToken.balanceOf(user1) == 40 ether);
}
function test_RepayLoan() public {
vm.startPrank(user1);
collateral.approve(address(lending), 100 ether);
lending.depositCollateral(50 ether);
lending.takeLoan(40 ether);
lendingToken.approve(address(lending), 40 ether);
lending.repayLoan(40 ether);
assert(lendingToken.balanceOf(user1) == 0 ether);
}
function test_WithdrawCollateral() public {
vm.startPrank(user1);
collateral.approve(address(lending), 100 ether);
lending.depositCollateral(50 ether);
lending.takeLoan(40 ether);
lendingToken.approve(address(lending), 40 ether);
lending.repayLoan(40 ether);
lending.withdrawCollateral(50 ether);
assert(lendingToken.balanceOf(user1) == 0 ether);
assert(collateral.balanceOf(user1) == 100 ether);
}
}
테스트 설정
이 함수는 각 테스트 함수 전에 호출되며 상태를 초기화하는 데 사용됩니다.
토큰 컨트랙트를 배포합니다: collateral 및 lendingToken 컨트랙트가 모두 배포됩니다.
LendingBorrowing 컨트랙트를 배포합니다: LendingBorrowing 컨트랙트는 담보 토큰, lendingToken, 그리고 담보 대 대출 비율 80%(사용자가 담보 가치의 최대 80%까지만 빌릴 수 있음을 의미)로 초기화됩니다.
토큰을 발행합니다:- 사용자 1에게 담보 토큰 100개가 발행됩니다.
대출 컨트랙트에 대출 토큰 1000개가 발행됩니다.
담보 예치 테스트
이 함수는 사용자가 대출 컨트랙트에 담보를 예치할 수 있는지 테스트합니다. 사용자 1은 대출 컨트랙트가 자신의 담보 토큰 최대 100개를 사용할 수 있도록 승인하고, 담보 토큰 50개를 컨트랙트에 예치합니다. 이 함수는 다음을 검증합니다:
사용자 1의 잔액이 50개 토큰만큼 감소합니다.
대출 컨트랙트의 담보 잔액이 50개 토큰만큼 증가합니다.
대출 실행 테스트
이 함수는 사용자가 예치한 담보를 기반으로 대출을 받을 수 있는지 테스트합니다. 사용자 1은 담보 토큰 50개를 예치하고 대출 컨트랙트가 이를 사용할 수 있도록 승인합니다.
테스트는 허용된 금액을 초과하는 대출 또는 (담보 기반) 최소 요구 금액 미만의 대출이 되돌려지는지 확인합니다.
사용자 1은 lendingToken 토큰 40개의 대출을 성공적으로 실행합니다.
테스트는 대출 실행 후 사용자 1이 lendingToken 토큰 40개를 받는지 검증합니다.
대출 상환 테스트
이 함수는 사용자가 대출을 올바르게 상환할 수 있는지 테스트합니다.
사용자 1은 담보 토큰 50개를 예치하고 lendingToken 토큰 40개의 대출을 실행합니다.
사용자 1은 대출 컨트랙트가 lendingToken 토큰 40개를 사용할 수 있도록 승인하고 대출을 상환합니다.
테스트는 대출 상환 후 사용자 1의 lendingToken 잔액이 0인지 검증합니다.
담보 출금 테스트
이 함수는 사용자가 대출 상환 후 담보를 출금할 수 있는지 테스트합니다.
사용자 1은 담보 토큰 50개를 예치하고, lendingToken 토큰 40개의 대출을 실행한 후 대출을 상환합니다.
대출 상환 후, 사용자 1은 담보를 출금합니다.
테스트는 다음을 검증합니다:- 사용자 1이 담보 토큰 50개를 돌려받습니다.
사용자 1의 잔액이 100입니다(초기에 토큰 100개를 발행받았으므로).
대출 상환 후 사용자 1의 lendingToken 잔액이 0입니다.
테스트 실행
forge test
Linea에서의 배포 및 검증
.env에 필요한 변수를 추가합니다. .env 파일은 루트 레벨에 위치해야 합니다.
LINEA_RPC_URL=https://linea-mainnet.infura.io/v3/
PRIVATE_KEY=*************************************************
foundry.toml에 아래 세부 정보를 추가합니다.
[etherscan]
linea = { key = "${LINEA_API_KEY}", url = "https://api-sepolia.lineascan.build/api" }
Makefile을 생성하고 아래 세부 정보를 추가합니다.
deploy:
forge create src/Lending.sol: LendingBorrowing --rpc-url $(LINEA_RPC_URL) --private-key $(PRIVATE_KEY)
verify:; forge verify-contract --rpc-url $(LINEA_RPC_URL) --chain linea <contract address> src/Lending.sol:LendingBorrowing --constructor-args arg1 arg2
배포
make deploy
검증
make verify
결론
간단한 대출 스마트 컨트랙트를 구축하는 것은 DeFi와 블록체인 개발을 이해하기 위한 기초를 제공합니다. 이 가이드에서 다룬 내용은 다음과 같습니다:
담보 예치 – 사용자가 대출 보증으로 자산을 잠급니다.
대출 실행 – 대출자가 담보를 기반으로 자금에 접근합니다.
대출 상환 – 사용자가 빌린 자금을 반환하여 부채를 청산합니다.
담보 출금 – 사용자가 전액 상환 후 담보를 회수합니다.
이러한 요소들을 통합하여 기능적이고 자동화된 대출 시스템을 구축했습니다. DeFi가 성장함에 따라, 이러한 개념들은 더욱 발전된 금융 애플리케이션을 위한 구성 요소로 활용됩니다.
더 많은 실습 튜토리얼과 블록체인 개발에 대한 심층 분석을 원하신다면 MetaMask Developer YouTube 채널을 확인하시고, Twitter에서 팔로우하여 최신 인사이트를 받아보세요.