Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion contracts/AggregatorDataProvider.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ contract AggregatorDataProvider is
uint256 public constant GANACHE = 1337;
uint256 public constant GANACHE2 = 1234;
uint256 public constant MUMBAI = 80001;
uint256 public constant AMOY = 80002;

AggregatorV2V3Interface private _aggregator;

Expand Down Expand Up @@ -297,6 +298,7 @@ contract AggregatorDataProvider is
{
return (block.chainid == GANACHE)
|| (block.chainid == GANACHE2)
|| (block.chainid == MUMBAI);
|| (block.chainid == MUMBAI)
|| (block.chainid == AMOY);
}
}
274 changes: 274 additions & 0 deletions contracts/DepegDistribution.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.2;

import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@etherisc/gif-interface/contracts/modules/IRegistry.sol";
import "@etherisc/gif-interface/contracts/services/IInstanceService.sol";

import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {DepegProduct} from "./DepegProduct.sol";
import {DepegRiskpool} from "./DepegRiskpool.sol";

contract DepegDistribution is
Ownable
{
struct DistributorInfo {
uint256 commissionRate;
uint256 commissionBalance;
uint256 policiesSold;
uint256 createdAt;
uint256 updatedAt;
}

event LogDepegPolicySold(address distributor, bytes32 processId, uint256 premiumTotalAmount, address protectedWallet, uint256 protectedBalance);
event LogDistributionInfoUpdated(address distributor, uint256 commissionAmount, uint256 commissionBalance, uint256 totalPoliciesSold);

uint8 public constant DECIMALS = 18;
uint256 public constant COMMISSION_RATE_DEFAULT = 5 * 10 ** (DECIMALS - 2);
uint256 public constant COMMISSION_RATE_MAX = 33 * 10 ** (DECIMALS - 2);

DepegProduct private _depegProduct;
DepegRiskpool private _depegRiskpool;
IERC20Metadata private _token;
address private _treasury;

mapping(address => DistributorInfo) private _distributor;
address [] private _distributors;


modifier onlyDistributor() {
require(
isDistributor(msg.sender),
"ERROR:DST-001:NOT_DISTRIBUTOR"
);
_;
}

constructor(
address depegProduct,
uint256 productId
)
Ownable()
{
_depegProduct = DepegProduct(depegProduct);
require(_depegProduct.getId() == productId, "ERROR:DST-010:PRODUCT_ID_MISMATCH");

IRegistry registry = IRegistry(_depegProduct.getRegistry());
IInstanceService instanceService = IInstanceService(registry.getContract("InstanceService"));

_depegRiskpool = DepegRiskpool(
address(instanceService.getComponent(
_depegProduct.getRiskpoolId())));

_token = IERC20Metadata(_depegProduct.getToken());
_treasury = instanceService.getTreasuryAddress();
}

function createDistributor(address distributor)
external
onlyOwner()
returns (DistributorInfo memory)
{
require(!isDistributor(distributor), "ERROR:DST-020:DISTRIBUTOR_ALREADY_EXISTS");

_distributor[distributor] = DistributorInfo(
COMMISSION_RATE_DEFAULT,
0, // commissionAmount,
0, // policiesSold
block.timestamp, // createdAt
block.timestamp // updatedAt
);

_distributors.push(distributor);

return _distributor[distributor];
}

function setCommissionRate(
address distributor,
uint256 commissionRate
)
external
onlyOwner()
{
require(isDistributor(distributor), "ERROR:DST-030:NOT_DISTRIBUTOR");
require(commissionRate <= COMMISSION_RATE_MAX, "ERROR:DST-031:COMMISSION_RATE_TOO_HIGH");

DistributorInfo storage info = _distributor[distributor];
info.commissionRate = commissionRate;
info.updatedAt = block.timestamp;
}


/// @dev lets a distributor create a policy for the specified wallet address
// the policy holder is this contract, the beneficiary is the specified wallet address
function createPolicy(
address buyer,
address protectedWallet,
uint256 protectedBalance,
uint256 duration,
uint256 bundleId
)
external
onlyDistributor()
returns(bytes32 processId)
{
// collect premium and commission from buyer to this contract
(
uint256 premiumTotalAmount,
uint256 premiumNetAmount,
) = _collectTokenAndUpdateCommission(
buyer,
protectedBalance,
duration,
bundleId);

// create allowance for net premium
SafeERC20.safeIncreaseAllowance(_token, _treasury, premiumNetAmount);

// create policy
// this will transfer premium amount from this contract to depeg (and keep the commission in this contract)
processId = _depegProduct.applyForPolicyWithBundle(
protectedWallet,
protectedBalance,
duration,
bundleId);

emit LogDepegPolicySold(msg.sender, processId, premiumTotalAmount, protectedWallet, protectedBalance);
}

function _collectTokenAndUpdateCommission(
address buyer,
uint256 protectedBalance,
uint256 duration,
uint256 bundleId
)
internal
returns (
uint256 premiumTotalAmount,
uint256 premiumNetAmount,
uint256 commissionAmount
)
{
address distributor = msg.sender;

// calculate amounts
(
premiumTotalAmount,
commissionAmount
) = calculatePrice(distributor, protectedBalance, duration, bundleId);

premiumNetAmount = premiumTotalAmount - commissionAmount;

// update distributor book keeping record
DistributorInfo storage info = _distributor[distributor];
info.commissionBalance += commissionAmount;
info.policiesSold += 1;
info.updatedAt = block.timestamp;

// collect total premium amount
SafeERC20.safeTransferFrom(_token, buyer, address (this), premiumTotalAmount);

emit LogDistributionInfoUpdated(distributor, commissionAmount, info.commissionBalance, info.policiesSold);
}


function calculatePrice(
address distributor,
uint256 protectedBalance,
uint256 duration,
uint256 bundleId
)
public
view
returns (
uint256 premiumTotalAmount,
uint256 commissionAmount
)
{
// fetch policy price
uint256 sumInsured = _depegRiskpool.calculateSumInsured(protectedBalance);
uint256 netPremium = _depegProduct.calculateNetPremium(
sumInsured,
duration,
bundleId);

uint256 depegPremium = _depegProduct.calculatePremium(netPremium);

// calculate commission and total premium
commissionAmount = calculateCommission(distributor, depegPremium);
premiumTotalAmount = depegPremium + commissionAmount;
}

function calculateCommission(address distributor, uint256 netPremiumAmount)
public
view
returns(uint256 commissionAmount)
{
uint256 rate = _distributor[distributor].commissionRate;
if(rate == 0) {
return 0;
}

return (netPremiumAmount * rate) / (10**DECIMALS - rate);
}

/// @dev distribution owner "override" to potentially collect commissions that
/// that is not collected by
function withdraw(uint256 amount)
external
onlyOwner()
{
require(_token.balanceOf(address(this)) >= amount, "ERROR:DST-040:BALANCE_INSUFFICIENT");
SafeERC20.safeTransfer(_token, owner(), amount);
}

function withdrawCommission(uint256 amount)
external
onlyDistributor()
{
address distributor = msg.sender;
require(getCommissionBalance(distributor) >= amount, "ERROR:DST-050:AMOUNT_TOO_LARGE");
require(_token.balanceOf(address(this)) >= amount, "ERROR:DST-051:BALANCE_INSUFFICIENT");

// update distributor book keeping record
DistributorInfo storage info = _distributor[distributor];
info.commissionBalance -= amount;
info.updatedAt = block.timestamp;

SafeERC20.safeTransfer(_token, distributor, amount);
}

function getToken() external view returns (address token) {
return address(_token);
}

function distributors() external view returns(uint256) {
return _distributors.length;
}

function getDistributor(uint256 idx) external view returns(address) {
return _distributors[idx];
}

function isDistributor(address distributor) public view returns (bool) {
return _distributor[distributor].createdAt > 0;
}

function getPoliciesSold(address distributor) external view returns (uint256 policies) {
return _distributor[distributor].policiesSold;
}

function getCommissionBalance(address distributor) public view returns (uint256 commissionAmount) {
return _distributor[distributor].commissionBalance;
}

function getCommissionRate(address distributor) external view returns (uint256 commissionRate) {
return _distributor[distributor].commissionRate;
}

function getDistributorInfo(address distributor) external view returns (DistributorInfo memory) {
return _distributor[distributor];
}
}
30 changes: 30 additions & 0 deletions contracts/DepegRiskpool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,36 @@ contract DepegRiskpool is
}


function _afterCloseBundle(uint256 bundleId)
internal
override
{
super._afterCloseBundle(bundleId);
_syncBundleExpiryWithCurrentTime(bundleId);
}

function _afterBurnBundle(uint256 bundleId)
internal
override
{
_syncBundleExpiryWithCurrentTime(bundleId);
}

function _syncBundleExpiryWithCurrentTime(uint256 bundleId)
internal
{
if (address(_chainRegistry) == address(0) || _bundleNftId[bundleId] == 0) {
return;
}

uint96 nftId = getNftId(bundleId);
(,,,,, uint256 expiryAt) = _chainRegistry.decodeBundleData(nftId);

if (expiryAt > block.timestamp) {
_chainRegistry.setBundleExpiryAt(nftId, block.timestamp);
}
}

function getSumInsuredPercentage()
external
view
Expand Down
14 changes: 14 additions & 0 deletions contracts/experiment/IUniswapV2Router.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

// Interface for Uniswap V2 Router
interface IUniswapV2Router {
function swapExactETHForTokens(
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external payable returns (uint256[] memory amounts);

function WETH() external pure returns (address);
}
Loading