Skip to content

Instantly share code, notes, and snippets.

@WPSmartContracts
Created March 17, 2023 23:23
Show Gist options
  • Save WPSmartContracts/0b90a2f89a4b038473e4a9824423d561 to your computer and use it in GitHub Desktop.
Save WPSmartContracts/0b90a2f89a4b038473e4a9824423d561 to your computer and use it in GitHub Desktop.
const consoleLog = false;
const { BN, time } = require('@openzeppelin/test-helpers');
const { expect, assert } = require('chai');
const { expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = require('@openzeppelin/test-helpers/src/constants');
const ERC20Mock = artifacts.require('ERC20Mock');
const EVEOTC = artifacts.require('EVEOTC');
const EVEOTCLending = artifacts.require('EVEOTCOptionsLending');
const EVEOTCStakes = artifacts.require('EVEOTCStakes');
const MOCK_OTCChainLinkOracle = artifacts.require('MOCK_OTCChainLinkOracle');
const AggregatorV3Interface = artifacts.require('AggregatorV3InterfaceMockIncrease');
const AggregatorV3InterfaceDecrease = artifacts.require('AggregatorV3InterfaceMockDecrease');
let factory;
let account_master;
let account_customer;
function toleratedDiff(diff, expected_diff, tol) {
let rel;
diff = BigInt(diff.toString());
expected_diff = BigInt(expected_diff.toString());
if (diff==expected_diff) return true;
if (diff>expected_diff) {
rel = diff/expected_diff;
} else {
rel = expected_diff/diff;
}
if (!tol) tol = 1.001
return rel < tol;
}
contract('EVE OTC Options', function (accounts) {
const [ owner, seller, buyer, recipient, provider, provider2, provider3 ] = accounts;
const _com1 = new BN(10);
const _com2 = new BN(20);
const _com3 = new BN(30);
const _com4 = new BN(40);
account_master = accounts[0];
account_customer = accounts[1];
beforeEach(async function () {
await time.advanceBlock();
this.eve = await ERC20Mock.new("EVE", 'EVE', owner, web3.utils.toWei('1000000', 'ether'), 18, { from: owner });
this.eve2 = await ERC20Mock.new("EVE", 'EVE', owner, web3.utils.toWei('1000000', 'ether'), 18, { from: owner });
this.usdt = await ERC20Mock.new("USDT", 'USDT', owner, web3.utils.toWei('1000000', 'ether'), 6, { from: owner });
this.dai = await ERC20Mock.new("DAI", 'DAI', owner, web3.utils.toWei('1000000', 'ether'), 9, { from: owner });
// transfer funds to the recipient
await this.eve.transfer(seller, web3.utils.toWei('1000', 'ether'), { from: owner });
await this.eve2.transfer(seller, web3.utils.toWei('1000', 'ether'), { from: owner });
await this.usdt.transfer(buyer, "10000000000", { from: owner });
this.otc_option = await EVEOTCLending.new({ from: owner });
this.eve_exchange = await EVEOTC.new(owner, this.eve.address, { from: owner });
await this.otc_option.eveExchangeContractSet(this.eve_exchange.address, {from: owner});
this.otc_stake = await EVEOTCStakes.new(this.eve.address, owner, { from: owner });
await this.otc_option.stakingContractSet(this.otc_stake.address, { from: owner });
this.oracle = await MOCK_OTCChainLinkOracle.new({ from: owner });
this.aggregator = await AggregatorV3Interface.new(8, { from: owner });
this.aggregator2 = await AggregatorV3InterfaceDecrease.new(8, { from: owner });
// EVE increase price in time
await this.eve_exchange.addToken(this.eve.address, 0, this.aggregator.address, 0, {from: owner});
// EVE2 decrease price in time
await this.eve_exchange.addToken(this.eve2.address, 0, this.aggregator2.address, 0, {from: owner});
// stable coins in 1$
await this.eve_exchange.addToken(this.usdt.address, 0, ZERO_ADDRESS, "100000000", {from: owner});
await this.eve_exchange.addToken(this.dai.address, 0, ZERO_ADDRESS, "100000000", {from: owner});
});
describe('Lending', function () {
describe('canSettlePutWithLending', function () {
beforeEach(async function () {
await this.otc_option.lendingTokenSet(this.dai.address, {from: owner});
});
describe('when the pool have money', function () {
it('Check able to settle if prices decrease, should be true', async function () {
await this.otc_option.commissionSet("50", "50", "100", "1000", { from: owner });
// ADD EVE TO THE POOL
// add liquidity to the pool
let amount = "2000000000000000"; // 20000 DAI
await this.dai.transfer(provider, amount, { from: owner });
await this.dai.approve(this.otc_option.address, amount, { from: provider });
await this.otc_option.addLiquidity(amount, {from: provider});
// add stable coin
await this.otc_option.addStableCoin(this.usdt.address, {from: owner});
// add call option to sell 100 EVE @ 1$ each
const amount2sell = web3.utils.toWei('10000', 'ether');
let premium = "10000000000";
let strike = "10000000000";
await this.eve.approve(this.otc_option.address, amount2sell, {from: seller});
await this.eve.transfer(seller, amount2sell, { from: owner });
await this.otc_option.addOption(true, this.eve.address, premium, amount2sell, strike, 90*24*60*60, 0, {from: seller});
// pay premium
let premium_price_now = await this.otc_option.getPremiumPrice(0, 0);
await this.usdt.approve(this.otc_option.address, premium_price_now, {from: buyer});
await this.otc_option.payPremium(0, 0, {from: buyer});
expect(await this.otc_option.canSettleCallWithLending(0)).to.be.equal(true);
// should fail
await expectRevert(this.otc_option.settlePutWithLending(0, {from: buyer}), 'OTC: this is not a put option');
await this.otc_option.settleCallWithLending(0, {from: buyer});
// add liquidity to the pool
amount = "10000000000000"; // 10000 DAI
await this.dai.transfer(provider, amount, { from: owner });
await this.dai.approve(this.otc_option.address, amount, { from: provider });
await this.otc_option.addLiquidity(amount, {from: provider});
// add stable coin
await this.otc_option.addStableCoin(this.usdt.address, {from: owner});
// add call option to sell 100 EVE @ 1$ each
const amount2buy = web3.utils.toWei('1000', 'ether'); // 1000 EVE
premium = "10000000000"; // 100$ de premium
strike = "20000000000"; // 200 strike price
await this.otc_option.addStableCoin(this.usdt.address, {from: owner});
const amount2pay = await this.otc_option.estimatePutOption(this.eve.address, amount2buy, strike, 0); // 1000 USDT
await this.usdt.transfer(buyer, amount2pay, { from: owner });
await this.usdt.approve(this.otc_option.address, amount2pay, {from: buyer});
// add option
await this.otc_option.addOption(false, this.eve.address, premium, amount2buy, strike, 90*24*60*60, 0, {from: buyer});
// pay premium
premium_price_now = await this.otc_option.getPremiumPrice(1, 0);
await this.usdt.approve(this.otc_option.address, premium_price_now, {from: seller});
option = await this.otc_option.options(1);
expect(option.call).to.be.equal(false);
expect(option.seller).to.be.equal(ZERO_ADDRESS);
expect(option.token).to.be.equal(this.eve.address);
expect(option.premium).to.be.bignumber.equal(premium);
expect(option.amount).to.be.bignumber.equal(amount2buy);
expect(option.strike).to.be.bignumber.equal(strike);
expect(option.buyer).to.be.equal(buyer);
expect(option.premiumPaid).to.be.equal(false);
expect(option.optionPaid).to.be.equal(false);
expect(option.canceled).to.be.equal(false);
await this.usdt.transfer(seller, premium_price_now, { from: owner });
receipt = await this.otc_option.payPremium(1, 0, {from: seller});
option = await this.otc_option.options(1);
expect(option.call).to.be.equal(false);
expect(option.seller).to.be.equal(seller);
expect(option.token).to.be.equal(this.eve.address);
expect(option.premium).to.be.bignumber.equal(premium);
expect(option.amount).to.be.bignumber.equal(amount2buy);
expect(option.strike).to.be.bignumber.equal(strike);
expect(option.buyer).to.be.equal(buyer);
expect(option.premiumPaid).to.be.equal(true);
expect(option.optionPaid).to.be.equal(false);
expect(option.canceled).to.be.equal(false);
await expectRevert(this.otc_option.payPremium(1, 0, {from: seller}), 'OTC: the option is already paid');
expect(await this.otc_option.canSettlePutWithLending(1)).to.be.equal(true);
// should fail
await expectRevert(this.otc_option.settlePutWithLending(1, {from: buyer}), 'OTC: only the seller can settle the put option');
const buyer_before = await this.eve.balanceOf(buyer);
const owner_before = await this.usdt.balanceOf(owner);
const seller_before = await this.usdt.balanceOf(seller);
// succeed
receipt = await this.otc_option.settlePutWithLending(1, {from: seller});
const tokenPrice = receipt.logs[1].args.tokenPrice.toString();
const buyer_after = await this.eve.balanceOf(buyer);
const buyer_diff = BigInt(buyer_after) - BigInt(buyer_before);
const owner_after = await this.usdt.balanceOf(owner);
const owner_diff = BigInt(owner_after) - BigInt(owner_before);
const seller_after = await this.usdt.balanceOf(seller);
const seller_diff = BigInt(seller_after) - BigInt(seller_before);
option = await this.otc_option.options(1);
expect(option.call).to.be.equal(false);
expect(option.seller).to.be.equal(seller);
expect(option.token).to.be.equal(this.eve.address);
expect(option.premium).to.be.bignumber.equal(premium);
expect(option.amount).to.be.bignumber.equal(amount2buy);
expect(option.strike).to.be.bignumber.equal(strike);
expect(option.buyer).to.be.equal(buyer);
expect(option.premiumPaid).to.be.equal(true);
expect(option.optionPaid).to.be.equal(true);
expect(option.canceled).to.be.equal(false);
// Loan:
// a) Pool pays Bob 1000 EVE (valued at 200,000$). Bob = buyer
expect(buyer_diff.toString()).to.be.equal(amount2buy);
// strike: 20000000000
// token price: 14700000000
// profit: 1000 * (20000000000 - 14700000000) = 5300000000000 = 53000$
// b) Profit is: 55000
const total = BigInt(1000) * BigInt(strike) / BigInt(100); // 1000 adjust decimals to usdt
const profit = BigInt(1000) * (BigInt(strike) - BigInt(tokenPrice)) / BigInt(100);
const lent = total - profit;
expect(profit.toString()).to.be.equal("53000000000");
// c) Pool gets the lended 800$ plus 10% commission: 20$ = 820$
const poolCommission = profit / BigInt(10);
const poolTokensBalance = await this.otc_option.poolTokensBalance(this.usdt.address);
const contract_eve_balance = await this.usdt.balanceOf(this.otc_option.address);
const paid2contract = lent + poolCommission;
expect(poolTokensBalance.toString()).to.be.equal(paid2contract.toString());
expect(contract_eve_balance.toString()).to.be.equal(paid2contract.toString());
// d) EVE gets 1% commission: 550$
const eveCommission = profit / BigInt(100);
expect(owner_diff.toString()).to.be.equal(eveCommission.toString());
// e) Mike gets the rest: 1000 - 820 - 2 = 178$. Mike = seller
const seller_gets = total - paid2contract - eveCommission;
expect(seller_diff.toString()).to.be.equal(seller_gets.toString());
});
it('Check able to settle if prices increase, should be false', async function () {
// add liquidity to the pool
const amount = "1000000000000"; // 1000 DAI
await this.dai.transfer(provider, amount, { from: owner });
await this.dai.approve(this.otc_option.address, amount, { from: provider });
await this.otc_option.addLiquidity(amount, {from: provider});
// add stable coin
await this.otc_option.addStableCoin(this.usdt.address, {from: owner});
// add call option to sell 100 EVE @ 1$ each
const amount2buy = web3.utils.toWei('1000', 'ether'); // 1000 EVE
const premium = "10000000000"; // 100$ de premium
const strike = "100000000"; // 1$ strike price
await this.otc_option.addStableCoin(this.usdt.address, {from: owner});
const amount2pay = await this.otc_option.estimatePutOption(this.eve.address, amount2buy, strike, 0); // 1000 USDT
await this.usdt.approve(this.otc_option.address, amount2pay, {from: buyer});
// add option
await this.otc_option.addOption(false, this.eve.address, premium, amount2buy, strike, 90*24*60*60, 0, {from: buyer});
// pay premium
const premium_price_now = await this.otc_option.getPremiumPrice(0, 0);
await this.usdt.approve(this.otc_option.address, premium_price_now, {from: seller});
await this.usdt.transfer(seller, premium_price_now, {from: owner});
await this.otc_option.payPremium(0, 0, {from: seller});
expect(await this.otc_option.canSettlePutWithLending(0)).to.be.equal(false);
});
});
describe('when the pool have no money', function () {
it('Check if it is able to settle if prices decrease, should be false', async function () {
// add liquidity to the pool
amount = "10000000000000"; // 10000 DAI
await this.dai.transfer(provider, amount, { from: owner });
await this.dai.approve(this.otc_option.address, amount, { from: provider });
await this.otc_option.addLiquidity(amount, {from: provider});
// add stable coin
await this.otc_option.addStableCoin(this.usdt.address, {from: owner});
// add call option to sell 100 EVE @ 1$ each
const amount2buy = web3.utils.toWei('1000', 'ether'); // 1000 EVE
premium = "10000000000"; // 100$ de premium
strike = "20000000000"; // 200 strike price
await this.otc_option.addStableCoin(this.usdt.address, {from: owner});
const amount2pay = await this.otc_option.estimatePutOption(this.eve.address, amount2buy, strike, 0); // 1000 USDT
await this.usdt.transfer(buyer, amount2pay, { from: owner });
await this.usdt.approve(this.otc_option.address, amount2pay, {from: buyer});
// add option
await this.otc_option.addOption(false, this.eve.address, premium, amount2buy, strike, 90*24*60*60, 0, {from: buyer});
// pay premium
premium_price_now = await this.otc_option.getPremiumPrice(0, 0);
await this.usdt.approve(this.otc_option.address, premium_price_now, {from: seller});
await this.usdt.transfer(seller, premium_price_now, {from: owner});
await this.otc_option.payPremium(0, 0, {from: seller});
receipt = await this.otc_option.canSettlePutWithLending(0);
expect(await this.otc_option.canSettlePutWithLending(0)).to.be.equal(false);
});
});
});
describe('canSettlePutWithCash', function () {
beforeEach(async function () {
await this.otc_option.lendingTokenSet(this.dai.address, {from: owner});
});
describe('when the pool have money', function () {
it('should fail', async function () {
await this.otc_option.commissionSet("50", "50", "100", "1000", { from: owner });
// ADD EVE TO THE POOL
// add liquidity to the pool
let amount = "2000000000000000"; // 20000 DAI
await this.dai.transfer(provider, amount, { from: owner });
await this.dai.approve(this.otc_option.address, amount, { from: provider });
await this.otc_option.addLiquidity(amount, {from: provider});
// add stable coin
await this.otc_option.addStableCoin(this.usdt.address, {from: owner});
// add call option to sell 100 EVE @ 1$ each
const amount2sell = web3.utils.toWei('10000', 'ether');
let premium = "10000000000";
let strike = "10000000000";
await this.eve.approve(this.otc_option.address, amount2sell, {from: seller});
await this.eve.transfer(seller, amount2sell, { from: owner });
await this.otc_option.addOption(true, this.eve.address, premium, amount2sell, strike, 90*24*60*60, 0, {from: seller});
// pay premium
let premium_price_now = await this.otc_option.getPremiumPrice(0, 0);
await this.usdt.approve(this.otc_option.address, premium_price_now, {from: buyer});
await this.otc_option.payPremium(0, 0, {from: buyer});
expect(await this.otc_option.canSettleCallWithLending(0)).to.be.equal(true);
expect(await this.otc_option.canSettlePutWithCash(0)).to.be.equal(false);
});
it('Check able to settle if prices increase, should be false', async function () {
// add liquidity to the pool
const amount = "1000000000000"; // 1000 DAI
await this.dai.transfer(provider, amount, { from: owner });
await this.dai.approve(this.otc_option.address, amount, { from: provider });
await this.otc_option.addLiquidity(amount, {from: provider});
// add stable coin
await this.otc_option.addStableCoin(this.usdt.address, {from: owner});
// add call option to sell 100 EVE @ 1$ each
const amount2buy = web3.utils.toWei('1000', 'ether'); // 1000 EVE
const premium = "10000000000"; // 100$ de premium
const strike = "100000000"; // 1$ strike price
await this.otc_option.addStableCoin(this.usdt.address, {from: owner});
const amount2pay = await this.otc_option.estimatePutOption(this.eve.address, amount2buy, strike, 0); // 1000 USDT
await this.usdt.approve(this.otc_option.address, amount2pay, {from: buyer});
// add option
await this.otc_option.addOption(false, this.eve.address, premium, amount2buy, strike, 90*24*60*60, 0, {from: buyer});
// pay premium
const premium_price_now = await this.otc_option.getPremiumPrice(0, 0);
await this.usdt.approve(this.otc_option.address, premium_price_now, {from: seller});
await this.usdt.transfer(seller, premium_price_now, {from: owner});
await this.otc_option.payPremium(0, 0, {from: seller});
expect(await this.otc_option.canSettlePutWithLending(0)).to.be.equal(false);
expect(await this.otc_option.canSettlePutWithCash(0)).to.be.equal(false);
});
});
describe('when the pool have no money', function () {
it('Check if it is able to settle if prices decrease, should be true', async function () {
await this.otc_option.commissionSet("50", "50", "100", "1000", { from: owner });
// add stable coin
await this.otc_option.addStableCoin(this.usdt.address, {from: owner});
// add call option to sell 100 EVE @ 1$ each
const amount2buy = web3.utils.toWei('1000', 'ether'); // 1000 EVE
premium = "10000000000"; // 100$ de premium
strike = "40000000000"; // 400$ strike price
await this.otc_option.addStableCoin(this.usdt.address, {from: owner});
const amount2pay = await this.otc_option.estimatePutOption(this.eve.address, amount2buy, strike, 0); // 1000 USDT
await this.usdt.transfer(buyer, amount2pay, { from: owner });
await this.usdt.approve(this.otc_option.address, amount2pay, {from: buyer});
// add option
await this.otc_option.addOption(false, this.eve.address, premium, amount2buy, strike, 90*24*60*60, 0, {from: buyer});
// pay premium
premium_price_now = await this.otc_option.getPremiumPrice(0, 0);
await this.usdt.approve(this.otc_option.address, premium_price_now, {from: seller});
await this.usdt.transfer(seller, premium_price_now, {from: owner});
await this.otc_option.payPremium(0, 0, {from: seller});
receipt = await this.otc_option.canSettlePutWithLending(0);
expect(await this.otc_option.canSettlePutWithLending(0)).to.be.equal(false);
expect(await this.otc_option.canSettlePutWithCash(0)).to.be.equal(true);
const buyer_before = await this.usdt.balanceOf(buyer);
const owner_before = await this.usdt.balanceOf(owner);
const seller_before = await this.usdt.balanceOf(seller);
// succeed
receipt = await this.otc_option.settlePutWithCash(0, {from: seller});
const tokenPrice = receipt.logs[1].args.tokenPrice.toString(); // 30300000000
// console.log("tokenPrice", tokenPrice.toString());
const buyer_after = await this.usdt.balanceOf(buyer);
const buyer_diff = BigInt(buyer_after) - BigInt(buyer_before);
const owner_after = await this.usdt.balanceOf(owner);
const owner_diff = BigInt(owner_after) - BigInt(owner_before);
const seller_after = await this.usdt.balanceOf(seller);
const seller_diff = BigInt(seller_after) - BigInt(seller_before);
option = await this.otc_option.options(0);
expect(option.call).to.be.equal(false);
expect(option.seller).to.be.equal(seller);
expect(option.token).to.be.equal(this.eve.address);
expect(option.premium).to.be.bignumber.equal(premium);
expect(option.amount).to.be.bignumber.equal(amount2buy);
expect(option.strike).to.be.bignumber.equal(strike);
expect(option.buyer).to.be.equal(buyer);
expect(option.premiumPaid).to.be.equal(true);
expect(option.optionPaid).to.be.equal(true);
expect(option.canceled).to.be.equal(false);
// Pool refunds Bob: 800$
expect(buyer_diff.toString()).to.be.equal("303000000000"); // 1000 * 303 = 303000 in usdt: 303000000000
// EVE get 1$
expect(owner_diff.toString()).to.be.equal("970000000"); // profit: 1000 * (400 - 303) = 1000 * 97 = 97000 | 1% is: 970 in usdt: 970000000
// Mike gets 199$
expect(seller_diff.toString()).to.be.equal("96030000000"); // amount - refund - commission: 1000 * 400 - 303000 - 970 = 400000 - 303000 - 970 = 96030 in usdt: 96030000000
});
});
});
describe('canSettleCallWithLending', function () {
beforeEach(async function () {
await this.otc_option.lendingTokenSet(this.dai.address, {from: owner});
});
describe('when the pool have money', function () {
it('Check able to settle if prices increase, should be true', async function () {
// add liquidity to the pool
const amount = "10000000000000"; // 10000 DAI
await this.dai.transfer(provider, amount, { from: owner });
await this.dai.approve(this.otc_option.address, amount, { from: provider });
await this.otc_option.addLiquidity(amount, {from: provider});
// add stable coin
await this.otc_option.addStableCoin(this.usdt.address, {from: owner});
// add call option to sell 100 EVE @ 1$ each
const amount2sell = web3.utils.toWei('100', 'ether');
const premium = "100000000";
const strike = "9000000000";
await this.eve.approve(this.otc_option.address, amount2sell, {from: seller});
await this.otc_option.addOption(true, this.eve.address, premium, amount2sell, strike, 90*24*60*60, 0, {from: seller});
// pay premium
const premium_price_now = await this.otc_option.getPremiumPrice(0, 0);
await this.usdt.approve(this.otc_option.address, premium_price_now, {from: buyer});
await this.otc_option.payPremium(0, 0, {from: buyer});
expect(await this.otc_option.canSettleCallWithLending(0)).to.be.equal(true);
});
it('Check able to settle if prices decrease, should be false', async function () {
// add liquidity to the pool
const amount = "1000000000000"; // 1000 DAI
await this.dai.transfer(provider, amount, { from: owner });
await this.dai.approve(this.otc_option.address, amount, { from: provider });
await this.otc_option.addLiquidity(amount, {from: provider});
// add stable coin
await this.otc_option.addStableCoin(this.usdt.address, {from: owner});
// add call option to sell 100 EVE @ 1$ each
const amount2sell = web3.utils.toWei('100', 'ether');
const premium = "100000000";
const strike = "100000000000";
await this.eve2.approve(this.otc_option.address, amount2sell, {from: seller});
await this.otc_option.addOption(true, this.eve2.address, premium, amount2sell, strike, 90*24*60*60, 0, {from: seller});
// pay premium
const premium_price_now = await this.otc_option.getPremiumPrice(0, 0);
await this.usdt.approve(this.otc_option.address, premium_price_now, {from: buyer});
await this.otc_option.payPremium(0, 0, {from: buyer});
expect(await this.otc_option.canSettleCallWithLending(0)).to.be.equal(false);
});
});
describe('when the pool have no money', function () {
it('Check able to settle if prices increase, should be false', async function () {
// add liquidity to the pool
const amount = "1000000000000"; // 10000 DAI
await this.dai.transfer(provider, amount, { from: owner });
await this.dai.approve(this.otc_option.address, amount, { from: provider });
await this.otc_option.addLiquidity(amount, {from: provider});
// add stable coin
await this.otc_option.addStableCoin(this.usdt.address, {from: owner});
// add call option to sell 100 EVE @ 1$ each
const amount2sell = web3.utils.toWei('100', 'ether');
const premium = "100000000";
const strike = "9000000000";
await this.eve.approve(this.otc_option.address, amount2sell, {from: seller});
await this.otc_option.addOption(true, this.eve.address, premium, amount2sell, strike, 90*24*60*60, 0, {from: seller});
// pay premium
const premium_price_now = await this.otc_option.getPremiumPrice(0, 0);
await this.usdt.approve(this.otc_option.address, premium_price_now, {from: buyer});
await this.otc_option.payPremium(0, 0, {from: buyer});
expect(await this.otc_option.canSettleCallWithLending(0)).to.be.equal(false);
});
it('Check able to settle if prices decrease, should be false', async function () {
// add stable coin
await this.otc_option.addStableCoin(this.usdt.address, {from: owner});
// add call option to sell 100 EVE @ 1$ each
const amount2sell = web3.utils.toWei('100', 'ether');
const premium = "100000000";
const strike = "100000000000";
await this.eve2.approve(this.otc_option.address, amount2sell, {from: seller});
await this.otc_option.addOption(true, this.eve2.address, premium, amount2sell, strike, 90*24*60*60, 0, {from: seller});
// pay premium
const premium_price_now = await this.otc_option.getPremiumPrice(0, 0);
await this.usdt.approve(this.otc_option.address, premium_price_now, {from: buyer});
await this.otc_option.payPremium(0, 0, {from: buyer});
expect(await this.otc_option.canSettleCallWithLending(0)).to.be.equal(false);
});
});
});
describe('settleCallWithLending', function () {
beforeEach(async function () {
await this.otc_option.lendingTokenSet(this.dai.address, {from: owner});
await this.otc_option.commissionSet("50", "50", "100", "1000", { from: owner });
});
describe('when the pool have money', function () {
it('Check able to settle if prices increase, should be true', async function () {
// add liquidity to the pool
const amount = "20000000000000"; // 20000 DAI
await this.dai.transfer(provider, amount, { from: owner });
await this.dai.approve(this.otc_option.address, amount, { from: provider });
await this.otc_option.addLiquidity(amount, {from: provider});
// add stable coin
await this.otc_option.addStableCoin(this.usdt.address, {from: owner});
// add call option to sell 100 EVE @ 1$ each
const amount2sell = web3.utils.toWei('100', 'ether');
const premium = "10000000000";
const strike = "20000000000";
await this.eve.approve(this.otc_option.address, amount2sell, {from: seller});
await this.otc_option.addOption(true, this.eve.address, premium, amount2sell, strike, 90*24*60*60, 0, {from: seller});
// pay premium
const premium_price_now = await this.otc_option.getPremiumPrice(0, 0);
await this.usdt.approve(this.otc_option.address, premium_price_now, {from: buyer});
await this.otc_option.payPremium(0, 0, {from: buyer});
expect(await this.otc_option.canSettleCallWithLending(0)).to.be.equal(true);
option = await this.otc_option.options(0);
expect(option.call).to.be.equal(true);
expect(option.seller).to.be.equal(seller);
expect(option.token).to.be.equal(this.eve.address);
expect(option.premium).to.be.bignumber.equal(premium);
expect(option.amount).to.be.bignumber.equal(amount2sell);
expect(option.strike).to.be.bignumber.equal(strike);
expect(option.buyer).to.be.equal(buyer);
expect(option.premiumPaid).to.be.equal(true);
expect(option.optionPaid).to.be.equal(false);
expect(option.canceled).to.be.equal(false);
// should succeed
const seller_before = await this.dai.balanceOf(seller);
const eve_owner_before = await this.eve.balanceOf(owner);
const eve_buyer_before = await this.eve.balanceOf(buyer);
const receipt = await this.otc_option.settleCallWithLending(0, {from: buyer});
const seller_after = await this.dai.balanceOf(seller);
const seller_diff = seller_after - seller_before;
const eve_owner_after = await this.eve.balanceOf(owner);
const eve_owner_diff = eve_owner_after - eve_owner_before;
const eve_buyer_after = await this.eve.balanceOf(buyer);
const eve_buyer_diff = eve_buyer_after - eve_buyer_before;
//amount: 100 EVE
//strike: 200 $
//total to pay to seller: 20000 $
// console.log("seller diff should be 20000", seller_diff.toString());
assert.ok(toleratedDiff(seller_diff, "20000000000000"));
//tokenPrice 61700000000 617 $
const tokenPrice = receipt.logs[0].args.tokenPrice;
// console.log("tokenPrice should be 617", tokenPrice.toString());
assert.ok(toleratedDiff(tokenPrice, "61700000000"));
//lent 32,4149 EVE
//total value of trade: 61700 $
//profit: 41700 $
//profit in EVE: 67,585
// console.log("profit should be 67,585", receipt.logs[0].args.profit.toString())
assert.ok(toleratedDiff(receipt.logs[0].args.profit, "67585000000000000000"));
//eve commission: 0,67585
// console.log("eveCommission should be 0,67585", receipt.logs[0].args.eveCommission.toString())
// console.log("eveCommission commission", eve_owner_diff.toString())
assert.ok(toleratedDiff(receipt.logs[0].args.eveCommission, "675850000000000000"));
assert.ok(toleratedDiff(eve_owner_diff, "675850000000000000"));
//lent + 10% profit: 39,1734
//paid to buyer: 60,15075
// console.log("paidToBuyer should be 60,15075", receipt.logs[0].args.paidToBuyer.toString())
// console.log("eve buyer diff", eve_buyer_diff.toString())
assert.ok(toleratedDiff(receipt.logs[0].args.paidToBuyer, "60150750000000000000"));
assert.ok(toleratedDiff(eve_buyer_diff, "60150750000000000000"));
//profit 67585089141004862236
//addedToPool 39173419773095623987
// console.log("addedToPool should be 39,1734", receipt.logs[0].args.addedToPool.toString())
assert.ok(toleratedDiff(receipt.logs[0].args.addedToPool, "39173400000000000000"));
const poolTokensBalance = await this.otc_option.poolTokensBalance(this.eve.address);
const contract_eve_balance = await this.eve.balanceOf(this.otc_option.address);
// console.log("poolTokensBalance, should be 39,1734", poolTokensBalance.toString())
assert.ok(toleratedDiff(poolTokensBalance, "39173400000000000000"));
// console.log("contract_eve_balance", contract_eve_balance.toString())
assert.ok(toleratedDiff(contract_eve_balance, "39173400000000000000"));
//eveCommission 675850891410048622
//poolTokensBalance 39173419773095623987
//paidToBuyer 60150729335494327391
//----------------------------------------
// 100000000000000000000
const total = BigInt(receipt.logs[0].args.eveCommission) + BigInt(receipt.logs[0].args.addedToPool) + BigInt(receipt.logs[0].args.paidToBuyer);
// console.log("total should be 100000000000000000000", total.toString());
assert.ok(toleratedDiff(total, "100000000000000000000"));
option = await this.otc_option.options(0);
expect(option.call).to.be.equal(true);
expect(option.seller).to.be.equal(seller);
expect(option.token).to.be.equal(this.eve.address);
expect(option.premium).to.be.bignumber.equal(premium);
expect(option.amount).to.be.bignumber.equal(amount2sell);
expect(option.strike).to.be.bignumber.equal(strike);
expect(option.buyer).to.be.equal(buyer);
expect(option.premiumPaid).to.be.equal(true);
expect(option.optionPaid).to.be.equal(true);
expect(option.canceled).to.be.equal(false);
});
it('Check able to settle if prices decrease, should be false', async function () {
// add liquidity to the pool
const amount = "1000000000000"; // 1000 DAI
await this.dai.transfer(provider, amount, { from: owner });
await this.dai.approve(this.otc_option.address, amount, { from: provider });
await this.otc_option.addLiquidity(amount, {from: provider});
// add stable coin
await this.otc_option.addStableCoin(this.usdt.address, {from: owner});
// add call option to sell 100 EVE @ 1$ each
const amount2sell = web3.utils.toWei('100', 'ether');
const premium = "100000000";
const strike = "100000000000";
await this.eve2.approve(this.otc_option.address, amount2sell, {from: seller});
await this.otc_option.addOption(true, this.eve2.address, premium, amount2sell, strike, 90*24*60*60, 0, {from: seller});
// pay premium
const premium_price_now = await this.otc_option.getPremiumPrice(0, 0);
await this.usdt.approve(this.otc_option.address, premium_price_now, {from: buyer});
await this.otc_option.payPremium(0, 0, {from: buyer});
expect(await this.otc_option.canSettleCallWithLending(0)).to.be.equal(false);
await expectRevert(this.otc_option.settleCallWithLending(0, {from: buyer}), 'OTC: call option is not profitable enough');
});
});
describe('when the pool have no money', function () {
it('Check able to settle if prices increase, should be false', async function () {
// add liquidity to the pool
const amount = "1000000000000"; // 1000 DAI, not enough
await this.dai.transfer(provider, amount, { from: owner });
await this.dai.approve(this.otc_option.address, amount, { from: provider });
await this.otc_option.addLiquidity(amount, {from: provider});
// add stable coin
await this.otc_option.addStableCoin(this.usdt.address, {from: owner});
// add call option to sell 100 EVE @ 1$ each
const amount2sell = web3.utils.toWei('100', 'ether');
const premium = "100000000";
const strike = "9000000000";
await this.eve.approve(this.otc_option.address, amount2sell, {from: seller});
await this.otc_option.addOption(true, this.eve.address, premium, amount2sell, strike, 90*24*60*60, 0, {from: seller});
// pay premium
const premium_price_now = await this.otc_option.getPremiumPrice(0, 0);
await this.usdt.approve(this.otc_option.address, premium_price_now, {from: buyer});
await this.otc_option.payPremium(0, 0, {from: buyer});
expect(await this.otc_option.canSettleCallWithLending(0)).to.be.equal(false);
await expectRevert(this.otc_option.settleCallWithLending(0, {from: buyer}), 'OTC: not enough funds in the pool');
});
it('Check able to settle if prices decrease, should be false', async function () {
// add stable coin
await this.otc_option.addStableCoin(this.usdt.address, {from: owner});
// add call option to sell 100 EVE @ 1$ each
const amount2sell = web3.utils.toWei('100', 'ether');
const premium = "100000000";
const strike = "100000000000";
await this.eve2.approve(this.otc_option.address, amount2sell, {from: seller});
await this.otc_option.addOption(true, this.eve2.address, premium, amount2sell, strike, 90*24*60*60, 0, {from: seller});
// pay premium
const premium_price_now = await this.otc_option.getPremiumPrice(0, 0);
await this.usdt.approve(this.otc_option.address, premium_price_now, {from: buyer});
await this.otc_option.payPremium(0, 0, {from: buyer});
expect(await this.otc_option.canSettleCallWithLending(0)).to.be.equal(false);
await expectRevert(this.otc_option.settleCallWithLending(0, {from: buyer}), 'OTC: call option is not profitable enough');
});
});
});
});
describe('Basic data', function () {
it('commission', async function () {
await this.otc_option.commissionSet(_com1, _com2, _com3, _com4, { from: owner });
expect(await this.otc_option.commission_premium()).to.be.bignumber.equal(_com1);
expect(await this.otc_option.commission_sell()).to.be.bignumber.equal(_com2);
expect(await this.otc_option.commission_lending()).to.be.bignumber.equal(_com3);
expect(await this.otc_option.commission_pool()).to.be.bignumber.equal(_com4);
});
it('modify value', async function () {
await this.otc_option.commissionSet("1", "2", "3", "4", { from: owner });
expect(await this.otc_option.commission_premium()).to.be.bignumber.equal("1");
expect(await this.otc_option.commission_sell()).to.be.bignumber.equal("2");
expect(await this.otc_option.commission_lending()).to.be.bignumber.equal("3");
expect(await this.otc_option.commission_pool()).to.be.bignumber.equal("4");
});
it('Verify permission for modify with a non-admin user', async function () {
await expectRevert(this.otc_option.commissionSet("1", "2", "3", "4", {from: recipient}), 'Caller is not owner');
});
});
describe('Coins', function () {
it('Add', async function () {
await this.otc_option.addStableCoin(this.usdt.address, {from: owner});
expect(await this.otc_option.stableCoinsLength()).to.be.bignumber.equal("1");
expect(await this.otc_option.stable_coins(0)).to.be.equal(this.usdt.address);
await this.otc_option.addStableCoin(this.dai.address, {from: owner});
expect(await this.otc_option.stableCoinsLength()).to.be.bignumber.equal("2");
expect(await this.otc_option.stable_coins(1)).to.be.equal(this.dai.address);
});
it('Remove', async function () {
await this.otc_option.addStableCoin(this.usdt.address, {from: owner});
await this.otc_option.addStableCoin(this.dai.address, {from: owner});
await this.otc_option.removeStableCoin(0, {from: owner});
expect(await this.otc_option.stableCoinsLength()).to.be.bignumber.equal("1");
expect(await this.otc_option.stable_coins(0)).to.be.equal(this.dai.address);
});
it('addStableCoin: verify permission for modify with a non-admin user', async function () {
await expectRevert(this.otc_option.addStableCoin(this.usdt.address, { from: recipient }), 'Caller is not owner');
});
it('Remove: verify permission for modify with a non-admin user', async function () {
await this.otc_option.addStableCoin(this.usdt.address, {from: owner});
await expectRevert(this.otc_option.removeStableCoin(0, {from: recipient }), 'Caller is not owner');
});
});
describe('Flow', function () {
it('Call Option', async function () {
// initialization
const amount2sell = web3.utils.toWei('100', 'ether');
const premium = "10000000000";
const strike = "100000000";
await this.eve.approve(this.otc_option.address, amount2sell, {from: seller});
await this.otc_option.addStableCoin(this.usdt.address, {from: owner});
// add option
await this.otc_option.addOption(true, this.eve.address, premium, amount2sell, strike, 90*24*60*60, 0, {from: seller});
// verify option
expect(await this.otc_option.options_length()).to.be.bignumber.equal("1");
let option = await this.otc_option.options(0);
expect(option.call).to.be.equal(true);
expect(option.seller).to.be.equal(seller);
expect(option.token).to.be.equal(this.eve.address);
expect(option.premium).to.be.bignumber.equal(premium);
expect(option.amount).to.be.bignumber.equal(amount2sell);
expect(option.strike).to.be.bignumber.equal(strike);
expect(option.buyer).to.be.equal(ZERO_ADDRESS);
expect(option.premiumPaid).to.be.equal(false);
expect(option.optionPaid).to.be.equal(false);
expect(option.canceled).to.be.equal(false);
// no token used yet for settling
expect(await this.otc_option.settleTokens(0)).to.be.equal(ZERO_ADDRESS);
// check contract balance
expect(await this.eve.balanceOf(this.otc_option.address)).to.be.bignumber.equal(amount2sell);
// pay premium
const premium_price_now = await this.otc_option.getPremiumPrice(0, 0);
await this.usdt.approve(this.otc_option.address, premium_price_now, {from: buyer});
await this.otc_option.payPremium(0, 0, {from: buyer});
option = await this.otc_option.options(0);
expect(option.call).to.be.equal(true);
expect(option.seller).to.be.equal(seller);
expect(option.token).to.be.equal(this.eve.address);
expect(option.premium).to.be.bignumber.equal(premium);
expect(option.amount).to.be.bignumber.equal(amount2sell);
expect(option.strike).to.be.bignumber.equal(strike);
expect(option.buyer).to.be.equal(buyer);
expect(option.premiumPaid).to.be.equal(true);
expect(option.optionPaid).to.be.equal(false);
expect(option.canceled).to.be.equal(false);
// no token used yet for settling
expect(await this.otc_option.settleTokens(0)).to.be.equal(ZERO_ADDRESS);
let balance = await this.usdt.balanceOf(seller);
assert.ok(toleratedDiff(balance, premium_price_now));
const settle = await this.otc_option.getSettleAmount(0, this.usdt.address);
await this.usdt.approve(this.otc_option.address, settle[0] + settle[1], {from: buyer});
await this.otc_option.settleCall(0, 0, {from: buyer});
option = await this.otc_option.options(0);
expect(option.call).to.be.equal(true);
expect(option.seller).to.be.equal(seller);
expect(option.token).to.be.equal(this.eve.address);
expect(option.premium).to.be.bignumber.equal(premium);
expect(option.amount).to.be.bignumber.equal(amount2sell);
expect(option.strike).to.be.bignumber.equal(strike);
expect(option.buyer).to.be.equal(buyer);
expect(option.premiumPaid).to.be.equal(true);
expect(option.optionPaid).to.be.equal(true);
expect(option.canceled).to.be.equal(false);
// usdt used to settle
expect(await this.otc_option.settleTokens(0)).to.be.equal(this.usdt.address);
});
it('Put Option', async function () {
// initialization
// Bob wants to buy 1000 EVE at $1 each with a Premium of $100
const amount2buy = web3.utils.toWei('1000', 'ether'); // 1000 EVE
const premium = "10000000000"; // 100$ de premium
const strike = "100000000"; // 1$ strike price
await this.otc_option.addStableCoin(this.usdt.address, {from: owner});
const amount2pay = await this.otc_option.estimatePutOption(this.eve.address, amount2buy, strike, 0); // 1000 USDT
await this.usdt.approve(this.otc_option.address, amount2pay, {from: buyer});
// add option
await this.otc_option.addOption(false, this.eve.address, premium, amount2buy, strike, 90*24*60*60, 0, {from: buyer});
// verify option
expect(await this.otc_option.options_length()).to.be.bignumber.equal("1");
let option = await this.otc_option.options(0);
expect(option.call).to.be.equal(false);
expect(option.seller).to.be.equal(ZERO_ADDRESS);
expect(option.token).to.be.equal(this.eve.address);
expect(option.premium).to.be.bignumber.equal(premium);
expect(option.amount).to.be.bignumber.equal(amount2buy);
expect(option.strike).to.be.bignumber.equal(strike);
expect(option.buyer).to.be.equal(buyer);
expect(option.premiumPaid).to.be.equal(false);
expect(option.optionPaid).to.be.equal(false);
expect(option.canceled).to.be.equal(false);
// Settling token is USDT
expect(await this.otc_option.settleTokens(0)).to.be.equal(this.usdt.address);
// check contract balance
expect(await this.usdt.balanceOf(this.otc_option.address)).to.be.bignumber.equal(amount2pay);
// pay premium
const premium_price_now = await this.otc_option.getPremiumPrice(0, 0);
await this.usdt.transfer(seller, premium_price_now, { from: owner });
await this.usdt.approve(this.otc_option.address, premium_price_now, {from: seller});
let balance_before = await this.usdt.balanceOf(buyer);
await this.otc_option.payPremium(0, 0, {from: seller});
option = await this.otc_option.options(0);
expect(option.call).to.be.equal(false);
expect(option.seller).to.be.equal(seller);
expect(option.token).to.be.equal(this.eve.address);
expect(option.premium).to.be.bignumber.equal(premium);
expect(option.amount).to.be.bignumber.equal(amount2buy);
expect(option.strike).to.be.bignumber.equal(strike);
expect(option.buyer).to.be.equal(buyer);
expect(option.premiumPaid).to.be.equal(true);
expect(option.optionPaid).to.be.equal(false);
expect(option.canceled).to.be.equal(false);
// Settling token is USDT
expect(await this.otc_option.settleTokens(0)).to.be.equal(this.usdt.address);
let balance = await this.usdt.balanceOf(buyer);
let diff = balance - balance_before;
assert.ok(toleratedDiff(diff, premium_price_now));
await this.eve.approve(this.otc_option.address, amount2buy, {from: seller});
await this.otc_option.settlePut(0, {from: seller});
option = await this.otc_option.options(0);
expect(option.call).to.be.equal(false);
expect(option.seller).to.be.equal(seller);
expect(option.token).to.be.equal(this.eve.address);
expect(option.premium).to.be.bignumber.equal(premium);
expect(option.amount).to.be.bignumber.equal(amount2buy);
expect(option.strike).to.be.bignumber.equal(strike);
expect(option.buyer).to.be.equal(buyer);
expect(option.premiumPaid).to.be.equal(true);
expect(option.optionPaid).to.be.equal(true);
expect(option.canceled).to.be.equal(false);
// usdt used to settle
expect(await this.otc_option.settleTokens(0)).to.be.equal(this.usdt.address);
});
});
describe('Liquidity', function () {
describe('Add / remove lending tokens', function () {
it('Add', async function () {
await this.otc_option.addLendingPaymentToken(this.usdt.address, {from: owner});
expect(await this.otc_option.lendingPaymentTokenLength()).to.be.bignumber.equal("1");
expect(await this.otc_option.lendingPaymentToken(0)).to.be.equal(this.usdt.address);
await this.otc_option.addLendingPaymentToken(this.dai.address, {from: owner});
expect(await this.otc_option.lendingPaymentTokenLength()).to.be.bignumber.equal("2");
expect(await this.otc_option.lendingPaymentToken(1)).to.be.equal(this.dai.address);
});
it('Remove', async function () {
await this.otc_option.addLendingPaymentToken(this.usdt.address, {from: owner});
await this.otc_option.addLendingPaymentToken(this.dai.address, {from: owner});
await this.otc_option.removeLendingPaymentToken(0, {from: owner});
expect(await this.otc_option.lendingPaymentTokenLength()).to.be.bignumber.equal("1");
expect(await this.otc_option.lendingPaymentToken(0)).to.be.equal(this.dai.address);
});
it('addLendingPaymentToken: verify permission for modify with a non-admin user', async function () {
await expectRevert(this.otc_option.addLendingPaymentToken(this.usdt.address, { from: recipient }), 'Caller is not owner');
});
it('Remove: verify permission for modify with a non-admin user', async function () {
await this.otc_option.addLendingPaymentToken(this.usdt.address, {from: owner});
await expectRevert(this.otc_option.removeLendingPaymentToken(0, {from: recipient }), 'Caller is not owner');
});
});
describe('Add / remove liquidity', function () {
beforeEach(async function () {
await this.otc_option.lendingTokenSet(this.usdt.address, {from: owner});
});
it('non-admin user', async function () {
await expectRevert(this.otc_option.lendingTokenSet(this.usdt.address, {from: recipient}), 'Caller is not owner');
});
it('Add', async function () {
const amount = "10000000";
await this.usdt.transfer(provider, amount, { from: owner });
await this.usdt.approve(this.otc_option.address, amount, { from: provider });
await this.otc_option.addLiquidity(amount, {from: provider});
expect(await this.usdt.balanceOf(this.otc_option.address)).to.be.bignumber.equal(amount);
});
it('Add / remove', async function () {
const amount = "10000000";
const amount2 = "20000000";
const amount3 = "30000000";
await this.usdt.transfer(provider, amount, { from: owner });
await this.usdt.transfer(provider2, amount2, { from: owner });
await this.usdt.transfer(provider3, amount3, { from: owner });
await this.usdt.approve(this.otc_option.address, amount, { from: provider });
await this.usdt.approve(this.otc_option.address, amount2, { from: provider2 });
await this.usdt.approve(this.otc_option.address, amount3, { from: provider3 });
await this.otc_option.addLiquidity(amount, {from: provider});
await this.otc_option.addLiquidity(amount2, {from: provider2});
await this.otc_option.addLiquidity(amount3, {from: provider3});
expect(await this.usdt.balanceOf(this.otc_option.address)).to.be.bignumber.equal("60000000");
await this.otc_option.withdraw({from: provider2});
expect(await this.usdt.balanceOf(this.otc_option.address)).to.be.bignumber.equal("40000000");
await this.otc_option.withdraw({from: provider3});
expect(await this.usdt.balanceOf(this.otc_option.address)).to.be.bignumber.equal("10000000");
await this.otc_option.withdraw({from: provider});
expect(await this.usdt.balanceOf(this.otc_option.address)).to.be.bignumber.equal("0");
});
});
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment