Skip to content

Commit aa3d83b

Browse files
authored
Merge pull request #113 from primitivefinance/feature/deploy-mainnet-options
Feature/deploy mainnet options
2 parents 52711be + 69001c9 commit aa3d83b

6 files changed

+890
-1
lines changed

buidler.config.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ Object.assign(module.exports, {
4141
},
4242
chainId: 1,
4343
from: "0xaF31D3C2972F62Eb08F96a1Fe29f579d61b4294D",
44-
gasPrice: 60000000000,
44+
gasPrice: 40000000000,
4545
},
4646
rinkeby: {
4747
url: rinkeby,

scripts/deploy_mainnet_options.js

+290
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
1+
const { parseEther, formatEther } = require("ethers/lib/utils");
2+
const { checkInitialization } = require("../test/lib/utils");
3+
const TestERC20 = require("../artifacts/TestERC20.json");
4+
const { ADDRESSES } = require("../test/lib/constants");
5+
const { ZERO_ADDRESS } = ADDRESSES;
6+
const fs = require("fs");
7+
8+
/**
9+
* @dev Checks the optionTemplate and redeemTemplate. If they are address zero, it will call deployTemplate().
10+
* @param {*} optionFactory The OptionFactory contract instance.
11+
* @param {*} redeemFactory The RedeemFactory contract instance.
12+
*/
13+
const checkTemplates = async (optionFactory, redeemFactory) => {
14+
const optionTemplate = await optionFactory.optionTemplate();
15+
const redeemTemplate = await redeemFactory.redeemTemplate();
16+
if (optionTemplate.toString() == ethers.constants.AddressZero.toString()) {
17+
await optionFactory.deployOptionTemplate();
18+
}
19+
if (redeemTemplate.toString() == ethers.constants.AddressZero.toString()) {
20+
await redeemFactory.deployRedeemTemplate();
21+
}
22+
return { optionTemplate, redeemTemplate };
23+
};
24+
25+
const writeOptionJson = (optionJsonObject, path) => {
26+
let data = JSON.stringify(optionJsonObject, null, 2);
27+
fs.writeFileSync(path, data);
28+
};
29+
30+
/**
31+
* @dev Gets the contract instance of a contract using its name.
32+
* @param {*} contractName The contract name `contract NAME {}`.
33+
* @param {*} signer The ethers js Signer object to call the transaction.
34+
* @return Contract instance.
35+
*/
36+
const getInstance = async (contractName, signer) => {
37+
const contract = await deployments.get(contractName);
38+
const instance = new ethers.Contract(
39+
contract.address,
40+
contract.abi,
41+
signer
42+
);
43+
return instance;
44+
};
45+
46+
/**
47+
* @dev Function to validate argument data and create the correct data object.
48+
* @return optionParametersObject Returns an optionParametersObject.
49+
*/
50+
const getOptionParametersObject = (
51+
underlyingToken,
52+
strikeToken,
53+
base,
54+
quote,
55+
expiry
56+
) => {
57+
const optionParametersObject = {
58+
underlyingToken: underlyingToken,
59+
strikeToken: strikeToken,
60+
base: parseEther(base),
61+
quote: parseEther(quote),
62+
expiry: expiry,
63+
};
64+
return optionParametersObject;
65+
};
66+
67+
/**
68+
* @dev Concatenates a string of the option's symbol in the format:
69+
* ASSET + YY + MM + DD + TYPE + STRIKE
70+
* @param {*} optionParametersObject The object with the option's parameters.
71+
* @returns An option's symbol according to its parameters.
72+
*/
73+
const getOptionSymbol = (underlyingSymbol, optionParametersObject) => {
74+
let base = formatEther(optionParametersObject.base);
75+
let quote = formatEther(optionParametersObject.quote);
76+
let expiry = optionParametersObject.expiry;
77+
let asset = underlyingSymbol.toString().toUpperCase();
78+
let type;
79+
let strike;
80+
if (base == 1) {
81+
type = "C";
82+
strike = +quote;
83+
}
84+
if (quote == 1) {
85+
type = "P";
86+
strike = +base;
87+
}
88+
89+
const date = new Date(expiry * 1000);
90+
let month = (date.getUTCMonth() + 1).toString();
91+
let day = date.getUTCDate().toString();
92+
let year = date.getUTCFullYear().toString();
93+
let formattedSymbol =
94+
asset +
95+
year +
96+
month +
97+
day +
98+
type +
99+
strike.toString().padStart(6, "0").padEnd(2, "0");
100+
return formattedSymbol;
101+
};
102+
103+
/**
104+
* @dev Deploys an option contract clone through the Registry contract.
105+
* @notice Deploys a Uniswap V2 Pair and adds liquidity to it (if its testnet).
106+
* @param optionParametersObject An object with the option parameters that will be deployed.
107+
* @return Address of the deployed option clone.
108+
*/
109+
const deployOption = async (optionParametersObject) => {
110+
// Get the Registry admin.
111+
const { deployer } = await getNamedAccounts();
112+
const signer = ethers.provider.getSigner(deployer);
113+
114+
// Get the contract instances.
115+
const registry = await getInstance("Registry", signer);
116+
const optionFactory = await getInstance("OptionFactory", signer);
117+
const redeemFactory = await getInstance("RedeemFactory", signer);
118+
119+
// Check to see if Registry is in a ready-to-deploy-clone state.
120+
await checkInitialization(registry, optionFactory, redeemFactory);
121+
await checkTemplates(optionFactory, redeemFactory);
122+
123+
// Get the option parameters from the object.
124+
let underlyingToken = optionParametersObject.underlyingToken;
125+
let strikeToken = optionParametersObject.strikeToken;
126+
let base = optionParametersObject.base;
127+
let quote = optionParametersObject.quote;
128+
let expiry = optionParametersObject.expiry;
129+
130+
// Check to see if the option exists by trying to get its address. Returns zero address if not deployed.
131+
let optionAddress = await registry.getOptionAddress(
132+
underlyingToken,
133+
strikeToken,
134+
base,
135+
quote,
136+
expiry
137+
);
138+
139+
// Deploy the option if it is the zero address.
140+
let deployCloneTx;
141+
if (optionAddress == ZERO_ADDRESS) {
142+
try {
143+
deployCloneTx = await registry.deployOption(
144+
underlyingToken,
145+
strikeToken,
146+
base,
147+
quote,
148+
expiry,
149+
{ gasLimit: 1000000 }
150+
);
151+
} catch (err) {
152+
console.log(err);
153+
}
154+
// get deployed option address
155+
optionAddress = await registry.getOptionAddress(
156+
underlyingToken,
157+
strikeToken,
158+
base,
159+
quote,
160+
expiry
161+
);
162+
}
163+
164+
return optionAddress;
165+
};
166+
167+
const ADDRESS_FOR_MARKET = {
168+
yfi: "0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e",
169+
eth: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
170+
sushi: "0x6B3595068778DD592e39A122f4f5a5cF09C90fE2",
171+
comp: "0xc00e94Cb662C3520282E6f5717214004A7f26888",
172+
uni: "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984",
173+
link: "0x514910771AF9Ca656af840dff83E8264EcF986CA",
174+
aave: "0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9",
175+
snx: "0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F",
176+
mkr: "0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2",
177+
};
178+
179+
const STRIKES_FOR_MARKET = {
180+
yfi: ["14500", "18500", "28000"],
181+
eth: ["340", "400", "440"],
182+
sushi: ["0.75", "1", "2"],
183+
comp: ["125", "150", "250"],
184+
uni: ["3", "5", "10"],
185+
link: ["15", "20", "40"],
186+
aave: ["30", "50", "75"],
187+
snx: ["4", "6", "10"],
188+
mkr: ["500", "750", "1250"],
189+
};
190+
191+
const DAI = "0x6B175474E89094C44Da98b954EedeAC495271d0F";
192+
193+
// 12/30/2020 @ 12:00am (UTC)
194+
const DECEMBER_30 = "1609286400";
195+
const BASE = "1";
196+
197+
async function main() {
198+
// Get the signer.
199+
const [signer] = await ethers.getSigners();
200+
201+
// allOptions = { [eth]: [ [address0, address1, base, quote, expiry], ] }
202+
let allOptions = {};
203+
204+
// Each of the assets
205+
let keys = Object.keys(ADDRESS_FOR_MARKET);
206+
207+
// for each asset create an array of calls and puts
208+
for (let k = 0; k < keys.length; k++) {
209+
let asset = keys[k];
210+
let quotes = STRIKES_FOR_MARKET[asset];
211+
let address = ADDRESS_FOR_MARKET[asset];
212+
213+
let array = [];
214+
215+
// Calls
216+
for (let q = 0; q < quotes.length; q++) {
217+
let quote = quotes[q];
218+
let option = [address, DAI, BASE, quote, DECEMBER_30];
219+
array.push(option);
220+
}
221+
222+
// Puts
223+
for (let q = 0; q < quotes.length; q++) {
224+
let quote = quotes[q];
225+
let option = [DAI, address, quote, BASE, DECEMBER_30];
226+
array.push(option);
227+
}
228+
229+
// allOptions[eth] = [ [optionParams], ..., ]
230+
allOptions[asset] = array;
231+
}
232+
233+
let allDeployements = {};
234+
// For each option object, parse its parameters, deploy it, and save it to options.json.
235+
for (let i = 0; i < Object.keys(allOptions).length; i++) {
236+
// Asset: e.g. 'eth'
237+
let asset = Object.keys(allOptions)[i];
238+
239+
// allOptions[eth]
240+
let assetOptions = allOptions[asset];
241+
242+
// For each of the options of the asset, deploy it using the parameters
243+
let optionJsonObject = {};
244+
for (let x = 0; x < assetOptions.length; x++) {
245+
let option = assetOptions[x];
246+
let underlyingToken = option[0];
247+
let strikeToken = option[1];
248+
let base = option[2];
249+
let quote = option[3];
250+
let expiry = option[4];
251+
let optionParametersObject = getOptionParametersObject(
252+
underlyingToken,
253+
strikeToken,
254+
base,
255+
quote,
256+
expiry
257+
);
258+
let underlyingSymbol = asset;
259+
260+
// Deploy the option
261+
let optionAddress = await deployOption(optionParametersObject);
262+
let symbol = await getOptionSymbol(
263+
underlyingSymbol,
264+
optionParametersObject
265+
);
266+
Object.assign(optionJsonObject, {
267+
[symbol]: {
268+
optionParameters: option,
269+
address: optionAddress,
270+
},
271+
});
272+
}
273+
274+
// allDeployments[eth] = { optionsObjects }
275+
Object.assign(allDeployements, {
276+
[asset]: optionJsonObject,
277+
});
278+
}
279+
280+
const path = "./scripts/json/option_mainnet_deployments.json";
281+
writeOptionJson(allDeployements, path);
282+
console.log(allDeployements.yfi);
283+
}
284+
285+
main()
286+
.then(() => process.exit(0))
287+
.catch((error) => {
288+
console.error(error);
289+
process.exit(1);
290+
});

0 commit comments

Comments
 (0)