Created
February 16, 2021 20:31
-
-
Save combattardigrade/6961f3bf019b561e6e4ed87751b5abc0 to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.6.0+commit.26b70077.js&optimize=false&runs=200&gist=
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @title CrosschainLoans | |
* @author BlitsLabs (www.blits.net) | |
*/ | |
pragma solidity 0.6.0; | |
interface ERC20 { | |
/** | |
* @dev Returns the amount of tokens in existence. | |
*/ | |
function totalSupply() external view returns (uint256); | |
/** | |
* @dev Returns the amount of tokens owned by `account`. | |
*/ | |
function balanceOf(address account) external view returns (uint256); | |
/** | |
* @dev Moves `amount` tokens from the caller's account to `recipient`. | |
* | |
* Returns a boolean value indicating whether the operation succeeded. | |
* | |
* Emits a {Transfer} event. | |
*/ | |
function transfer(address recipient, uint256 amount) external returns (bool); | |
/** | |
* @dev Returns the remaining number of tokens that `spender` will be | |
* allowed to spend on behalf of `owner` through {transferFrom}. This is | |
* zero by default. | |
* | |
* This value changes when {approve} or {transferFrom} are called. | |
*/ | |
function allowance(address owner, address spender) external view returns (uint256); | |
/** | |
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens. | |
* | |
* Returns a boolean value indicating whether the operation succeeded. | |
* | |
* IMPORTANT: Beware that changing an allowance with this method brings the risk | |
* that someone may use both the old and the new allowance by unfortunate | |
* transaction ordering. One possible solution to mitigate this race | |
* condition is to first reduce the spender's allowance to 0 and set the | |
* desired value afterwards: | |
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 | |
* | |
* Emits an {Approval} event. | |
*/ | |
function approve(address spender, uint256 amount) external returns (bool); | |
/** | |
* @dev Moves `amount` tokens from `sender` to `recipient` using the | |
* allowance mechanism. `amount` is then deducted from the caller's | |
* allowance. | |
* | |
* Returns a boolean value indicating whether the operation succeeded. | |
* | |
* Emits a {Transfer} event. | |
*/ | |
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); | |
/** | |
* @dev Emitted when `value` tokens are moved from one account (`from`) to | |
* another (`to`). | |
* | |
* Note that `value` may be zero. | |
*/ | |
event Transfer(address indexed from, address indexed to, uint256 value); | |
/** | |
* @dev Emitted when the allowance of a `spender` for an `owner` is set by | |
* a call to {approve}. `value` is the new allowance. | |
*/ | |
event Approval(address indexed owner, address indexed spender, uint256 value); | |
} | |
library SafeMath { | |
/** | |
* @dev Returns the addition of two unsigned integers, reverting on | |
* overflow. | |
* | |
* Counterpart to Solidity's `+` operator. | |
* | |
* Requirements: | |
* | |
* - Addition cannot overflow. | |
*/ | |
function add(uint256 a, uint256 b) internal pure returns (uint256) { | |
uint256 c = a + b; | |
require(c >= a, "SafeMath: addition overflow"); | |
return c; | |
} | |
/** | |
* @dev Returns the subtraction of two unsigned integers, reverting on | |
* overflow (when the result is negative). | |
* | |
* Counterpart to Solidity's `-` operator. | |
* | |
* Requirements: | |
* | |
* - Subtraction cannot overflow. | |
*/ | |
function sub(uint256 a, uint256 b) internal pure returns (uint256) { | |
return sub(a, b, "SafeMath: subtraction overflow"); | |
} | |
/** | |
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on | |
* overflow (when the result is negative). | |
* | |
* Counterpart to Solidity's `-` operator. | |
* | |
* Requirements: | |
* | |
* - Subtraction cannot overflow. | |
*/ | |
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { | |
require(b <= a, errorMessage); | |
uint256 c = a - b; | |
return c; | |
} | |
/** | |
* @dev Returns the multiplication of two unsigned integers, reverting on | |
* overflow. | |
* | |
* Counterpart to Solidity's `*` operator. | |
* | |
* Requirements: | |
* | |
* - Multiplication cannot overflow. | |
*/ | |
function mul(uint256 a, uint256 b) internal pure returns (uint256) { | |
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the | |
// benefit is lost if 'b' is also tested. | |
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 | |
if (a == 0) { | |
return 0; | |
} | |
uint256 c = a * b; | |
require(c / a == b, "SafeMath: multiplication overflow"); | |
return c; | |
} | |
/** | |
* @dev Returns the integer division of two unsigned integers. Reverts on | |
* division by zero. The result is rounded towards zero. | |
* | |
* Counterpart to Solidity's `/` operator. Note: this function uses a | |
* `revert` opcode (which leaves remaining gas untouched) while Solidity | |
* uses an invalid opcode to revert (consuming all remaining gas). | |
* | |
* Requirements: | |
* | |
* - The divisor cannot be zero. | |
*/ | |
function div(uint256 a, uint256 b) internal pure returns (uint256) { | |
return div(a, b, "SafeMath: division by zero"); | |
} | |
/** | |
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on | |
* division by zero. The result is rounded towards zero. | |
* | |
* Counterpart to Solidity's `/` operator. Note: this function uses a | |
* `revert` opcode (which leaves remaining gas untouched) while Solidity | |
* uses an invalid opcode to revert (consuming all remaining gas). | |
* | |
* Requirements: | |
* | |
* - The divisor cannot be zero. | |
*/ | |
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { | |
require(b > 0, errorMessage); | |
uint256 c = a / b; | |
// assert(a == b * c + a % b); // There is no case in which this doesn't hold | |
return c; | |
} | |
/** | |
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), | |
* Reverts when dividing by zero. | |
* | |
* Counterpart to Solidity's `%` operator. This function uses a `revert` | |
* opcode (which leaves remaining gas untouched) while Solidity uses an | |
* invalid opcode to revert (consuming all remaining gas). | |
* | |
* Requirements: | |
* | |
* - The divisor cannot be zero. | |
*/ | |
function mod(uint256 a, uint256 b) internal pure returns (uint256) { | |
return mod(a, b, "SafeMath: modulo by zero"); | |
} | |
/** | |
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), | |
* Reverts with custom message when dividing by zero. | |
* | |
* Counterpart to Solidity's `%` operator. This function uses a `revert` | |
* opcode (which leaves remaining gas untouched) while Solidity uses an | |
* invalid opcode to revert (consuming all remaining gas). | |
* | |
* Requirements: | |
* | |
* - The divisor cannot be zero. | |
*/ | |
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { | |
require(b != 0, errorMessage); | |
return a % b; | |
} | |
} | |
contract Administration { | |
// --- Data --- | |
uint256 public contractEnabled = 0; | |
// --- Auth --- | |
mapping(address => uint256) public authorizedAccounts; | |
/** | |
* @notice Add auth to an account | |
* @param account Account to add auth to | |
*/ | |
function addAuthorization(address account) | |
external | |
isAuthorized | |
contractIsEnabled | |
{ | |
authorizedAccounts[account] = 1; | |
emit AddAuthorization(account); | |
} | |
/** | |
* @notice Remove auth from an account | |
* @param account Account to add auth to | |
*/ | |
function removeAuthorization(address account) | |
external | |
isAuthorized | |
contractIsEnabled | |
{ | |
authorizedAccounts[account] = 0; | |
emit RemoveAuthorization(account); | |
} | |
/** | |
* @notice Checks whether msg.sender can call an authed function | |
*/ | |
modifier isAuthorized { | |
require( | |
authorizedAccounts[msg.sender] == 1, | |
"CrosschainLoans/account-not-authorized" | |
); | |
_; | |
} | |
/** | |
* @notice Checks whether the contract is enabled | |
*/ | |
modifier contractIsEnabled { | |
require(contractEnabled == 1, "CrosschainLoans/contract-not-enabled"); | |
_; | |
} | |
// --- Administration --- | |
function enableContract() external isAuthorized { | |
contractEnabled = 1; | |
emit EnableContract(); | |
} | |
/** | |
* @notice Disable this contract | |
*/ | |
function disableContract() external isAuthorized { | |
contractEnabled = 0; | |
emit DisableContract(); | |
} | |
// --- Events --- | |
event AddAuthorization(address account); | |
event RemoveAuthorization(address account); | |
event EnableContract(); | |
event DisableContract(); | |
} | |
contract CrosschainLoans is Administration { | |
using SafeMath for uint256; | |
// --- Data --- | |
uint256 public secondsPerYear = 31556952; | |
uint256 public loanExpirationPeriod = 2592000; // 30 days | |
uint256 public acceptExpirationPeriod = 259200; // 3 days | |
// --- Loans Data --- | |
mapping(uint256 => Loan) loans; | |
uint256 public loanIdCounter; | |
mapping(address => uint256[]) public userLoans; | |
mapping(address => uint256) public userLoansCount; | |
enum State { | |
Open, | |
Funded, | |
Approved, | |
Withdrawn, | |
Repaid, | |
PaybackRefunded, | |
Closed, | |
Canceled | |
} | |
struct Loan { | |
// Actors | |
address payable borrower; | |
address payable lender; | |
address lenderAuto; | |
// Lender's aCoin address | |
address aCoinLenderAddress; | |
// Hashes | |
bytes32 secretHashA1; | |
bytes32 secretHashB1; | |
bytes32 secretHashAutoB1; | |
// Secrets | |
bytes32 secretA1; | |
bytes32 secretB1; | |
bytes32 secretAutoB1; | |
// Expiration Dates | |
uint256 loanExpiration; | |
uint256 acceptExpiration; | |
uint256 createdAt; | |
// Loan Details | |
uint256 principal; | |
uint256 interest; | |
// Loan State | |
State state; | |
// token | |
address contractAddress; | |
ERC20 token; | |
} | |
struct AssetType { | |
uint256 maxLoanAmount; | |
uint256 minLoanAmount; | |
uint256 supply; | |
uint256 demand; | |
uint256 baseRatePerPeriod; | |
uint256 multiplierPerPeriod; | |
uint256 enabled; | |
address contractAddress; | |
ERC20 token; | |
} | |
// Data about each asset type | |
mapping(address => AssetType) public assetTypes; | |
// -- Init --- | |
constructor() public { | |
contractEnabled = 1; | |
authorizedAccounts[msg.sender] = 1; | |
emit AddAuthorization(msg.sender); | |
} | |
/** | |
* @notice Calculates the utilization rate for the given asset | |
* @param _supply The total supply for the given asset | |
* @param _demand The total demand for the given asset | |
*/ | |
function utilizationRate(uint256 _supply, uint256 _demand) | |
public | |
pure | |
returns (uint256) | |
{ | |
if (_demand == 0) { | |
return 0; | |
} | |
return _demand.mul(1e18).div(_supply.add(_demand)); | |
} | |
/** | |
* @notice Calculates the loan period interest rate | |
* @param _contractAddress The contract address of the given asset | |
*/ | |
function getAssetInterestRate(address _contractAddress) | |
public | |
view | |
returns (uint256) | |
{ | |
uint256 ur = | |
utilizationRate( | |
assetTypes[_contractAddress].supply, | |
assetTypes[_contractAddress].demand | |
); | |
return | |
ur | |
.mul(assetTypes[_contractAddress].multiplierPerPeriod) | |
.div(1e18) | |
.add(assetTypes[_contractAddress].baseRatePerPeriod); | |
} | |
/** | |
* @notice Create a loan offer | |
* @param _lenderAuto Address of auto lender | |
* @param _secretHashB1 Hash of the secret B1 | |
* @param _secretHashAutoB1 Hash fo the secret B1 of auto lender | |
* @param _principal Principal of the loan | |
* @param _contractAddress The contract address of the ERC20 token | |
*/ | |
function createLoan( | |
// actors | |
address _lenderAuto, | |
// secret hashes | |
bytes32 _secretHashB1, | |
bytes32 _secretHashAutoB1, | |
// loan details | |
uint256 _principal, | |
address _contractAddress, | |
address _aCoinLenderAddress | |
) public contractIsEnabled returns (uint256 loanId) { | |
require(_principal > 0, "CrosschainLoans/invalid-principal-amount"); | |
require(_contractAddress != address(0), "CrosschainLoans/invalid-token-address"); | |
require(_aCoinLenderAddress != address(0), "CrosschainLoans/invalid-acoin-address"); | |
require( | |
assetTypes[_contractAddress].enabled == 1, | |
"CrosschainLoans/asset-type-disabled" | |
); | |
require( | |
_principal <= assetTypes[_contractAddress].maxLoanAmount && | |
_principal >= assetTypes[_contractAddress].minLoanAmount, | |
"CrosschainLoans/invalid-principal-range" | |
); | |
// Check allowance | |
ERC20 token = ERC20(_contractAddress); | |
uint256 allowance = token.allowance(msg.sender, address(this)); | |
require( | |
allowance >= _principal, | |
"CrosschainLoans/insufficient-token-allowance" | |
); | |
// Transfer Token | |
require( | |
token.transferFrom(msg.sender, address(this), _principal), | |
"CrosschainLoans/token-transfer-failed" | |
); | |
// Increment loanIdCounter | |
loanIdCounter = loanIdCounter + 1; | |
// Add Loan to mapping | |
loans[loanIdCounter] = Loan({ // Actors | |
borrower: address(0), | |
lender: msg.sender, | |
lenderAuto: _lenderAuto, | |
aCoinLenderAddress: _aCoinLenderAddress, // Secret Hashes | |
secretHashA1: "", | |
secretHashB1: _secretHashB1, | |
secretHashAutoB1: _secretHashAutoB1, | |
secretA1: "", | |
secretB1: "", | |
secretAutoB1: "", // Expiration dates | |
loanExpiration: 0, | |
acceptExpiration: 0, | |
createdAt: now, | |
principal: _principal, | |
interest: _principal | |
.mul(getAssetInterestRate(_contractAddress)) | |
.div(1e18), | |
contractAddress: _contractAddress, | |
token: token, | |
state: State.Funded | |
}); | |
// Add LoanId to user | |
userLoans[msg.sender].push(loanIdCounter); | |
// Increase userLoansCount | |
userLoansCount[msg.sender] = userLoansCount[msg.sender] + 1; | |
// Increase asset type supply | |
assetTypes[_contractAddress].supply = assetTypes[_contractAddress] | |
.supply | |
.add(_principal); | |
emit LoanCreated(loanIdCounter); | |
return loanIdCounter; | |
} | |
/** | |
* @notice Set borrower and approve loan | |
* @param _loanId The ID of the loan | |
* @param _borrower Borrower's address | |
* @param _secretHashA1 The hash of borrower's secret A1 | |
*/ | |
function setBorrowerAndApprove( | |
uint256 _loanId, | |
address payable _borrower, | |
bytes32 _secretHashA1 | |
) public contractIsEnabled { | |
require( | |
loans[_loanId].state == State.Funded, | |
"CrosschainLoans/loan-not-funded" | |
); | |
require( | |
msg.sender == loans[_loanId].lender || | |
msg.sender == loans[_loanId].lenderAuto, | |
"CrosschainLoans/account-not-authorized" | |
); | |
require(_borrower != address(0), "CrosschainLoans/invalid-borrower"); | |
// Add LoanId to user | |
userLoans[_borrower].push(loanIdCounter); | |
// Increase userLoanCount | |
userLoansCount[_borrower] = userLoansCount[_borrower] + 1; | |
loans[_loanId].state = State.Approved; | |
loans[_loanId].borrower = _borrower; | |
loans[_loanId].secretHashA1 = _secretHashA1; | |
loans[_loanId].loanExpiration = now.add(loanExpirationPeriod); | |
loans[_loanId].acceptExpiration = now.add(loanExpirationPeriod).add( | |
acceptExpirationPeriod | |
); | |
emit LoanAssignedAndApproved( | |
_loanId, | |
_borrower, | |
_secretHashA1, | |
loans[_loanId].state | |
); | |
} | |
/** | |
* @notice Withdraw the loan's principal | |
* @param _loanId The ID of the loan | |
* @param _secretA1 Borrower's secret A1 | |
*/ | |
function withdraw(uint256 _loanId, bytes32 _secretA1) | |
public | |
contractIsEnabled | |
{ | |
require( | |
loans[_loanId].state == State.Approved, | |
"CrosschainLoans/loan-not-approved" | |
); | |
require( | |
sha256(abi.encodePacked(_secretA1)) == loans[_loanId].secretHashA1, | |
"CrosschainLoans/invalid-secret-A1" | |
); | |
require( | |
now <= loans[_loanId].loanExpiration, | |
"CrosschainLoans/loan-expired" | |
); | |
loans[_loanId].state = State.Withdrawn; | |
loans[_loanId].secretA1 = _secretA1; | |
loans[_loanId].token.transfer( | |
loans[_loanId].borrower, | |
loans[_loanId].principal | |
); | |
// Increase asset type demand | |
address contractAddress = loans[_loanId].contractAddress; | |
assetTypes[contractAddress].demand = assetTypes[contractAddress] | |
.demand | |
.add(loans[_loanId].principal); | |
emit LoanPrincipalWithdrawn( | |
_loanId, | |
loans[_loanId].borrower, | |
loans[_loanId].principal, | |
_secretA1, | |
loans[_loanId].state | |
); | |
} | |
/** | |
* @notice Accept borrower's repayment of principal | |
* @param _loanId The ID of the loan | |
* @param _secretB1 Lender's secret B1 | |
*/ | |
function acceptRepayment(uint256 _loanId, bytes32 _secretB1) | |
public | |
contractIsEnabled | |
{ | |
require( | |
sha256(abi.encodePacked(_secretB1)) == | |
loans[_loanId].secretHashB1 || | |
sha256(abi.encodePacked(_secretB1)) == | |
loans[_loanId].secretHashAutoB1, | |
"CrosschainLoans/invalid-secret-B1" | |
); | |
require( | |
now <= loans[_loanId].acceptExpiration, | |
"CrosschainLoans/accept-period-expired" | |
); | |
require( | |
loans[_loanId].state == State.Repaid, | |
"CrosschainLoans/loan-not-repaid" | |
); | |
loans[_loanId].state = State.Closed; | |
loans[_loanId].secretB1 = _secretB1; | |
uint256 repayment = | |
loans[_loanId].principal.add(loans[_loanId].interest); | |
require( | |
loans[_loanId].token.transfer(loans[_loanId].lender, repayment), | |
"CrosschainLoans/token-transfer-failed" | |
); | |
emit LoanRepaymentAccepted(_loanId, repayment, loans[_loanId].state); | |
} | |
/** | |
* @notice Cancel loan before the borrower withdraws the loan's principal | |
* @param _loanId The ID of the loan | |
* @param _secretB1 Lender's secret B1 | |
*/ | |
function cancelLoanBeforePrincipalWithdraw( | |
uint256 _loanId, | |
bytes32 _secretB1 | |
) public contractIsEnabled { | |
require( | |
sha256(abi.encodePacked(_secretB1)) == | |
loans[_loanId].secretHashB1 || | |
sha256(abi.encodePacked(_secretB1)) == | |
loans[_loanId].secretHashAutoB1, | |
"CrosschainLoans/invalid-secret-B1" | |
); | |
// require(now <= loans[_loanId].acceptExpiration,"CrosschainLoans/accept-period-expired"); | |
require( | |
loans[_loanId].state == State.Funded || | |
loans[_loanId].state == State.Approved, | |
"CrosschainLoans/principal-withdrawn" | |
); | |
loans[_loanId].state = State.Canceled; | |
uint256 principal = loans[_loanId].principal; | |
loans[_loanId].principal = 0; | |
loans[_loanId].secretB1 = _secretB1; | |
// Decrease supply | |
address contractAddress = loans[_loanId].contractAddress; | |
assetTypes[contractAddress].supply = assetTypes[contractAddress] | |
.supply | |
.sub(loans[_loanId].principal); | |
require( | |
loans[_loanId].token.transfer(loans[_loanId].lender, principal), | |
"CrosschainLoans/token-refund-failed" | |
); | |
emit CancelLoan(_loanId, _secretB1, loans[_loanId].state); | |
} | |
/** | |
* @notice Payback loan's principal and interest | |
* @param _loanId The ID of the loan | |
*/ | |
function payback(uint256 _loanId) public contractIsEnabled { | |
require( | |
loans[_loanId].state == State.Withdrawn, | |
"CrosschainLoans/invalid-loan-state" | |
); | |
require( | |
now <= loans[_loanId].loanExpiration, | |
"CrosschainLoans/loan-expired" | |
); | |
uint256 repayment = | |
loans[_loanId].principal.add(loans[_loanId].interest); | |
// Check allowance | |
uint256 allowance = | |
loans[_loanId].token.allowance(msg.sender, address(this)); | |
require( | |
allowance >= repayment, | |
"CrosschainLoans/insufficient-token-allowance" | |
); | |
loans[_loanId].state = State.Repaid; | |
require( | |
loans[_loanId].token.transferFrom( | |
msg.sender, | |
address(this), | |
repayment | |
), | |
"CrosschainLoans/token-transfer-failed" | |
); | |
emit Payback( | |
_loanId, | |
loans[_loanId].borrower, | |
repayment, | |
loans[_loanId].state | |
); | |
} | |
/** | |
* @notice Refund the payback amount | |
* @param _loanId The ID of the loan | |
*/ | |
function refundPayback(uint256 _loanId) public contractIsEnabled { | |
require( | |
now > loans[_loanId].acceptExpiration, | |
"CrosschainLoans/accept-period-not-expired" | |
); | |
require( | |
loans[_loanId].state == State.Repaid, | |
"CrosschainLoans/loan-not-repaid" | |
); | |
loans[_loanId].state = State.PaybackRefunded; | |
uint256 refund = loans[_loanId].principal.add(loans[_loanId].interest); | |
loans[_loanId].principal = 0; | |
loans[_loanId].interest = 0; | |
require( | |
loans[_loanId].token.transfer(loans[_loanId].borrower, refund), | |
"CrosschainLoans/token-transfer-failed" | |
); | |
emit RefundPayback( | |
_loanId, | |
loans[_loanId].borrower, | |
refund, | |
loans[_loanId].state | |
); | |
} | |
/** | |
* @notice Get information about an Asset Type | |
* @param contractAddress The contract address of the given asset | |
*/ | |
function getAssetType(address _contractAddress) | |
public | |
view | |
returns ( | |
uint256 maxLoanAmount, | |
uint256 minLoanAmount, | |
uint256 supply, | |
uint256 demand, | |
uint256 baseRatePerPeriod, | |
uint256 multiplierPerPeriod, | |
uint256 interestRate, | |
uint256 enabled, | |
address contractAddress | |
) | |
{ | |
maxLoanAmount = assetTypes[_contractAddress].maxLoanAmount; | |
minLoanAmount = assetTypes[_contractAddress].minLoanAmount; | |
supply = assetTypes[_contractAddress].supply; | |
demand = assetTypes[_contractAddress].demand; | |
baseRatePerPeriod = assetTypes[_contractAddress].baseRatePerPeriod; | |
multiplierPerPeriod = assetTypes[_contractAddress].multiplierPerPeriod; | |
interestRate = getAssetInterestRate(_contractAddress); | |
enabled = assetTypes[_contractAddress].enabled; | |
contractAddress = assetTypes[_contractAddress].contractAddress; | |
} | |
/** | |
* @notice Get information about a loan | |
* @param _loanId The ID of the loan | |
*/ | |
function fetchLoan(uint256 _loanId) | |
public | |
view | |
returns ( | |
address[3] memory actors, | |
bytes32[3] memory secretHashes, | |
bytes32[3] memory secrets, | |
uint256[2] memory expirations, | |
uint256[2] memory details, | |
address aCoinLenderAddress, | |
State state, | |
address contractAddress | |
) | |
{ | |
actors = [ | |
loans[_loanId].borrower, | |
loans[_loanId].lender, | |
loans[_loanId].lenderAuto | |
]; | |
secretHashes = [ | |
loans[_loanId].secretHashA1, | |
loans[_loanId].secretHashB1, | |
loans[_loanId].secretHashAutoB1 | |
]; | |
secrets = [ | |
loans[_loanId].secretA1, | |
loans[_loanId].secretB1, | |
loans[_loanId].secretAutoB1 | |
]; | |
expirations = [ | |
loans[_loanId].loanExpiration, | |
loans[_loanId].acceptExpiration | |
]; | |
aCoinLenderAddress = loans[_loanId].aCoinLenderAddress; | |
state = loans[_loanId].state; | |
details = [loans[_loanId].principal, loans[_loanId].interest]; | |
contractAddress = loans[_loanId].contractAddress; | |
} | |
/** | |
* @notice Get Account loans | |
* @param _account The user account | |
*/ | |
function getAccountLoans(address _account) | |
public | |
view | |
returns (uint256[] memory) | |
{ | |
return userLoans[_account]; | |
} | |
/** | |
* @notice Modify Loan expiration periods | |
* @param _parameter The name of the parameter modified | |
* @param _data The new value for the parameter | |
*/ | |
function modifyLoanParameters(bytes32 _parameter, uint256 _data) | |
external | |
isAuthorized | |
contractIsEnabled | |
{ | |
require(_data > 0, "CrosschainLoans/null-data"); | |
if (_parameter == "loanExpirationPeriod") loanExpirationPeriod = _data; | |
else if (_parameter == "acceptExpirationPeriod") | |
acceptExpirationPeriod = _data; | |
else revert("CrosschainLoans/modify-unrecognized-param"); | |
emit ModifyLoanParameters(_parameter, _data); | |
} | |
/** | |
* @notice Modify AssetType related parameters | |
* @param _contractAddress The contract address of the ERC20 token | |
* @param _parameter The name of the parameter modified | |
* @param _data The new value for the parameter | |
*/ | |
function modifyAssetTypeLoanParameters( | |
address _contractAddress, | |
bytes32 _parameter, | |
uint256 _data | |
) external isAuthorized contractIsEnabled { | |
require(_data > 0, "CrosschainLoans/null-data"); | |
require( | |
_contractAddress != address(0) && | |
assetTypes[_contractAddress].contractAddress != address(0), | |
"CrosschainLoans/invalid-assetType" | |
); | |
if (_parameter == "maxLoanAmount") | |
assetTypes[_contractAddress].maxLoanAmount = _data; | |
else if (_parameter == "minLoanAmount") | |
assetTypes[_contractAddress].minLoanAmount = _data; | |
else if (_parameter == "baseRatePerYear") { | |
assetTypes[_contractAddress].baseRatePerPeriod = _data | |
.mul(loanExpirationPeriod) | |
.div(secondsPerYear); | |
} else if (_parameter == "multiplierPerYear") { | |
assetTypes[_contractAddress].multiplierPerPeriod = _data | |
.mul(loanExpirationPeriod) | |
.div(secondsPerYear); | |
} else revert("CrosschainLoans/modify-unrecognized-param"); | |
emit ModifyAssetTypeLoanParameters(_parameter, _data); | |
} | |
/** | |
* @notice Disable AssetType | |
* @param _contractAddress The contract address of the ERC20 token | |
*/ | |
function disableAssetType(address _contractAddress) | |
external | |
isAuthorized | |
contractIsEnabled | |
{ | |
require( | |
_contractAddress != address(0) && | |
assetTypes[_contractAddress].contractAddress != address(0), | |
"CrosschainLoans/invalid-assetType" | |
); | |
assetTypes[_contractAddress].enabled = 0; | |
emit DisableAssetType(_contractAddress); | |
} | |
/** | |
* @notice Enable AssetType | |
*/ | |
function enableAssetType(address _contractAddress) | |
external | |
isAuthorized | |
contractIsEnabled | |
{ | |
require( | |
_contractAddress != address(0) && | |
assetTypes[_contractAddress].contractAddress != address(0), | |
"CrosschainLoans/invalid-assetType" | |
); | |
assetTypes[_contractAddress].enabled = 1; | |
emit EnableAssetType(_contractAddress); | |
} | |
/** | |
* @notice Add AssetType | |
* @param _contractAddress The contract address of the ERC20 token | |
* @param _maxLoanAmount The maximum principal allowed for the token | |
* @param _minLoanAmount The minimum principal allowerd for the token | |
* @param _baseRatePerYear The approximate target base APR | |
* @param _multiplierPerYear The rate of increase in interest rate | |
*/ | |
function addAssetType( | |
address _contractAddress, | |
uint256 _maxLoanAmount, | |
uint256 _minLoanAmount, | |
uint256 _baseRatePerYear, | |
uint256 _multiplierPerYear | |
) external isAuthorized contractIsEnabled { | |
require(_contractAddress != address(0)); | |
require(_maxLoanAmount > 0, "CrosschainLoans/invalid-maxLoanAmount"); | |
require(_minLoanAmount > 0, "CrosschainLoans/invalid-minLoanAmount"); | |
require( | |
assetTypes[_contractAddress].minLoanAmount == 0, | |
"CrosschainLoans/assetType-already-exists" | |
); | |
assetTypes[_contractAddress] = AssetType({ | |
contractAddress: _contractAddress, | |
token: ERC20(_contractAddress), | |
maxLoanAmount: _maxLoanAmount, | |
minLoanAmount: _minLoanAmount, | |
baseRatePerPeriod: _baseRatePerYear.mul(loanExpirationPeriod).div( | |
secondsPerYear | |
), | |
multiplierPerPeriod: _multiplierPerYear | |
.mul(loanExpirationPeriod) | |
.div(secondsPerYear), | |
enabled: 1, | |
supply: 0, | |
demand: 0 | |
}); | |
emit AddAssetType(_contractAddress, _maxLoanAmount, _minLoanAmount); | |
} | |
// --- Events --- | |
event LoanCreated(uint256 loanId); | |
event LoanFunded(uint256 loanId, uint256 amount, State state); | |
event LoanAssignedAndApproved( | |
uint256 loanId, | |
address borrower, | |
bytes32 secretHashA1, | |
State state | |
); | |
event LoanPrincipalWithdrawn( | |
uint256 loanId, | |
address borrower, | |
uint256 amount, | |
bytes32 secretA1, | |
State state | |
); | |
event LoanRepaymentAccepted(uint256 loanId, uint256 amount, State state); | |
event CancelLoan(uint256 loanId, bytes32 secretB1, State state); | |
event Payback( | |
uint256 loanId, | |
address borrower, | |
uint256 amount, | |
State state | |
); | |
event RefundPayback( | |
uint256 loanId, | |
address borrower, | |
uint256 amount, | |
State state | |
); | |
event ModifyLoanParameters(bytes32 parameter, uint256 data); | |
event ModifyAssetTypeLoanParameters(bytes32 parameter, uint256 data); | |
event DisableAssetType(address contractAddress); | |
event EnableAssetType(address contractAddress); | |
event AddAssetType( | |
address contractAddress, | |
uint256 maxLoanAmount, | |
uint256 minLoanAmount | |
); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity ^0.5.12; | |
contract MatchingEngine { | |
struct Order { | |
uint256 id; | |
uint256 amount; | |
uint256 price; | |
Operation operation; | |
Status status; | |
} | |
enum Operation { | |
Ask, | |
Bid | |
} | |
enum Status { | |
Open, | |
Completed, | |
Canceled | |
} | |
uint256 orderId = 1; | |
mapping(uint256 => Order) asks; | |
mapping(uint256 => Order) bids; | |
uint256[] sortedAsks; | |
uint256[] sorderBids; | |
function bid(uint256 amount, uint256 price) public { | |
orderId = orderId + 1; | |
Order memory order = Order({ | |
id: orderId, | |
amount: amount, | |
price: price, | |
operation: Operation.Bid, | |
status: Status.Open | |
}); | |
bids[orderId] = order; | |
if(sortedAsks.length == 0 || asks[sortedAsks[0]].price > price) { | |
sorderBids.push(orderId); | |
} | |
uint256 i = 0; | |
while(price >= sortedAsks[0].price && amount >= sortedAsks[0].amount) { | |
} | |
} | |
function ask(uint256 amount, uint256 price) public { | |
orderId = orderId + 1; | |
Order memory order = Order({ | |
id: orderId, | |
amount: amount, | |
price: price, | |
operation: Operation.Ask, | |
status: Status.Open | |
}); | |
asks[orderId] = order; | |
if(sortedBids.length == 0 || bids[sortedBids[sortedBids.length - 1]].price < price) { | |
sortedBids.push(orderId); | |
} | |
} | |
function sortOrderbook(uint256 orderId) { | |
uint256 i = 1; | |
uint256 position = 0; | |
while(orderbook[i].price > orderbook[orderId]) { | |
sortedOrderbook[i] = | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity 0.6.0; | |
interface CrosschainLoans { | |
function createLoan( | |
address _lenderAuto, | |
bytes32 _secretHashB1, | |
bytes32 _secretHashAutoB1, | |
uint256 _principal, | |
address _contractAddress, | |
address _aCoinLenderAddress | |
) external returns (uint256 loanId); | |
} | |
interface MasterChef { | |
function deposit(uint256 _pid, uint256 _amount) external; | |
function withdraw(uint256 _pid, uint256 _amount) external; | |
function enterStaking(uint256 _amount) external; | |
function leaveStaking(uint256 _amount) external; | |
} | |
contract PancakeLoans { | |
CrosschainLoans crosschainLoans; | |
MasterChef masterChef; | |
constructor(address _crosschainLoans, address _masterChef) public { | |
crosschainLoans = CrosschainLoans(_crosschainLoans); | |
masterChef = MasterChef(_masterChef); | |
} | |
function createLoan( | |
address _lenderAuto, | |
bytes32 _secretHashB1, | |
bytes32 _secretHashAutoB1, | |
uint256 _principal, | |
address _contractAddress, | |
address _aCoinLenderAddress, | |
uint256 _pid | |
) public { | |
crosschainLoans.createLoan( | |
_lenderAuto, | |
_secretHashB1, | |
_secretHashAutoB1, | |
_principal, | |
_contractAddress, | |
_aCoinLenderAddress | |
); | |
masterChef.deposit(_pid, _principal); | |
// or use staking instead? | |
masterChef.enterStaking(_principal); | |
} | |
function withdraw() public { | |
} | |
function cancelLoanBeforePrincipalWithdraw() public { | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity >=0.6.0 <0.8.0; | |
contract Stack { | |
uint256[] array; | |
uint256 nextIndex = 0; | |
uint256 numElements = 0; | |
constructor() public { | |
} | |
function push(uint256 value) public { | |
array.push(value); | |
nextIndex += 1; | |
numElements += 1; | |
} | |
function size() public view returns(uint256) { | |
return numElements; | |
} | |
function isEmpty() public view returns(bool) { | |
return numElements == 0; | |
} | |
function pop() public returns(uint256) { | |
if(isEmpty()) return nextIndex; | |
delete array[numElements - 1]; | |
nextIndex -= 1; | |
numElements -= 1; | |
return array[nextIndex]; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity >=0.6.0 <0.8.0; | |
contract SyncToken { | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity >=0.6.0 <0.8.0; | |
// SPDX-License-Identifier: MIT" | |
contract Token { | |
mapping (address => uint256) private _balances; | |
uint256 private _totalSupply; | |
string private _name; | |
string private _symbol; | |
uint256 private _decimals; | |
uint256 private theTotalSupply; | |
constructor(string memory name, string memory symbol) public { | |
// _name = "Test"; | |
// _symbol = "TEST"; | |
// _decimals = 18; | |
// _totalSupply = 0; | |
// Assign temporal variables to contract variables | |
// https://docs.soliditylang.org/en/v0.5.3/contracts.html | |
_name = name; | |
_symbol = symbol; | |
// You can assign the total number of token in circulation here | |
// Solidity does not have decimal values so they are represented in integers | |
// The standard is to repredent balances in wei units | |
// https://eth-converter.com/ | |
_totalSupply = 1000e18; // = 1000 tokens in wei units (18 decimals) | |
// Add balance to msg.sender, the Ethereum account that deployed the contract | |
_balances[msg.sender] = 1000e18; | |
// The ERC20 tokens have 18 decimals | |
_decimals = 18; | |
} | |
/** | |
* @dev Returns the name of the token | |
*/ | |
function name() public view returns (string memory) { | |
return _name; | |
} | |
/** | |
* @dev Set the name of the token | |
*/ | |
function setName(string memory name) public { | |
_name = name; | |
} | |
/** | |
* | |
* @dev Returns the symbol of the token | |
*/ | |
// function symbol() public view returns (string) { | |
// You need to add the keyword `memory` to temporal strings | |
function symbol() public view returns (string memory) { | |
return _symbol; | |
} | |
/** | |
* @dev Returns the decimals of the token | |
*/ | |
// function decimals() public view returns (uint8) { | |
// The returned value does not match `_decimals` data type since it's of type uint256 | |
// and you are trying to return a uint8 | |
function decimals() public view returns (uint256) { | |
return _decimals; | |
} | |
/** | |
* @dev Returns the total supply of the token | |
*/ | |
function totalSupply() public view returns (uint256) { | |
// This function should return the totalSupply or amount of tokens in circulation | |
// so you don't need to assign anything | |
// theTotalSupply = _totalSupply; | |
return _totalSupply; | |
} | |
/** | |
* @dev Returns the balance of the token | |
*/ | |
function balanceOf(address _owner) public view returns (uint256) { | |
// You are missing an `s` | |
// return balance[_owner]; | |
return _balances[_owner]; | |
} | |
/** | |
* @dev Returns the transfer of the token | |
*/ | |
function transfer(address _to, uint256 _value) public returns (bool success) { | |
_balances[msg.sender] -= _value; | |
_balances[_to] += _value; | |
// This is not necessary or maybe you are trying to emit an Event | |
// https://docs.soliditylang.org/en/v0.6.7/contracts.html#events | |
// transfer(msg.sender, _to, _value); // | |
emit Transfer(msg.sender, _to, _value); | |
return true; | |
} | |
// Declare event | |
event Transfer(address sender, address recipient, uint256 amount); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment