Skip to content

Commit

Permalink
Merge pull request #249 from solace-fi/feat/soteriatokenuri
Browse files Browse the repository at this point in the history
Added tokenURI and setBaseURI functions, exposed minRequiredAccountBalance
  • Loading branch information
ssozuer authored Feb 12, 2022
2 parents a137589 + dd47586 commit 514f11f
Show file tree
Hide file tree
Showing 5 changed files with 202 additions and 28 deletions.
33 changes: 27 additions & 6 deletions contracts/interfaces/products/ISolaceCoverProduct.sol
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,8 @@ interface ISolaceCoverProduct {
uint256 rewardPointsEarned
);

/// @notice Emitted when stablecoin is added to accepted stablecoin list
event StablecoinAdded(address stablecoin);

/// @notice Emitted when stablecoin is removed from accepted stablecoin list
event StablecoinRemoved(address stablecoin);
/// @notice Emitted when baseURI is set
event BaseURISet(string baseURI);

/***************************************
POLICY FUNCTIONS
Expand Down Expand Up @@ -273,10 +270,28 @@ interface ISolaceCoverProduct {
* @notice True if a policyholder has previously used a valid referral code, false if not
*
* A policyholder can only use a referral code once. Afterwards a policyholder is ineligible to receive further rewards from additional referral codes.
* @return isReferralCodeUsed_ True if the policyholder has previoulsy used a valid referral code, false if not
* @return isReferralCodeUsed_ True if the policyholder has previously used a valid referral code, false if not
*/
function isReferralCodeUsed(address policyholder) external view returns (bool isReferralCodeUsed_);

/**
* @notice Returns true if valid referral code, false otherwise.
* @param referralCode The referral code.
*/
function isReferralCodeValid(bytes calldata referralCode) external view returns (bool);

/**
* @notice Get referrer from referral code, returns 0 address if invalid referral code.
* @param referralCode The referral code.
* @return referrer The referrer address, returns 0 address if invalid referral code.
*/
function getReferrerFromReferralCode(bytes calldata referralCode) external view returns (address referrer);
/**
* @notice Calculate minimum required account balance for a given cover limit. Equals the maximum chargeable fee for one epoch.
* @param coverLimit Cover limit.
*/
function minRequiredAccountBalance(uint256 coverLimit) external view returns (uint256 minRequiredAccountBalance_);

/***************************************
GOVERNANCE FUNCTIONS
***************************************/
Expand Down Expand Up @@ -338,6 +353,12 @@ interface ISolaceCoverProduct {
*/
function setIsReferralOn(bool isReferralOn_) external;

/**
* @notice Sets the base URI for computing `tokenURI`.
* @param baseURI_ The new base URI.
*/
function setBaseURI(string memory baseURI_) external;

/***************************************
COVER PROMOTION ADMIN FUNCTIONS
***************************************/
Expand Down
69 changes: 60 additions & 9 deletions contracts/products/SolaceCoverProduct.sol
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,12 @@ contract SolaceCoverProduct is
/// @notice Cannot buy new policies while paused. (Default is False)
bool internal _paused;

/**
* @notice Referral typehash.
*/
// solhint-disable-next-line var-name-mixedcase
/// @notice Referral typehash.
/// solhint-disable-next-line var-name-mixedcase
bytes32 private constant _REFERRAL_TYPEHASH = keccak256("SolaceReferral(uint256 version)");

string public baseURI;

/***************************************
BOOK-KEEPING VARIABLES
***************************************/
Expand Down Expand Up @@ -165,7 +165,8 @@ contract SolaceCoverProduct is
_chargeCycle = 604800; // One-week charge cycle
_cooldownPeriod = 604800; // One-week cooldown period
_referralReward = 50e18; // 50 DAI
_isReferralOn = true; // Referral rewards activate
_isReferralOn = true; // Referral rewards active
baseURI = string(abi.encodePacked("https://stats.solace.fi/policy/?chainID=", Strings.toString(block.chainid), "&policyID="));
}

/***************************************
Expand All @@ -192,7 +193,8 @@ contract SolaceCoverProduct is
policyID = policyOf(policyholder_);
require(!policyStatus(policyID), "policy already activated");
require(_canPurchaseNewCover(0, coverLimit_), "insufficient capacity for new cover");
require(IERC20(_getAsset()).balanceOf(msg.sender) >= amount_ && amount_ + accountBalanceOf(policyholder_) > _minRequiredAccountBalance(coverLimit_), "insufficient deposit for minimum required account balance");
require(IERC20(_getAsset()).balanceOf(msg.sender) >= amount_, "insufficient caller balance for deposit");
require(amount_ + accountBalanceOf(policyholder_) > _minRequiredAccountBalance(coverLimit_), "insufficient deposit for minimum required account balance");

// Exit cooldown
_exitCooldown(policyholder_);
Expand Down Expand Up @@ -258,6 +260,7 @@ contract SolaceCoverProduct is
address policyholder,
uint256 amount
) external override nonReentrant whileUnpaused {
require(policyholder != address(0x0), "zero address policyholder");
_deposit(msg.sender, policyholder, amount);
}

Expand All @@ -269,6 +272,7 @@ contract SolaceCoverProduct is
* @notice If cooldown has not started, or has not passed, the user will not be able to withdraw their entire account. A minimum required account balance (one epoch's fee) will be left in the user's account.
*/
function withdraw() external override nonReentrant whileUnpaused {
require(_accountBalanceOf[msg.sender] > 0, "no account balance to withdraw");
if ( _hasCooldownPassed(msg.sender) ) {
_withdraw(msg.sender, _accountBalanceOf[msg.sender]);
_preDeactivateCoverLimitOf[_policyOf[msg.sender]] = 0;
Expand Down Expand Up @@ -458,12 +462,49 @@ contract SolaceCoverProduct is
* @notice True if a policyholder has previously used a valid referral code, false if not
*
* A policyholder can only use a referral code once. A policyholder is then ineligible to receive further rewards from additional referral codes.
* @return isReferralCodeUsed_ True if the policyholder has previoulsy used a valid referral code, false if not
* @return isReferralCodeUsed_ True if the policyholder has previously used a valid referral code, false if not
*/
function isReferralCodeUsed(address policyholder) external view override returns (bool isReferralCodeUsed_) {
return _isReferralCodeUsed[_policyOf[policyholder]];
}

/**
* @notice Returns true if valid referral code, false otherwise.
* @param referralCode The referral code.
*/
function isReferralCodeValid(bytes calldata referralCode) external view override returns (bool) {
(address referrer,) = ECDSA.tryRecover(_getEIP712Hash(), referralCode);
if(referrer == address(0)) return false;
return true;
}

/**
* @notice Get referrer from referral code, returns 0 address if invalid referral code.
* @param referralCode The referral code.
* @return referrer The referrer address, returns 0 address if invalid referral code.
*/
function getReferrerFromReferralCode(bytes calldata referralCode) external view override returns (address referrer) {
(referrer,) = ECDSA.tryRecover(_getEIP712Hash(), referralCode);
}

/**
* @notice Calculate minimum required account balance for a given cover limit. Equals the maximum chargeable fee for one epoch.
* @param coverLimit Cover limit.
*/
function minRequiredAccountBalance(uint256 coverLimit) external view override returns (uint256 minRequiredAccountBalance_) {
return _minRequiredAccountBalance(coverLimit);
}

/**
* @notice Returns the Uniform Resource Identifier (URI) for `policyID`.
* @param policyID The policy ID.
*/
function tokenURI(uint256 policyID) public view virtual override returns (string memory tokenURI_) {
require(_exists(policyID), "invalid policy");
string memory baseURI_ = baseURI;
return string(abi.encodePacked( baseURI_, Strings.toString(policyID) ));
}

/***************************************
GOVERNANCE FUNCTIONS
***************************************/
Expand Down Expand Up @@ -553,6 +594,15 @@ contract SolaceCoverProduct is
emit IsReferralOnSet(isReferralOn_);
}

/**
* @notice Sets the base URI for computing `tokenURI`.
* @param baseURI_ The new base URI.
*/
function setBaseURI(string memory baseURI_) external override onlyGovernance {
baseURI = baseURI_;
emit BaseURISet(baseURI_);
}

/***************************************
COVER PROMOTION ADMIN FUNCTIONS
***************************************/
Expand Down Expand Up @@ -630,7 +680,7 @@ contract SolaceCoverProduct is
}

// single DAI transfer to the premium pool
SafeERC20.safeTransferFrom(_getAsset(), address(this), _registry.get("premiumPool"), amountToPayPremiumPool);
SafeERC20.safeTransfer(_getAsset(), _registry.get("premiumPool"), amountToPayPremiumPool);
}

/***************************************
Expand All @@ -647,6 +697,7 @@ contract SolaceCoverProduct is
uint256 existingTotalCover_,
uint256 newTotalCover_
) internal view returns (bool acceptable) {
if (newTotalCover_ <= existingTotalCover_) return true; // Return if user is lowering cover limit
uint256 changeInTotalCover = newTotalCover_ - existingTotalCover_; // This will revert if newTotalCover_ < existingTotalCover_
if (changeInTotalCover < availableCoverCapacity()) return true;
else return false;
Expand Down Expand Up @@ -677,7 +728,7 @@ contract SolaceCoverProduct is
address policyholder,
uint256 amount
) internal whileUnpaused {
SafeERC20.safeTransferFrom(_getAsset(), address(this), policyholder, amount);
SafeERC20.safeTransfer(_getAsset(), policyholder, amount);
_accountBalanceOf[policyholder] -= amount;
emit WithdrawMade(policyholder, amount);
}
Expand Down
23 changes: 12 additions & 11 deletions scripts/deploy-rinkeby-4.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ import { logContractAddress } from "./utils";

import { import_artifacts, ArtifactImports } from "./../test/utilities/artifact_importer";
import { Deployer, CoverageDataProvider, Registry, RiskManager, SolaceCoverProduct } from "../typechain";
import { isDeployed } from "../test/utilities/expectDeployed";

const DEPLOYER_CONTRACT_ADDRESS = "0x501aCe4732E4A80CC1bc5cd081BEe7f88ff694EF";

const DAI_ADDRESS = "0x5592ec0cfb4dbc12d3ab100b257153436a1f0fea"; // testnet
const DAI_ADDRESS = "0x8ad3aA5d5ff084307d28C8f514D7a193B2Bfe725"; // testnet DAI with approve, mint functions exposed on Etherscan

// wallet addresses
let COVERAGE_DATA_PROVIDER_UPDATER_ADDRESS = "0xc5683ea4888DadfdE421a1E593DfbD36290D63AB"; // the bot address to update underwriting pool values
Expand All @@ -24,10 +25,10 @@ let COVER_PROMOTION_ADMIN_ADDRESS = "0x4770becA2628685F7C45102c7a649
const PREMIUM_COLLECTOR_ADDRESS = "0xF321be3577B1AcB436869493862bA18bDde6fc39"; // the bot address that will be set in registry

// contract addresses
const REGISTRY_V2_ADDRESS = "";
const RISK_MANAGER_V2_ADDRESS = "";
const COVERAGE_DATA_PROVIDER_ADDRESS = "";
const SOLACE_COVER_PRODUCT_ADDRESS = "";
const REGISTRY_V2_ADDRESS = "0x501ACe0f576fc4ef9C0380AA46A578eA96b85776";
const RISK_MANAGER_V2_ADDRESS = "0x501AcEf9020632a71CB25CFa9F554252eB51732b";
const COVERAGE_DATA_PROVIDER_ADDRESS = "0x501ACE6C5fFf4d42EaC02357B6DD9b756E337355";
const SOLACE_COVER_PRODUCT_ADDRESS = "0x501aceFe2DfB6496c421512Ed0e5bE42eE0dFA2a";

const DOMAIN_NAME = "Solace.fi-SolaceCoverProduct";
const VERSION = "1";
Expand All @@ -46,12 +47,12 @@ async function main() {
signerAddress = await deployer.getAddress();
console.log(`Using ${signerAddress} as deployer and governor`);

if (!!COVERAGE_DATA_PROVIDER_ADDRESS) {
if (await isDeployed(COVERAGE_DATA_PROVIDER_ADDRESS)) {
console.log(`Using ${signerAddress} as COVERAGE_DATA_PROVIDER_UPDATER_ADDRESS`);
COVERAGE_DATA_PROVIDER_UPDATER_ADDRESS = signerAddress;
}

if (!!COVER_PROMOTION_ADMIN_ADDRESS) {
if (await isDeployed(COVER_PROMOTION_ADMIN_ADDRESS)) {
console.log(`Using ${signerAddress} as COVER_PROMOTION_ADMIN_ADDRESS`);
COVER_PROMOTION_ADMIN_ADDRESS = signerAddress;
}
Expand All @@ -77,7 +78,7 @@ async function main() {
}

async function deployRegistry() {
if(!!REGISTRY_V2_ADDRESS) {
if(await isDeployed(REGISTRY_V2_ADDRESS)) {
registryV2 = (await ethers.getContractAt(artifacts.Registry.abi, REGISTRY_V2_ADDRESS)) as Registry;
} else {
console.log("Deploying Registry(V2)");
Expand All @@ -98,7 +99,7 @@ async function deployRegistry() {
}

async function deployCoverageDataProvider() {
if (!!COVERAGE_DATA_PROVIDER_ADDRESS) {
if (await isDeployed(COVERAGE_DATA_PROVIDER_ADDRESS)) {
coverageDataProvider = (await ethers.getContractAt(artifacts.CoverageDataProvider.abi, COVERAGE_DATA_PROVIDER_ADDRESS)) as CoverageDataProvider;
} else {
console.log("Deploying Coverage Data Provider");
Expand Down Expand Up @@ -127,7 +128,7 @@ async function deployCoverageDataProvider() {
}

async function deployRiskManager() {
if (!!RISK_MANAGER_V2_ADDRESS) {
if (await isDeployed(RISK_MANAGER_V2_ADDRESS)) {
riskManagerV2 = (await ethers.getContractAt(artifacts.RiskManager.abi, RISK_MANAGER_V2_ADDRESS)) as RiskManager;
} else {
console.log("Deploying Risk Manager(V2)");
Expand All @@ -144,7 +145,7 @@ async function deployRiskManager() {
}

async function deploySolaceCoverProduct() {
if (!!SOLACE_COVER_PRODUCT_ADDRESS) {
if (await isDeployed(SOLACE_COVER_PRODUCT_ADDRESS)) {
solaceCoverProduct = (await ethers.getContractAt(artifacts.SolaceCoverProduct.abi, SOLACE_COVER_PRODUCT_ADDRESS)) as SolaceCoverProduct;
} else {
console.log("Deploying Solace Cover Product");
Expand Down
Loading

0 comments on commit 514f11f

Please sign in to comment.