Skip to content

Commit

Permalink
Added FeePoolBuilder
Browse files Browse the repository at this point in the history
  • Loading branch information
ggviana committed May 19, 2021
1 parent fde1712 commit bd008ec
Show file tree
Hide file tree
Showing 11 changed files with 202 additions and 18 deletions.
36 changes: 36 additions & 0 deletions abi/FeePoolBuilder.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
[
{
"inputs": [
{
"internalType": "address",
"name": "asset",
"type": "address"
},
{
"internalType": "uint256",
"name": "feeValue",
"type": "uint256"
},
{
"internalType": "uint8",
"name": "feeDecimals",
"type": "uint8"
},
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "buildFeePool",
"outputs": [
{
"internalType": "contract IFeePool",
"name": "",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "function"
}
]
18 changes: 18 additions & 0 deletions abi/OptionAMMFactory.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
"internalType": "contract IConfigurationManager",
"name": "_configurationManager",
"type": "address"
},
{
"internalType": "address",
"name": "_feePoolBuilder",
"type": "address"
}
],
"stateMutability": "nonpayable",
Expand Down Expand Up @@ -77,6 +82,19 @@
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "feePoolBuilder",
"outputs": [
{
"internalType": "contract IFeePoolBuilder",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
Expand Down
5 changes: 5 additions & 0 deletions abi/OptionAMMPool.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
"internalType": "contract IConfigurationManager",
"name": "_configurationManager",
"type": "address"
},
{
"internalType": "contract IFeePoolBuilder",
"name": "_feePoolBuilder",
"type": "address"
}
],
"stateMutability": "nonpayable",
Expand Down
31 changes: 31 additions & 0 deletions contracts/amm/FeePoolBuilder.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
pragma solidity 0.6.12;

import "./FeePool.sol";
import "../interfaces/IFeePool.sol";
import "../interfaces/IFeePoolBuilder.sol";

/**
* @title FeePoolBuilder
* @author Pods Finance
* @notice Builds FeePool
*/
contract FeePoolBuilder is IFeePoolBuilder {
/**
* @notice creates a new FeePool Contract
* @param asset The token in which the fees are collected
* @param feeValue The base value of fees
* @param feeDecimals Amount of decimals of feeValue
* @param owner Owner of the FeePool
* @return feePool
*/
function buildFeePool(
address asset,
uint256 feeValue,
uint8 feeDecimals,
address owner
) external override returns (IFeePool) {
FeePool feePool = new FeePool(asset, feeValue, feeDecimals);
feePool.transferOwnership(owner);
return feePool;
}
}
20 changes: 18 additions & 2 deletions contracts/amm/OptionAMMFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pragma solidity 0.6.12;
import "@openzeppelin/contracts/utils/Address.sol";
import "../interfaces/IConfigurationManager.sol";
import "../interfaces/IOptionAMMFactory.sol";
import "../interfaces/IFeePoolBuilder.sol";
import "./OptionAMMPool.sol";

/**
Expand All @@ -20,14 +21,23 @@ contract OptionAMMFactory is IOptionAMMFactory {
*/
IConfigurationManager public immutable configurationManager;

/**
* @dev store globally accessed configurations
*/
IFeePoolBuilder public immutable feePoolBuilder;

event PoolCreated(address indexed deployer, address pool, address option);

constructor(IConfigurationManager _configurationManager) public {
constructor(IConfigurationManager _configurationManager, address _feePoolBuilder) public {
require(
Address.isContract(address(_configurationManager)),
"OptionAMMFactory: Configuration Manager is not a contract"
);
require(Address.isContract(_feePoolBuilder), "OptionAMMFactory: FeePoolBuilder is not a contract");

configurationManager = _configurationManager;

feePoolBuilder = IFeePoolBuilder(_feePoolBuilder);
}

/**
Expand All @@ -45,7 +55,13 @@ contract OptionAMMFactory is IOptionAMMFactory {
) external override returns (address) {
require(address(_pools[_optionAddress]) == address(0), "OptionAMMFactory: Pool already exists");

OptionAMMPool pool = new OptionAMMPool(_optionAddress, _stableAsset, _initialSigma, configurationManager);
OptionAMMPool pool = new OptionAMMPool(
_optionAddress,
_stableAsset,
_initialSigma,
configurationManager,
feePoolBuilder
);

address poolAddress = address(pool);

Expand Down
9 changes: 5 additions & 4 deletions contracts/amm/OptionAMMPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import "../interfaces/IOptionAMMPool.sol";
import "../interfaces/IFeePool.sol";
import "../interfaces/IConfigurationManager.sol";
import "../interfaces/IEmergencyStop.sol";
import "./FeePool.sol";
import "../interfaces/IFeePoolBuilder.sol";

/**
* Represents an Option specific single-sided AMM.
Expand Down Expand Up @@ -77,15 +77,16 @@ contract OptionAMMPool is AMM, IOptionAMMPool, CappedPool, FlashloanProtection {
address _optionAddress,
address _stableAsset,
uint256 _initialSigma,
IConfigurationManager _configurationManager
IConfigurationManager _configurationManager,
IFeePoolBuilder _feePoolBuilder
) public AMM(_optionAddress, _stableAsset) CappedPool(_configurationManager) {
require(
IPodOption(_optionAddress).exerciseType() == IPodOption.ExerciseType.EUROPEAN,
"Pool: invalid exercise type"
);

feePoolA = new FeePool(_stableAsset, 15, 3);
feePoolB = new FeePool(_stableAsset, 15, 3);
feePoolA = _feePoolBuilder.buildFeePool(_stableAsset, 15, 3, address(this));
feePoolB = _feePoolBuilder.buildFeePool(_stableAsset, 15, 3, address(this));

priceProperties.currentSigma = _initialSigma;
priceProperties.sigmaInitialGuess = _initialSigma;
Expand Down
14 changes: 14 additions & 0 deletions contracts/interfaces/IFeePoolBuilder.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: agpl-3.0

pragma solidity 0.6.12;

import "./IFeePool.sol";

interface IFeePoolBuilder {
function buildFeePool(
address asset,
uint256 feeValue,
uint8 feeDecimals,
address owner
) external returns (IFeePool);
}
45 changes: 45 additions & 0 deletions test/amm/FeePoolBuilder.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
const { expect } = require('chai')
const { ethers } = require('hardhat')
const { toBigNumber } = require('../../utils/utils')

describe('FeePoolBuilder', () => {
let FeePoolBuilder, MintableERC20
let feePoolBuilder, usdc
let deployer, owner
const initialFee = toBigNumber(3)
const initialDecimals = toBigNumber(3)

before(async () => {
;[deployer, owner] = await ethers.getSigners()
;[FeePoolBuilder, MintableERC20] = await Promise.all([
ethers.getContractFactory('FeePoolBuilder'),
ethers.getContractFactory('MintableERC20')
])

usdc = await MintableERC20.deploy('USDC Token', 'USDC', 6)
})

beforeEach(async () => {
feePoolBuilder = await FeePoolBuilder.deploy()
})

it('should create a new FeePool correctly and not revert', async () => {
// Calculates address
const deterministicFeePoolAddress = await feePoolBuilder.callStatic.buildFeePool(
usdc.address,
initialFee,
initialDecimals,
owner.address
)

// Executes the transaction
const tx = feePoolBuilder.buildFeePool(usdc.address, initialFee, initialDecimals, owner.address)
await expect(tx).to.not.be.reverted

const feePool = await ethers.getContractAt('FeePool', deterministicFeePoolAddress)
expect(await feePool.owner()).to.be.equal(owner.address)
expect(await feePool.feeToken()).to.be.equal(usdc.address)
expect(await feePool.feeValue()).to.be.equal(initialFee)
expect(await feePool.feeDecimals()).to.be.equal(initialDecimals)
})
})
22 changes: 17 additions & 5 deletions test/amm/OptionAMMFactory.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,21 @@ const createConfigurationManager = require('../util/createConfigurationManager')

describe('OptionAMMFactory', () => {
let caller
let OptionAMMFactory, factory
let OptionAMMFactory, FeePoolBuilder, factory, feePoolBuilder
let configurationManager, priceProviderMock, mockUnderlyingAsset
let option
const initialSigma = '10000000000000000000000'

before(async () => {
;[caller] = await ethers.getSigners()

;[OptionAMMFactory, MockERC20] = await Promise.all([
;[OptionAMMFactory, FeePoolBuilder, MockERC20] = await Promise.all([
ethers.getContractFactory('OptionAMMFactory'),
ethers.getContractFactory('FeePoolBuilder'),
ethers.getContractFactory('MintableERC20')
])

feePoolBuilder = await FeePoolBuilder.deploy()
mockUnderlyingAsset = await MockERC20.deploy('USDC', 'USDC', 6)
await mockUnderlyingAsset.deployed()

Expand All @@ -36,7 +38,7 @@ describe('OptionAMMFactory', () => {
})

beforeEach(async () => {
factory = await OptionAMMFactory.deploy(configurationManager.address)
factory = await OptionAMMFactory.deploy(configurationManager.address, feePoolBuilder.address)
await factory.deployed()

option = await createMockOption({ configurationManager })
Expand All @@ -57,14 +59,24 @@ describe('OptionAMMFactory', () => {

it('should not deploy a factory without a proper ConfigurationManager', async () => {
await expect(
OptionAMMFactory.deploy(ethers.constants.AddressZero)
OptionAMMFactory.deploy(ethers.constants.AddressZero, feePoolBuilder.address)
).to.be.revertedWith('OptionAMMFactory: Configuration Manager is not a contract')

await expect(
OptionAMMFactory.deploy(await caller.getAddress())
OptionAMMFactory.deploy(await caller.getAddress(), feePoolBuilder.address)
).to.be.revertedWith('OptionAMMFactory: Configuration Manager is not a contract')
})

it('should not deploy a factory without a proper FeePoolBuilder', async () => {
await expect(
OptionAMMFactory.deploy(configurationManager.address, ethers.constants.AddressZero)
).to.be.revertedWith('OptionAMMFactory: FeePoolBuilder is not a contract')

await expect(
OptionAMMFactory.deploy(configurationManager.address, await caller.getAddress())
).to.be.revertedWith('OptionAMMFactory: FeePoolBuilder is not a contract')
})

it('should not create the same pool twice', async () => {
await factory.createPool(
option.address,
Expand Down
10 changes: 7 additions & 3 deletions test/amm/OptionAMMPool.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,14 @@ const scenarios = [

scenarios.forEach(scenario => {
describe('OptionAMMPool.sol - ' + scenario.name, () => {
let MockERC20, WETH, OptionAMMFactory, OptionAMMPool, PriceProvider
let MockERC20, WETH, OptionAMMFactory, FeePoolBuilder, OptionAMMPool, PriceProvider
let weth
let configurationManager
let mockUnderlyingAsset
let mockStrikeAsset
let factoryContract
let optionAMMFactory
let feePoolBuilder
let priceProvider
let option
let optionAMMPool
Expand All @@ -88,10 +89,11 @@ scenarios.forEach(scenario => {
lp.getAddress()
])

;[MockERC20, WETH, OptionAMMFactory, OptionAMMPool, PriceProvider] = await Promise.all([
;[MockERC20, WETH, OptionAMMFactory, FeePoolBuilder, OptionAMMPool, PriceProvider] = await Promise.all([
ethers.getContractFactory('MintableERC20'),
ethers.getContractFactory('WETH'),
ethers.getContractFactory('OptionAMMFactory'),
ethers.getContractFactory('FeePoolBuilder'),
ethers.getContractFactory('OptionAMMPool'),
ethers.getContractFactory('PriceProvider')
])
Expand All @@ -102,6 +104,8 @@ scenarios.forEach(scenario => {
MockERC20.deploy(scenario.strikeAssetSymbol, scenario.strikeAssetSymbol, scenario.strikeAssetDecimals)
])
defaultPriceFeed = await createPriceFeedMock(deployer)

feePoolBuilder = await FeePoolBuilder.deploy()
})

beforeEach(async function () {
Expand All @@ -127,7 +131,7 @@ scenarios.forEach(scenario => {
optionType: scenario.optionType
})

optionAMMFactory = await OptionAMMFactory.deploy(configurationManager.address)
optionAMMFactory = await OptionAMMFactory.deploy(configurationManager.address, feePoolBuilder.address)
optionAMMPool = await createNewPool(deployerAddress, optionAMMFactory, option.address, mockStrikeAsset.address, scenario.initialSigma)
})

Expand Down
10 changes: 6 additions & 4 deletions test/helpers/OptionHelper.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ const OPTION_TYPE_PUT = 0
const OPTION_TYPE_CALL = 1

describe('OptionHelper', () => {
let OptionHelper, OptionAMMFactory, MintableERC20
let OptionHelper, OptionAMMFactory, FeePoolBuilder, MintableERC20
let optionHelper, configurationManager
let stableAsset, strikeAsset, underlyingAsset
let option, pool, optionAMMFactory
let option, pool, optionAMMFactory, feePoolBuilder
let deployer, deployerAddress
let caller, callerAddress
let snapshotId
Expand All @@ -26,13 +26,15 @@ describe('OptionHelper', () => {
caller.getAddress()
])

;[OptionHelper, OptionAMMFactory, MintableERC20] = await Promise.all([
;[OptionHelper, OptionAMMFactory, FeePoolBuilder, MintableERC20] = await Promise.all([
ethers.getContractFactory('OptionHelper'),
ethers.getContractFactory('OptionAMMFactory'),
ethers.getContractFactory('FeePoolBuilder'),
ethers.getContractFactory('MintableERC20')
])

underlyingAsset = await MintableERC20.deploy('WBTC', 'WBTC', 8)
feePoolBuilder = await FeePoolBuilder.deploy()
})

beforeEach(async () => {
Expand All @@ -54,7 +56,7 @@ describe('OptionHelper', () => {
;[strikeAsset, stableAsset, optionAMMFactory] = await Promise.all([
ethers.getContractAt('MintableERC20', await option.strikeAsset()),
ethers.getContractAt('MintableERC20', await option.strikeAsset()),
OptionAMMFactory.deploy(configurationManager.address)
OptionAMMFactory.deploy(configurationManager.address, feePoolBuilder.address)
])

await configurationManager.setAMMFactory(optionAMMFactory.address)
Expand Down

0 comments on commit bd008ec

Please sign in to comment.