Skip to content

Commit 3a1c2e1

Browse files
authored
Merge pull request #105 from laminar-protocol/full-chainlink-oracles
Add all chainlink oracles and testnet deployment config
2 parents 501b33c + 6d78938 commit 3a1c2e1

28 files changed

+1472
-210
lines changed

artifacts/kovan/abi/MarginFlowProtocolConfig.json

Lines changed: 619 additions & 0 deletions
Large diffs are not rendered by default.

artifacts/kovan/abi/SyntheticFlowToken.json

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -542,13 +542,28 @@
542542
"internalType": "address",
543543
"name": "_protocol",
544544
"type": "address"
545+
},
546+
{
547+
"internalType": "uint256",
548+
"name": "_extremeCollateralRatio",
549+
"type": "uint256"
550+
},
551+
{
552+
"internalType": "uint256",
553+
"name": "_liquidationCollateralRatio",
554+
"type": "uint256"
555+
},
556+
{
557+
"internalType": "uint256",
558+
"name": "_defaultCollateralRatio",
559+
"type": "uint256"
545560
}
546561
],
547562
"name": "initialize",
548563
"outputs": [],
549564
"stateMutability": "nonpayable",
550565
"type": "function",
551-
"signature": "0x8f15b414"
566+
"signature": "0x6ec2a96e"
552567
},
553568
{
554569
"inputs": [

artifacts/kovan/deployment.json

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,26 @@
11
{
22
"baseToken": "0xbF7A7169562078c96f0eC1A8aFD6aE50f12e5A99",
33
"cToken": "0x0A1e4D0B5c71B955c0a5993023fc48bA6E380496",
4-
"moneyMarket": "0x4925c9B04329DFB4C1a758Be1246ADb727366860",
5-
"iToken": "0x99AA95a0BBAFfda386489c818f2110EA984d7ebC",
6-
"oracle": "0x7B01E27b89D7fF0Ce891E3b990Fd5f81eC67b25e",
7-
"syntheticProtocol": "0x5D33d44AEAE8a3784c5Eac0A0bacfB0d85556BED",
8-
"fEUR": "0x0CE323388b1A5b040baf033fbbB5b3ceF6FfB460",
9-
"fJPY": "0x4F1Cca2cbf2E44A108a010DF36f398aF5f95624b",
10-
"fXAU": "0x0DE89Fbd1D41D0feda36d93aA259eB84CE8cc272",
11-
"fAAPL": "0xb354f12289AFcFcd8B1022b3D6638F668668515c",
12-
"marginProtocol": "0x83717915a982F1a7EC120f2Ffb48Df703b75DCF7",
13-
"marginProtocolSafety": "0x0FFA116E89f9fF2e9A47ae118eBa5011369C864B",
14-
"marginProtocolConfig": "0xDB0079a15c3021FFcc9F34b45215534b6aD335b8",
15-
"marginPoolRegistry": "0x190b0f3Aa96a474D67AF9255DC6893303c53774A",
16-
"marginPool": "0xA7B0990bfeDA6f8FFB7b67873Add2B3F35C00287",
17-
"marginPool2": "0x3E514eF4a4bc5b6e9893957f834bb5bb21048F0A",
18-
"syntheticPool": "0x64796aE5cEA5aA2413946938dA90997748dB554F",
19-
"syntheticPool2": "0x6c3DbDD23a7e9367E8F542e7C86785d46E3a644f"
4+
"moneyMarket": "0x28A3ad47A5403376fa688ba9F1dA8dC9D374f084",
5+
"iToken": "0x660A43C5e3D515b5279770B90C10bf8387386e76",
6+
"oracle": "0x3726e065443c5677902665b0084959Ec4bC76447",
7+
"syntheticProtocol": "0x4Ef4BaA04bb0506DcF742435a7e3A70c23c747A7",
8+
"fEUR": "0x779DB2A890B7EbfFb9B38631Deb81C1febfB5361",
9+
"fJPY": "0xf3Cdc6904Bf22EE0C5DF3C91378Ef32df4A4eA74",
10+
"fCAD": "0x12015F48Ef76327397C86314Bcd14f0E41a446c7",
11+
"fCHF": "0x71d11FB22D33A31B33fA123d684C9E9dE33E741F",
12+
"fGBP": "0xc6D60d30a6504E5df86538Be84789b9dA0Fa6974",
13+
"fAUD": "0x564bC9ee2808C7aDB2C88b9eb899988E0c9E802C",
14+
"fUSOIL": "0x6faa090A838d95fB686c768dd4c3394386E66Ffd",
15+
"fXAU": "0xE9a6d4dF338dce3a1F9701127EE2273fce9EC592",
16+
"fBTC": "0x87d5f3c8DdE9621ce5d79c03E96F1d3a583dFb32",
17+
"fETH": "0x029FB7Dca3C2d70cA14a8D4F78ce646e25BC8F46",
18+
"marginProtocol": "0x10D09B705dd5162Bf50C38c1ac7b008E55cfE158",
19+
"marginProtocolSafety": "0x491E03E8208ef5A23b9eEACB976A6CF17a35C4E9",
20+
"marginProtocolConfig": "0xfc444961b454a761b48655ba2A4A47849B0Cd4Ae",
21+
"marginPoolRegistry": "0x932626A69Ce19f63868D84DCA037086366E26fac",
22+
"marginPoolACME": "0xc2452E6CF815dE52BE2CfB63E51E6DfFdCEA53a1",
23+
"marginPoolGeneral": "0x7884f42F780b6942e29151F96EEFC287fF2A0172",
24+
"syntheticPoolGeneral": "0x61A7645ef693Ea7740b121232Ddc7b874Be7Fa09",
25+
"syntheticPoolXYZ": "0x2b1627CFF4Eb38C774Ec1008045CEd2C4776eEf6"
2026
}

contracts/impls/margin/MarginFlowProtocol.sol

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -459,11 +459,11 @@ contract MarginFlowProtocol is Initializable, UpgradeReentrancyGuard {
459459
) internal {
460460
int256 heldSignum = _leverage > 0 ? int256(1) : int256(-1);
461461
uint256 leveragedDebits = _leveragedHeld.mulPercent(_debitsPrice);
462-
uint256 leveragedHeldInUsd = uint256(
462+
uint256 leveragedDebitsInUsd = uint256(
463463
market.getUsdValue(_pair.base, int256(leveragedDebits))
464464
);
465465
uint256 marginHeld = uint256(
466-
int256(leveragedHeldInUsd)
466+
int256(leveragedDebitsInUsd)
467467
.mul(_leverage > 0 ? int256(1) : int256(-1))
468468
.div(_leverage)
469469
);
@@ -474,14 +474,14 @@ contract MarginFlowProtocol is Initializable, UpgradeReentrancyGuard {
474474
poolLongPositionAccPerPair[_pool][_pair.base][_pair.quote][CurrencyType.BASE] = poolLongPositionAccPerPair[_pool][_pair.base][_pair.quote][CurrencyType.BASE]
475475
.add(leveragedDebits);
476476
poolLongPositionAccPerPair[_pool][_pair.base][_pair.quote][CurrencyType.USD] = poolLongPositionAccPerPair[_pool][_pair.base][_pair.quote][CurrencyType.USD]
477-
.add(leveragedHeldInUsd);
477+
.add(leveragedDebitsInUsd);
478478
} else {
479479
poolShortPositionAccPerPair[_pool][_pair.base][_pair.quote][CurrencyType.QUOTE] = poolShortPositionAccPerPair[_pool][_pair.base][_pair.quote][CurrencyType.QUOTE]
480480
.add(_leveragedHeld);
481481
poolShortPositionAccPerPair[_pool][_pair.base][_pair.quote][CurrencyType.BASE] = poolShortPositionAccPerPair[_pool][_pair.base][_pair.quote][CurrencyType.BASE]
482482
.add(leveragedDebits);
483483
poolShortPositionAccPerPair[_pool][_pair.base][_pair.quote][CurrencyType.USD] = poolShortPositionAccPerPair[_pool][_pair.base][_pair.quote][CurrencyType.USD]
484-
.add(leveragedHeldInUsd);
484+
.add(leveragedDebitsInUsd);
485485
}
486486

487487
Position memory position = _createPosition(
@@ -490,7 +490,7 @@ contract MarginFlowProtocol is Initializable, UpgradeReentrancyGuard {
490490
_leverage,
491491
_leveragedHeld,
492492
leveragedDebits,
493-
leveragedHeldInUsd,
493+
leveragedDebitsInUsd,
494494
marginHeld
495495
);
496496

@@ -508,7 +508,7 @@ contract MarginFlowProtocol is Initializable, UpgradeReentrancyGuard {
508508
_pair.base,
509509
_pair.quote,
510510
_leverage,
511-
int256(leveragedHeldInUsd).mul(heldSignum.mul(-1)),
511+
int256(leveragedDebitsInUsd).mul(heldSignum.mul(-1)),
512512
_debitsPrice.value
513513
);
514514
}

contracts/impls/margin/MarginFlowProtocolConfig.sol

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ contract MarginFlowProtocolConfig is Initializable, UpgradeOwnable {
3939
uint256 public liquidityPoolELLLiquidateThreshold;
4040

4141
function initialize(
42+
uint256 _maxSpread,
4243
uint256 _initialMinLeverage,
4344
uint256 _initialMaxLeverage,
4445
uint256 _initialMinLeverageAmount,
@@ -52,13 +53,12 @@ contract MarginFlowProtocolConfig is Initializable, UpgradeOwnable {
5253
) public initializer {
5354
UpgradeOwnable.initialize(msg.sender);
5455

56+
maxSpread = _maxSpread;
5557
minLeverage = _initialMinLeverage;
5658
maxLeverage = _initialMaxLeverage;
5759
minLeverageAmount = _initialMinLeverageAmount;
5860
swapRateUnit = _swapRateUnit;
5961

60-
maxSpread = 1 ether / 10; // 10% TODO: pick a justified value
61-
6262
liquidityPoolENPMarginThreshold = _initialLiquidityPoolENPMarginThreshold;
6363
liquidityPoolELLMarginThreshold = _initialLiquidityPoolELLMarginThreshold;
6464
liquidityPoolENPLiquidateThreshold = _initialLiquidityPoolENPLiquidateThreshold;
@@ -124,8 +124,8 @@ contract MarginFlowProtocolConfig is Initializable, UpgradeOwnable {
124124
*/
125125
function addTradingPair(address _base, address _quote, int256 _swapRateLong, int256 _swapRateShort) external onlyOwner {
126126
require(_base != address(0) && _quote != address(0) && _swapRateLong != 0 && _swapRateShort != 0, "0");
127-
require(_base != _quote, "TP3");
128127
require(!tradingPairWhitelist[_base][_quote], "TP2");
128+
require(_base != _quote, "TP3");
129129

130130
currentSwapRates[_base][_quote][PositionType.LONG] = Percentage.SignedPercent(_swapRateLong);
131131
currentSwapRates[_base][_quote][PositionType.SHORT] = Percentage.SignedPercent(_swapRateShort);

contracts/impls/margin/MarginMarketLib.sol

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -269,24 +269,24 @@ library MarginMarketLib {
269269
MarginFlowProtocol.TradingPair memory _pair,
270270
uint256[4] memory _pairValues
271271
) public returns (int256) {
272-
(int256 longUnrealized,) = getUnrealizedPlForParams(
272+
(int256 longUnrealized,) = _pairValues[2] > 0 ? getUnrealizedPlForParams(
273273
self,
274274
_pool,
275275
_pair,
276276
int256(_pairValues[0]).mul(-1),
277277
int256(_pairValues[2]),
278278
1,
279279
0
280-
);
281-
(int256 shortUnrealized,) = getUnrealizedPlForParams(
280+
) : (int256(0), Percentage.Percent(0));
281+
(int256 shortUnrealized,) = _pairValues[3] > 0 ? getUnrealizedPlForParams(
282282
self,
283283
_pool,
284284
_pair,
285285
int256(_pairValues[1]),
286286
int256(_pairValues[3]).mul(-1),
287287
-1,
288288
0
289-
);
289+
) : (int256(0), Percentage.Percent(0));
290290

291291
return longUnrealized.add(shortUnrealized);
292292
}
@@ -316,7 +316,7 @@ library MarginMarketLib {
316316
) public returns (uint256, uint256, int256) {
317317
Percentage.Percent memory basePrice = Percentage.Percent(getPrice(self, address(self.moneyMarket.baseToken())));
318318

319-
uint256 net = (int256(_longUsd).sub(int256(_shortUsd)) >= 0
319+
uint256 net = (_longUsd > _shortUsd
320320
? uint256(int256(_longUsd).sub(int256(_shortUsd)))
321321
: uint256(-(int256(_longUsd).sub(int256(_shortUsd))))
322322
).mulPercent(basePrice);

contracts/impls/oracles/ChainLinkOracle.sol

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,38 +9,43 @@ import "../../interfaces/PriceOracleInterface.sol";
99
import "../../libs/upgrades/UpgradeOwnable.sol";
1010

1111
contract ChainLinkOracle is ChainlinkClient, PriceOracleInterface, Initializable, UpgradeOwnable {
12-
mapping (address => AggregatorInterface) private aggregators;
12+
mapping (address => AggregatorInterface) public aggregators;
13+
address public usdToken;
1314

1415
function initialize(
1516
address _link,
16-
address _eurRef,
17-
address _jpyRef,
18-
address _xauRef,
19-
address _aaplRef,
20-
address _eurToken,
21-
address _jpyToken,
22-
address _xauToken,
23-
address _aaplToken
17+
address _usdToken,
18+
address[] memory _currencyReferences,
19+
address[] memory _tokenReferences
2420
) public initializer {
2521
UpgradeOwnable.initialize(msg.sender);
2622

23+
require(_currencyReferences.length == _tokenReferences.length, "Token count must match oracle count");
24+
2725
if(_link == address(0)) {
2826
setPublicChainlinkToken();
2927
} else {
3028
setChainlinkToken(_link);
3129
}
3230

33-
aggregators[_eurToken] = AggregatorInterface(_eurRef);
34-
aggregators[_jpyToken] = AggregatorInterface(_jpyRef);
35-
aggregators[_xauToken] = AggregatorInterface(_xauRef);
36-
aggregators[_aaplToken] = AggregatorInterface(_aaplRef);
31+
usdToken = _usdToken;
32+
33+
for (uint256 i = 0; i < _currencyReferences.length; i++) {
34+
aggregators[_tokenReferences[i]] = AggregatorInterface(_currencyReferences[i]);
35+
}
3736
}
3837

3938
function setOracleAddress(address _token, address _aggregator) public onlyOwner {
4039
aggregators[_token] = AggregatorInterface(_aggregator);
4140
}
4241

4342
function getPrice(address _key) public override returns (uint256) {
43+
if (_key == usdToken) {
44+
return 1e18;
45+
}
46+
47+
require(address(aggregators[_key]) != address(0), "Invalid token address for oracle");
48+
4449
int256 price = aggregators[_key].latestAnswer();
4550
require(price > 0, "no price available");
4651

contracts/impls/synthetic/SyntheticFlowToken.sol

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@ contract SyntheticFlowToken is ProtocolOwnable, ERC20, ERC20DetailedUpgradable {
4141
string memory _name,
4242
string memory _symbol,
4343
MoneyMarketInterface _moneyMarket,
44-
address _protocol
44+
address _protocol,
45+
uint _extremeCollateralRatio,
46+
uint _liquidationCollateralRatio,
47+
uint _defaultCollateralRatio
4548
) public initializer {
4649
ProtocolOwnable.initialize(_protocol);
4750
ERC20DetailedUpgradable.initialize(_name, _symbol, 18);
@@ -50,10 +53,9 @@ contract SyntheticFlowToken is ProtocolOwnable, ERC20, ERC20DetailedUpgradable {
5053

5154
moneyMarket.iToken().safeApprove(_protocol, MAX_UINT);
5255

53-
// TODO: from constructor parameter
54-
extremeCollateralRatio = Percentage.fromFraction(1, 100);
55-
liquidationCollateralRatio = Percentage.fromFraction(5, 100);
56-
defaultCollateralRatio = Percentage.fromFraction(10, 100);
56+
extremeCollateralRatio = Percentage.Percent(_extremeCollateralRatio);
57+
liquidationCollateralRatio = Percentage.Percent(_liquidationCollateralRatio);
58+
defaultCollateralRatio = Percentage.Percent(_defaultCollateralRatio);
5759
}
5860

5961
function setLiquidationCollateralRatio(uint percent) external onlyProtocol {

cucumber/step-definitions/margin.steps.ts

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ const flowMarginProtocolSafetyContract = new web3.eth.Contract(
4343
flowMarginProtocolSafetyAddress,
4444
);
4545

46-
const poolAddress = deployment.marginPool;
46+
const poolAddress = deployment.marginPoolGeneral;
4747
const poolContract = new web3.eth.Contract(poolAbi as any, poolAddress);
4848

4949
const oracleAddress = deployment.oracle;
@@ -61,7 +61,6 @@ const tokenStringToAddress = {
6161
EUR: deployment.fEUR,
6262
JPY: deployment.fJPY,
6363
AUX: deployment.fXAU,
64-
AAPL: deployment.fAAPL,
6564
};
6665

6766
const accountOf = (name: string): Account => {
@@ -101,7 +100,7 @@ const transferUsd = async (to: string, amount: BN): Promise<any> => {
101100
const parseCurrency = (amount: string): string => {
102101
const parsed = amount.replace('F', 'f');
103102

104-
return deployment[parsed as 'fEUR' | 'fJPY' | 'fXAU' | 'fAAPL'];
103+
return deployment[parsed as 'fEUR' | 'fJPY' | 'fXAU'];
105104
};
106105

107106
const parseAmount = (amount: string): BN => {
@@ -508,10 +507,10 @@ Then(
508507

509508
const swapRate = parseSwapRate(additionalSwapRate);
510509
const currentLongSwapRate = await flowMarginProtocolConfigContract.methods
511-
.currentSwapRates(baseAddress, quoteAddress, true)
510+
.currentSwapRates(baseAddress, quoteAddress, '0')
512511
.call();
513512
const currentShortSwapRate = await flowMarginProtocolConfigContract.methods
514-
.currentSwapRates(baseAddress, quoteAddress, false)
513+
.currentSwapRates(baseAddress, quoteAddress, '1')
515514
.call();
516515
const newLongSwapRate = new BN(currentLongSwapRate).add(swapRate);
517516
const newShortwapRate = new BN(currentShortSwapRate).add(swapRate);
@@ -728,6 +727,21 @@ Given('margin liquidity pool margin call', async (table: TableDefinition) => {
728727
Then('margin liquidity pool liquidate', async (table: TableDefinition) => {
729728
for (const [result] of table.rows()) {
730729
try {
730+
const enpThreshold = await flowMarginProtocolConfigContract.methods
731+
.liquidityPoolENPLiquidateThreshold()
732+
.call();
733+
const ellThreshold = await flowMarginProtocolConfigContract.methods
734+
.liquidityPoolELLLiquidateThreshold()
735+
.call();
736+
const enpAndEll = await flowMarginProtocolSafetyContract.methods
737+
.getEnpAndEll(poolAddress)
738+
.call();
739+
console.log({
740+
enp: enpAndEll['0'].toString(),
741+
ell: enpAndEll['1'].toString(),
742+
enpThreshold: enpThreshold.toString(),
743+
ellThreshold: ellThreshold.toString(),
744+
});
731745
await sendTx({
732746
contractMethod: flowMarginProtocolSafetyContract.methods.liquidateLiquidityPool(
733747
poolAddress,
@@ -737,7 +751,11 @@ Then('margin liquidity pool liquidate', async (table: TableDefinition) => {
737751
if (result !== 'Ok')
738752
expect.fail(`Pool liquidation call should have reverted, but didnt!`);
739753

754+
console.log('Before force close 1');
755+
740756
const alicePositionCount = await forceCloseForPool(alice, 0);
757+
758+
console.log('Before force close 2');
741759
await forceCloseForPool(bob, alicePositionCount);
742760
} catch (error) {
743761
if (result === 'Ok')

dev-scripts/getKovanFaiFaucet.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
module.exports = callback => {
2+
async function getFaucetDai() {
3+
const FaucetInterface = artifacts.require('FaucetInterface');
4+
const FaucetInterfaceContract = await FaucetInterface.at(
5+
'0xbf7a7169562078c96f0ec1a8afd6ae50f12e5a99',
6+
);
7+
8+
await FaucetInterfaceContract.allocateTo(
9+
'0x15ae150d7dC03d3B635EE90b85219dBFe071ED35',
10+
'100000000000000000000000000',
11+
);
12+
}
13+
14+
getFaucetDai()
15+
.then(() => {
16+
console.log('Successfully finished!');
17+
callback();
18+
})
19+
.catch(error => console.log({ error }));
20+
};

0 commit comments

Comments
 (0)