mirror of
https://github.com/luxfi/uma.git
synced 2026-10-30 15:14:44 +00:00
322 lines
12 KiB
JavaScript
322 lines
12 KiB
JavaScript
const { web3, getContract } = require("hardhat");
|
|
const { assert } = require("chai");
|
|
|
|
const winston = require("winston");
|
|
|
|
const { toWei, utf8ToHex, padRight } = web3.utils;
|
|
|
|
const { FinancialContractFactoryClient } = require("../../dist/clients/FinancialContractFactoryClient");
|
|
const { interfaceName, advanceBlockAndSetTime, ZERO_ADDRESS, RegistryRolesEnum } = require("@uma/common");
|
|
|
|
const ExpiringMultiPartyCreator = getContract("ExpiringMultiPartyCreator");
|
|
const ExpiringMultiPartyLib = getContract("ExpiringMultiPartyLib");
|
|
const PerpetualCreator = getContract("PerpetualCreator");
|
|
const PerpetualLib = getContract("PerpetualLib");
|
|
const Finder = getContract("Finder");
|
|
const IdentifierWhitelist = getContract("IdentifierWhitelist");
|
|
const Token = getContract("ExpandedERC20");
|
|
const TokenFactory = getContract("TokenFactory");
|
|
const Timer = getContract("Timer");
|
|
const AddressWhitelist = getContract("AddressWhitelist");
|
|
const Registry = getContract("Registry");
|
|
|
|
describe("FinancialContractFactoryClient.js", function () {
|
|
let accounts;
|
|
let deployer;
|
|
|
|
// Contracts
|
|
let empFactory;
|
|
let perpFactory;
|
|
let finder;
|
|
let identifierWhitelist;
|
|
let collateralWhitelist;
|
|
let registry;
|
|
let collateral;
|
|
let empsCreated = [];
|
|
let perpsCreated = [];
|
|
|
|
// Bot helper modules
|
|
let perpClient, empClient;
|
|
let dummyLogger;
|
|
|
|
// Default testing values.
|
|
let defaultCreationParams = {
|
|
expirationTimestamp: "1950000000", // Fri Oct 17 2031 10:40:00 GMT+0000
|
|
priceFeedIdentifier: padRight(utf8ToHex("Test Identifier"), 64),
|
|
syntheticName: "Test Synth",
|
|
syntheticSymbol: "TEST-SYNTH",
|
|
collateralRequirement: { rawValue: toWei("1.2") },
|
|
disputeBondPercentage: { rawValue: toWei("0.1") },
|
|
sponsorDisputeRewardPercentage: { rawValue: toWei("0.05") },
|
|
disputerDisputeRewardPercentage: { rawValue: toWei("0.04") },
|
|
minSponsorTokens: { rawValue: toWei("10") },
|
|
withdrawalLiveness: "7200",
|
|
liquidationLiveness: "7300",
|
|
};
|
|
let defaultEmpCreationParams;
|
|
let defaultPerpCreationParams;
|
|
let configStoreParams = {
|
|
timelockLiveness: 86400, // 1 day
|
|
rewardRatePerSecond: { rawValue: toWei("0.000001") },
|
|
proposerBondPercentage: { rawValue: toWei("0.0001") },
|
|
maxFundingRate: { rawValue: toWei("0.00001") },
|
|
minFundingRate: { rawValue: toWei("-0.00001") },
|
|
proposalTimePastLimit: 1800,
|
|
};
|
|
|
|
const deployNewContract = async (type) => {
|
|
if (type === "PerpetualCreator") {
|
|
const perpAddress = await perpFactory.methods
|
|
.createPerpetual(defaultPerpCreationParams, configStoreParams)
|
|
.call({ from: deployer });
|
|
const perpCreation = await perpFactory.methods
|
|
.createPerpetual(defaultPerpCreationParams, configStoreParams)
|
|
.send({ from: deployer });
|
|
perpsCreated.push({ transaction: perpCreation, address: perpAddress });
|
|
} else {
|
|
const empAddress = await empFactory.methods
|
|
.createExpiringMultiParty(defaultEmpCreationParams)
|
|
.call({ from: deployer });
|
|
const empCreation = await empFactory.methods
|
|
.createExpiringMultiParty(defaultEmpCreationParams)
|
|
.send({ from: deployer });
|
|
empsCreated.push({ transaction: empCreation, address: empAddress });
|
|
}
|
|
};
|
|
|
|
before(async function () {
|
|
accounts = await web3.eth.getAccounts();
|
|
[deployer] = accounts;
|
|
finder = await Finder.new().send({ from: accounts[0] });
|
|
const timer = await Timer.new().send({ from: accounts[0] });
|
|
const tokenFactory = await TokenFactory.new().send({ from: accounts[0] });
|
|
|
|
// Deploy new factories and link libraries.
|
|
await ExpiringMultiPartyCreator.link({
|
|
ExpiringMultiPartyLib: (await ExpiringMultiPartyLib.new().send({ from: accounts[0] })).options.address,
|
|
});
|
|
await PerpetualCreator.link({
|
|
PerpetualLib: (await PerpetualLib.new().send({ from: accounts[0] })).options.address,
|
|
});
|
|
empFactory = await ExpiringMultiPartyCreator.new(
|
|
finder.options.address,
|
|
tokenFactory.options.address,
|
|
timer.options.address
|
|
).send({ from: accounts[0] });
|
|
perpFactory = await PerpetualCreator.new(
|
|
finder.options.address,
|
|
tokenFactory.options.address,
|
|
timer.options.address
|
|
).send({ from: accounts[0] });
|
|
|
|
// Whitelist an initial identifier so we can deploy.
|
|
identifierWhitelist = await IdentifierWhitelist.new().send({ from: accounts[0] });
|
|
await identifierWhitelist.methods
|
|
.addSupportedIdentifier(defaultCreationParams.priceFeedIdentifier)
|
|
.send({ from: accounts[0] });
|
|
await finder.methods
|
|
.changeImplementationAddress(utf8ToHex(interfaceName.IdentifierWhitelist), identifierWhitelist.options.address)
|
|
.send({ from: accounts[0] });
|
|
|
|
// Create and whitelist collateral so we can deploy.
|
|
collateralWhitelist = await AddressWhitelist.new().send({ from: accounts[0] });
|
|
collateral = await Token.new("Wrapped Ether", "WETH", 18).send({ from: accounts[0] });
|
|
await collateralWhitelist.methods.addToWhitelist(collateral.options.address).send({ from: accounts[0] });
|
|
await finder.methods
|
|
.changeImplementationAddress(utf8ToHex(interfaceName.CollateralWhitelist), collateralWhitelist.options.address)
|
|
.send({ from: accounts[0] });
|
|
|
|
// Add collateral to default param
|
|
defaultCreationParams = { ...defaultCreationParams, collateralAddress: collateral.options.address };
|
|
defaultEmpCreationParams = { ...defaultCreationParams, financialProductLibraryAddress: ZERO_ADDRESS };
|
|
defaultPerpCreationParams = {
|
|
...defaultCreationParams,
|
|
fundingRateIdentifier: padRight(utf8ToHex("Test Funding Rate Identifier"), 64),
|
|
tokenScaling: { rawValue: toWei("1") },
|
|
};
|
|
|
|
// Add Registry to finder so factories can register contracts.
|
|
registry = await Registry.new().send({ from: accounts[0] });
|
|
await registry.methods
|
|
.addMember(RegistryRolesEnum.CONTRACT_CREATOR, empFactory.options.address)
|
|
.send({ from: accounts[0] });
|
|
await registry.methods
|
|
.addMember(RegistryRolesEnum.CONTRACT_CREATOR, perpFactory.options.address)
|
|
.send({ from: accounts[0] });
|
|
await finder.methods
|
|
.changeImplementationAddress(utf8ToHex(interfaceName.Registry), registry.options.address)
|
|
.send({ from: accounts[0] });
|
|
|
|
// Create new financial contracts:
|
|
await deployNewContract("ExpiringMultiPartyCreator");
|
|
await deployNewContract("PerpetualCreator");
|
|
|
|
// The Event client does not emit any info `level` events. Therefore no need to test Winston outputs.
|
|
// DummyLogger will not print anything to console as only capture `info` level events.
|
|
dummyLogger = winston.createLogger({ level: "info", transports: [new winston.transports.Console()] });
|
|
});
|
|
it("createdExpiringMultiParty", async function () {
|
|
empClient = new FinancialContractFactoryClient(
|
|
dummyLogger,
|
|
ExpiringMultiPartyCreator.abi,
|
|
web3,
|
|
empFactory.options.address,
|
|
0, // startingBlockNumber
|
|
null, // endingBlockNumber
|
|
"ExpiringMultiPartyCreator"
|
|
);
|
|
|
|
await empClient.clearState();
|
|
|
|
// State is empty before update().
|
|
assert.deepStrictEqual([], empClient.getAllCreatedContractEvents());
|
|
assert.deepStrictEqual([], empClient.getAllCreatedContractAddresses());
|
|
|
|
await empClient.update();
|
|
assert.deepStrictEqual(
|
|
[
|
|
{
|
|
transactionHash: empsCreated[0].transaction.transactionHash,
|
|
blockNumber: empsCreated[0].transaction.blockNumber,
|
|
deployerAddress: deployer,
|
|
contractAddress: empsCreated[0].address,
|
|
},
|
|
],
|
|
empClient.getAllCreatedContractEvents()
|
|
);
|
|
assert.deepStrictEqual([empsCreated[0].address], empClient.getAllCreatedContractAddresses());
|
|
|
|
// Correctly adds only new events after last query
|
|
await deployNewContract("ExpiringMultiPartyCreator");
|
|
await empClient.clearState();
|
|
await empClient.update();
|
|
assert.deepStrictEqual(
|
|
[
|
|
{
|
|
transactionHash: empsCreated[1].transaction.transactionHash,
|
|
blockNumber: empsCreated[1].transaction.blockNumber,
|
|
deployerAddress: deployer,
|
|
contractAddress: empsCreated[1].address,
|
|
},
|
|
],
|
|
empClient.getAllCreatedContractEvents()
|
|
);
|
|
assert.deepStrictEqual([empsCreated[1].address], empClient.getAllCreatedContractAddresses());
|
|
});
|
|
it("createdPerpetual", async function () {
|
|
perpClient = new FinancialContractFactoryClient(
|
|
dummyLogger,
|
|
PerpetualCreator.abi,
|
|
web3,
|
|
perpFactory.options.address,
|
|
0, // startingBlockNumber
|
|
null, // endingBlockNumber
|
|
"PerpetualCreator"
|
|
);
|
|
|
|
await perpClient.clearState();
|
|
|
|
// State is empty before update().
|
|
assert.deepStrictEqual([], perpClient.getAllCreatedContractEvents());
|
|
assert.deepStrictEqual([], perpClient.getAllCreatedContractAddresses());
|
|
|
|
await perpClient.update();
|
|
assert.deepStrictEqual(
|
|
[
|
|
{
|
|
transactionHash: perpsCreated[0].transaction.transactionHash,
|
|
blockNumber: perpsCreated[0].transaction.blockNumber,
|
|
deployerAddress: deployer,
|
|
contractAddress: perpsCreated[0].address,
|
|
},
|
|
],
|
|
perpClient.getAllCreatedContractEvents()
|
|
);
|
|
assert.deepStrictEqual([perpsCreated[0].address], perpClient.getAllCreatedContractAddresses());
|
|
|
|
// Correctly adds only new events after last query
|
|
await deployNewContract("PerpetualCreator");
|
|
await perpClient.clearState();
|
|
await perpClient.update();
|
|
assert.deepStrictEqual(
|
|
[
|
|
{
|
|
transactionHash: perpsCreated[1].transaction.transactionHash,
|
|
blockNumber: perpsCreated[1].transaction.blockNumber,
|
|
deployerAddress: deployer,
|
|
contractAddress: perpsCreated[1].address,
|
|
},
|
|
],
|
|
perpClient.getAllCreatedContractEvents()
|
|
);
|
|
assert.deepStrictEqual([perpsCreated[1].address], perpClient.getAllCreatedContractAddresses());
|
|
});
|
|
it("Starting client at an offset block number", async function () {
|
|
// Init the event client with an offset block number. If the current block number is used then all log events
|
|
// generated before the creation of the client should not be included. Rather, only subsequent logs should be reported.
|
|
|
|
const currentBlockNumber = await web3.eth.getBlockNumber();
|
|
const offsetClient = new FinancialContractFactoryClient(
|
|
dummyLogger,
|
|
PerpetualCreator.abi,
|
|
web3,
|
|
perpFactory.options.address,
|
|
currentBlockNumber + 1, // Start the bot one block after the latest event
|
|
null, // endingBlockNumber
|
|
"PerpetualCreator"
|
|
);
|
|
const currentTimestamp = (await web3.eth.getBlock("latest")).timestamp;
|
|
await advanceBlockAndSetTime(web3, currentTimestamp + 1);
|
|
await advanceBlockAndSetTime(web3, currentTimestamp + 2);
|
|
await advanceBlockAndSetTime(web3, currentTimestamp + 3);
|
|
|
|
await offsetClient.update();
|
|
|
|
assert.deepStrictEqual([], offsetClient.getAllCreatedContractEvents());
|
|
assert.deepStrictEqual([], offsetClient.getAllCreatedContractAddresses());
|
|
});
|
|
it("Client correctly defaults to PerpetualCreator", async function () {
|
|
perpClient = new FinancialContractFactoryClient(
|
|
dummyLogger,
|
|
PerpetualCreator.abi,
|
|
web3,
|
|
perpFactory.options.address,
|
|
0,
|
|
null
|
|
);
|
|
assert.equal(perpClient.getContractType(), "PerpetualCreator");
|
|
});
|
|
it("Correctly rejects invalid contract types", async function () {
|
|
let didThrow = false;
|
|
try {
|
|
perpClient = new FinancialContractFactoryClient(
|
|
dummyLogger,
|
|
PerpetualCreator.abi,
|
|
web3,
|
|
perpFactory.options.address,
|
|
0,
|
|
null,
|
|
"ExpiringMultiParty"
|
|
);
|
|
} catch (error) {
|
|
didThrow = true;
|
|
}
|
|
assert.isTrue(didThrow);
|
|
didThrow = false;
|
|
try {
|
|
perpClient = new FinancialContractFactoryClient(
|
|
dummyLogger,
|
|
PerpetualCreator.abi,
|
|
web3,
|
|
perpFactory.options.address,
|
|
0,
|
|
null,
|
|
"Perpetual"
|
|
);
|
|
} catch (error) {
|
|
didThrow = true;
|
|
}
|
|
assert.isTrue(didThrow);
|
|
});
|
|
});
|