Skip to content
Merged
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
5,893 changes: 0 additions & 5,893 deletions spot-contracts/.openzeppelin/goerli.json

This file was deleted.

901 changes: 901 additions & 0 deletions spot-contracts/.openzeppelin/sepolia.json

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions spot-contracts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ yarn test

### Testnets

There is a testnet deployment on Goerli.
There is a testnet deployment on Sepolia.

- SPOT ERC-20 Token: [0x95014Bc18F82a98CFAA3253fbD3184125A01f848](https://goerli.etherscan.io//address/0x95014Bc18F82a98CFAA3253fbD3184125A01f848)
- Bond issuer: [0xbC060a1EbEC5eC869C4D51d4563244d4a223D307](https://goerli.etherscan.io//address/0xbC060a1EbEC5eC869C4D51d4563244d4a223D307)
- Router: [0x5e902bdCC408550b4BD612678bE2d57677664Dc9](https://goerli.etherscan.io//address/0x5e902bdCC408550b4BD612678bE2d57677664Dc9)
- RolloverVault: [0xca36B64BEbdf141623911987b93767dcA4bF6F1f](https://goerli.etherscan.io//address/0xca36B64BEbdf141623911987b93767dcA4bF6F1f)
- SPOT ERC-20 Token: [0xdcCef9065876fD654bAddeBAa778FDA43E0bfC1F](https://sepolia.etherscan.io//address/0xdcCef9065876fD654bAddeBAa778FDA43E0bfC1F)
- Bond issuer: [0x3838C8d4D092d40Cb27DD22Dafc6E1A81ea2DB60](https://sepolia.etherscan.io//address/0x3838C8d4D092d40Cb27DD22Dafc6E1A81ea2DB60)
- Router: [0x5B59915E5754C62C40Ba5e7467382ced958F8559](https://sepolia.etherscan.io//address/0x5B59915E5754C62C40Ba5e7467382ced958F8559)
- RolloverVault: [0x107614c6602A8e602952Da107B8fE62b5Ab13b04](https://sepolia.etherscan.io//address/0x107614c6602A8e602952Da107B8fE62b5Ab13b04)

## Contribute

Expand Down
27 changes: 13 additions & 14 deletions spot-contracts/contracts/BondIssuer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,10 @@ contract BondIssuer is IBondIssuer, OwnableUpgradeable {
uint256 private constant TRANCHE_RATIO_GRANULARITY = 1000;

/// @notice Address of the bond factory.
// solhint-disable-next-line immutable-vars-naming
IBondFactory public immutable bondFactory;
IBondFactory public bondFactory;

/// @notice The underlying rebasing token used for tranching.
// solhint-disable-next-line immutable-vars-naming
address public immutable collateral;
address public collateral;

/// @notice The maximum maturity duration for the issued bonds.
/// @dev In practice, bonds issued by this issuer won't have a constant duration as
Expand Down Expand Up @@ -71,12 +69,9 @@ contract BondIssuer is IBondIssuer, OwnableUpgradeable {
/// @notice The timestamp when the issue window opened during the last issue.
uint256 public lastIssueWindowTimestamp;

/// @notice Contract constructor
/// @param bondFactory_ The bond factory reference.
/// @param collateral_ The address of the collateral ERC-20.
constructor(IBondFactory bondFactory_, address collateral_) {
bondFactory = bondFactory_;
collateral = collateral_;
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}

/// @notice Contract initializer.
Expand All @@ -85,12 +80,16 @@ contract BondIssuer is IBondIssuer, OwnableUpgradeable {
/// @param minIssueTimeIntervalSec_ The minimum time between successive issues.
/// @param issueWindowOffsetSec_ The issue window offset.
function init(
IBondFactory bondFactory_,
address collateral_,
uint256 maxMaturityDuration_,
uint256[] memory trancheRatios_,
uint256 minIssueTimeIntervalSec_,
uint256 issueWindowOffsetSec_
) public initializer {
) external initializer {
__Ownable_init();
bondFactory = bondFactory_;
collateral = collateral_;
updateMaxMaturityDuration(maxMaturityDuration_);
updateTrancheRatios(trancheRatios_);
updateIssuanceTimingConfig(minIssueTimeIntervalSec_, issueWindowOffsetSec_);
Expand All @@ -107,11 +106,11 @@ contract BondIssuer is IBondIssuer, OwnableUpgradeable {
function updateTrancheRatios(uint256[] memory trancheRatios_) public onlyOwner {
trancheRatios = trancheRatios_;
uint256 ratioSum;
for (uint8 i = 0; i < trancheRatios_.length; i++) {
uint8 numTranches = uint8(trancheRatios_.length);
for (uint8 i = 0; i < numTranches; ++i) {
ratioSum += trancheRatios_[i];
}

if (ratioSum > TRANCHE_RATIO_GRANULARITY) {
if (ratioSum != TRANCHE_RATIO_GRANULARITY) {
revert UnacceptableTrancheRatios();
}
}
Expand Down
107 changes: 36 additions & 71 deletions spot-contracts/contracts/FeePolicy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,23 @@ contract FeePolicy is IFeePolicy, OwnableUpgradeable {
// https://github.com/buttonwood-protocol/tranche/blob/main/contracts/BondController.sol
uint256 private constant TRANCHE_RATIO_GRANULARITY = 1000;

/// @dev The returned fee percentages are fixed point numbers with {DECIMALS} places.
/// The decimals should line up with value expected by consumer (perp, vault).
/// @notice The returned fee percentages are fixed point numbers with {DECIMALS} places.
/// @dev The decimals should line up with value expected by consumer (perp, vault).
/// NOTE: 10**DECIMALS => 100% or 1.0
uint8 public constant DECIMALS = 8;
uint256 public constant ONE = (1 * 10 ** DECIMALS); // 1.0 or 100%

/// @dev SIGMOID_BOUND is set to 1%, i.e) the rollover fee can be at most 1% on either direction.
uint256 public constant SIGMOID_BOUND = ONE / 100; // 0.01 or 1%
/// @notice Fixed point representation of 1.0 or 100%.
uint256 public constant ONE = (1 * 10 ** DECIMALS);

uint256 public constant TARGET_SR_LOWER_BOUND = (ONE * 75) / 100; // 0.75 or 75%
uint256 public constant TARGET_SR_UPPER_BOUND = 2 * ONE; // 2.0 or 200%
/// @notice Sigmoid asymptote bound.
/// @dev Set to 0.01 or 1%, i.e) the rollover fee can be at most 1% on either direction.
uint256 public constant SIGMOID_BOUND = ONE / 100;

/// @notice Target subscription ratio lower bound, 0.75 or 75%.
uint256 public constant TARGET_SR_LOWER_BOUND = (ONE * 75) / 100;

/// @notice Target subscription ratio higher bound, 2.0 or 200%.
uint256 public constant TARGET_SR_UPPER_BOUND = 2 * ONE;

//-----------------------------------------------------------------------------
/// @notice The target subscription ratio i.e) the normalization factor.
Expand Down Expand Up @@ -111,11 +117,6 @@ contract FeePolicy is IFeePolicy, OwnableUpgradeable {
//-----------------------------------------------------------------------------
// Vault fee parameters

/// @notice The fixed amount vault fee charged during each deployment.
/// @dev Denominated in the underlying collateral asset and
/// Paid by the vault note holders to the system owner.
uint256 public vaultDeploymentFee;

/// @notice The percentage fee charged on minting vault notes.
uint256 public vaultMintFeePerc;

Expand All @@ -130,16 +131,20 @@ contract FeePolicy is IFeePolicy, OwnableUpgradeable {

//-----------------------------------------------------------------------------

/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}

/// @notice Contract initializer.
function init() public initializer {
function init() external initializer {
__Ownable_init();

// initializing mint/burn fees to zero
perpMintFeePerc = 0;
perpBurnFeePerc = 0;
vaultMintFeePerc = 0;
vaultBurnFeePerc = 0;
vaultDeploymentFee = 0;

// initializing swap fees to 100%, to disable swapping initially
vaultUnderlyingToPerpSwapFeePerc = ONE;
Expand Down Expand Up @@ -234,12 +239,6 @@ contract FeePolicy is IFeePolicy, OwnableUpgradeable {
vaultBurnFeePerc = vaultBurnFeePerc_;
}

/// @notice Updates the vault's deployment fee parameters.
/// @param vaultDeploymentFee_ The new deployment fee denominated in the underlying tokens.
function updateVaultDeploymentFees(uint256 vaultDeploymentFee_) external onlyOwner {
vaultDeploymentFee = vaultDeploymentFee_;
}

/// @notice Updates the vault's share of the underlying to perp swap fee.
/// @param feePerc The new fee percentage.
function updateVaultUnderlyingToPerpSwapFeePerc(uint256 feePerc) external onlyOwner {
Expand All @@ -263,14 +262,14 @@ contract FeePolicy is IFeePolicy, OwnableUpgradeable {

/// @inheritdoc IFeePolicy
/// @dev Minting perps reduces system dr, i.e) drPost < drPre.
function computePerpMintFeePerc(uint256 drPre, uint256 drPost) public view override returns (uint256) {
return _stepFnFeePerc(drPost, drPre, perpMintFeePerc, 0);
function computePerpMintFeePerc() public view override returns (uint256) {
return perpMintFeePerc;
}

/// @inheritdoc IFeePolicy
/// @dev Burning perps increases system dr, i.e) drPost > drPre.
function computePerpBurnFeePerc(uint256 drPre, uint256 drPost) public view override returns (uint256) {
return _stepFnFeePerc(drPre, drPost, 0, perpBurnFeePerc);
function computePerpBurnFeePerc() public view override returns (uint256) {
return perpBurnFeePerc;
}

/// @inheritdoc IFeePolicy
Expand All @@ -287,46 +286,35 @@ contract FeePolicy is IFeePolicy, OwnableUpgradeable {

/// @inheritdoc IFeePolicy
/// @dev Minting vault notes increases system dr, i.e) drPost > drPre.
function computeVaultMintFeePerc(uint256 drPre, uint256 drPost) external view override returns (uint256) {
return _stepFnFeePerc(drPre, drPost, 0, vaultMintFeePerc);
function computeVaultMintFeePerc() external view override returns (uint256) {
return vaultMintFeePerc;
}

/// @inheritdoc IFeePolicy
function computeVaultBurnFeePerc(uint256 /*drPre*/, uint256 /*drPost*/) external view override returns (uint256) {
function computeVaultBurnFeePerc() external view override returns (uint256) {
return vaultBurnFeePerc;
}

/// @inheritdoc IFeePolicy
function computeVaultDeploymentFee() external view override returns (uint256) {
return vaultDeploymentFee;
}

/// @inheritdoc IFeePolicy
/// @dev Swapping by minting perps reduces system dr, i.e) drPost < drPre.
function computeUnderlyingToPerpSwapFeePercs(
uint256 drPre,
function computeUnderlyingToPerpVaultSwapFeePerc(
uint256 /*drPre*/,
uint256 drPost
) external view override returns (uint256, uint256) {
) external view override returns (uint256) {
// When the after op deviation ratio is below the bound,
// swapping is disabled. (fees are set to 100%)
return (
computePerpMintFeePerc(drPre, drPost),
(drPost < deviationRatioBoundLower ? ONE : vaultUnderlyingToPerpSwapFeePerc)
);
return (drPost < deviationRatioBoundLower ? ONE : vaultUnderlyingToPerpSwapFeePerc);
}

/// @inheritdoc IFeePolicy
/// @dev Swapping by burning perps increases system dr, i.e) drPost > drPre.
function computePerpToUnderlyingSwapFeePercs(
uint256 drPre,
function computePerpToUnderlyingVaultSwapFeePerc(
uint256 /*drPre*/,
uint256 drPost
) external view override returns (uint256, uint256) {
) external view override returns (uint256) {
// When the after op deviation ratio is above the bound,
// swapping is disabled. (fees are set to 100%)
return (
computePerpBurnFeePerc(drPre, drPost),
(drPost > deviationRatioBoundUpper ? ONE : vaultPerpToUnderlyingSwapFeePerc)
);
return (drPost > deviationRatioBoundUpper ? ONE : vaultPerpToUnderlyingSwapFeePerc);
}

/// @inheritdoc IFeePolicy
Expand All @@ -336,31 +324,8 @@ contract FeePolicy is IFeePolicy, OwnableUpgradeable {

/// @inheritdoc IFeePolicy
function computeDeviationRatio(SubscriptionParams memory s) public view returns (uint256) {
return computeDeviationRatio(s.perpTVL, s.vaultTVL, s.seniorTR);
}

/// @inheritdoc IFeePolicy
function computeDeviationRatio(uint256 perpTVL, uint256 vaultTVL, uint256 seniorTR) public view returns (uint256) {
// NOTE: We assume that perp's TVL and vault's TVL values have the same base denomination.
uint256 juniorTR = TRANCHE_RATIO_GRANULARITY - seniorTR;
return (vaultTVL * seniorTR).mulDiv(ONE, (perpTVL * juniorTR)).mulDiv(ONE, targetSubscriptionRatio);
}

/// @dev Computes step function fee perc, with a dr cutoff at 1.0. Expects drL < drU.
function _stepFnFeePerc(uint256 drL, uint256 drU, uint256 f1, uint256 f2) private pure returns (uint256) {
// When drU is below the cutoff, we use f1.
if (drU <= ONE) {
return f1;
}
// When drL is above the cutoff, we use f2.
else if (drL > ONE) {
return f2;
}
// Otherwise we use f1 and f2 partially.
else {
uint256 deltaDR = drU - drL;
return (f1.mulDiv(ONE - drL, deltaDR, MathUpgradeable.Rounding.Up) +
f2.mulDiv(drU - ONE, deltaDR, MathUpgradeable.Rounding.Up));
}
uint256 juniorTR = TRANCHE_RATIO_GRANULARITY - s.seniorTR;
return (s.vaultTVL * s.seniorTR).mulDiv(ONE, (s.perpTVL * juniorTR)).mulDiv(ONE, targetSubscriptionRatio);
}
}
Loading