diff --git a/README.md b/README.md
index f6ba842e..a90d9321 100644
--- a/README.md
+++ b/README.md
@@ -17,4 +17,4 @@
In `/packages/protcol`:
* Increment the version number in the cannonfiles
* Run `pnpm simulate-deploy:sepolia` to verify there are no issues
-* Run `CANNON_PRIVATE_KEY=x CANNON_PROVIDER_URL=y pnpm deploy sepolia` (or set those values in a `.env` file)
\ No newline at end of file
+* Run `CANNON_PRIVATE_KEY=x CANNON_PROVIDER_URL=y pnpm deploy:sepolia` (or set those values in a `.env` file)
\ No newline at end of file
diff --git a/packages/app/src/lib/components/chart.tsx b/packages/app/src/lib/components/chart.tsx
index 8c2bf188..56cda198 100644
--- a/packages/app/src/lib/components/chart.tsx
+++ b/packages/app/src/lib/components/chart.tsx
@@ -1,11 +1,10 @@
import type React from 'react';
-import { useContext } from 'react';
+import { useContext, useEffect, useRef, useState } from 'react';
import {
ResponsiveContainer,
XAxis,
YAxis,
CartesianGrid,
- Tooltip,
ComposedChart,
Bar,
ReferenceLine,
@@ -16,38 +15,51 @@ import { colors } from '~/lib/styles/theme/colors';
const CustomBarShape = ({
x,
- y,
width,
payload,
- yAxis,
+ yAxisDomain,
+ chartHeight,
+ gridOffsetFromParent,
}: {
- x: any;
- y: any;
- width: any;
+ x: number;
+ width: number;
payload: any;
- yAxis: any;
+ yAxisDomain: any;
+ chartHeight: number;
+ gridOffsetFromParent: number;
}) => {
const candleColor =
- colors.green &&
- colors.red &&
- (payload.open < payload.close ? colors.green[400] : colors.red[500]);
- const barHeight = Math.abs(yAxis(payload.open) - yAxis(payload.close));
- const wickHeight = Math.abs(yAxis(payload.low) - yAxis(payload.high));
- const wickY = Math.min(yAxis(payload.low), yAxis(payload.high));
- const barY = Math.min(yAxis(payload.open), yAxis(payload.close));
+ payload.open < payload.close
+ ? colors.green?.[400] ?? '#00FF00'
+ : colors.red?.[500] ?? '#FF0000';
+
+ const scaleY = (value: number) => {
+ const scaled = (value - yAxisDomain[0]) / (yAxisDomain[1] - yAxisDomain[0]);
+ return chartHeight - scaled * chartHeight + gridOffsetFromParent;
+ };
+
+ const lowY = scaleY(payload.low);
+ const highY = scaleY(payload.high);
+ const openY = scaleY(payload.open);
+ const closeY = scaleY(payload.close);
+
+ const barHeight = Math.abs(openY - closeY);
+ const wickHeight = Math.abs(lowY - highY);
return (
<>
+ {/* Wick */}
+ {/* Body */}
{
+ const grayColor = colors.gray?.[800] ?? '#808080';
+
const { averagePrice, prices } = useContext(MarketContext);
- console.log('prices:', prices);
+
+ const yAxisDomain = [
+ Math.min(...prices.map((p) => p.low)),
+ Math.max(...prices.map((p) => p.high)),
+ ];
+
+ const chartRef = useRef(null);
+ const [gridHeight, setGridHeight] = useState(0);
+ const [gridOffsetFromParent, setGridOffsetFromParent] = useState(0);
+
+ useEffect(() => {
+ if (chartRef.current) {
+ // Access the parent container and the CartesianGrid's bounding boxes
+ const parentElement = (chartRef.current as any).container;
+ const gridElement = parentElement.querySelector(
+ '.recharts-cartesian-grid'
+ );
+
+ if (gridElement && parentElement) {
+ const gridRect = gridElement.getBoundingClientRect();
+ const parentRect = parentElement.getBoundingClientRect();
+
+ // Calculate the height of the CartesianGrid
+ setGridHeight(gridRect.height);
+
+ // Calculate the offset from the top of the parent container
+ setGridOffsetFromParent(gridRect.top - parentRect.top);
+ }
+ }
+ }, [prices]);
return (
-
-
+
+
- p.high))]}
- tickFormatter={(value) => (value / 10e8).toFixed(2)}
- />
- ((value as number) / 10e8).toFixed(2)} />
+
(
- (d / 10e8) * (400 / 15)}
- />
- )}
+ dataKey="candles"
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, react/no-unstable-nested-components
+ shape={(props: any) => {
+ return (
+
+ );
+ }}
/>
diff --git a/packages/app/src/lib/layout/Header.tsx b/packages/app/src/lib/layout/Header.tsx
index cff8f2f4..11b2db62 100644
--- a/packages/app/src/lib/layout/Header.tsx
+++ b/packages/app/src/lib/layout/Header.tsx
@@ -2,11 +2,12 @@
import { Box, Flex, Image } from '@chakra-ui/react';
import Link from 'next/link';
-import FoilTestnet from '@/protocol/deployments/11155111/Foil.json';
-import FoilLocal from '@/protocol/deployments/13370/Foil.json';
import ConnectButton from '../components/ConnectButton';
+import FoilTestnet from '@/protocol/deployments/11155111/Foil.json';
+import FoilLocal from '@/protocol/deployments/13370/Foil.json';
+
const Header = () => {
return (
{
{/* Subscribe */}
{/* Earn */}
-
- Local Market
-
+ {process.env.NODE_ENV === 'development' && (
+
+ Local Market
+
+ )}
Testnet Market
diff --git a/packages/data/src/processes/market.ts b/packages/data/src/processes/market.ts
index ff2d2959..245a891b 100644
--- a/packages/data/src/processes/market.ts
+++ b/packages/data/src/processes/market.ts
@@ -21,6 +21,7 @@ export const indexMarketEvents = async (publicClient: PublicClient, Foil: { addr
const processLogs = async (logs: Log[]) => {
for (const log of logs) {
const serializedLog = JSON.stringify(log, bigintReplacer);
+ // TODO: Make this upsert!
const event = eventRepository.create({
logData: JSON.parse(serializedLog), // Parse back to JSON object
contractId: `${chainId}:${Foil.address}`,
@@ -60,6 +61,7 @@ export const indexMarketEventsRange = async (publicClient: PublicClient, start:
topics: log.topics,
});
const serializedLog = JSON.stringify(decodedLog, bigintReplacer);
+ // TODO: Make this upsert!
const event = eventRepository.create({
logData: JSON.parse(serializedLog), // Parse back to JSON object
contractId: `${await publicClient.getChainId()}:${contractAddress}`,
diff --git a/packages/data/src/service.ts b/packages/data/src/service.ts
index 41a39dac..38b8133f 100644
--- a/packages/data/src/service.ts
+++ b/packages/data/src/service.ts
@@ -85,7 +85,7 @@ createConnection(connectionOptions).then(async connection => {
});
// Get average price over a specified time period filtered by contractId
- app.get('/prices/average', async (req, res) => {
+app.get('/prices/average', async (req, res) => {
const { startTimestamp, endTimestamp, contractId } = req.query;
const where: any = {};
@@ -160,6 +160,7 @@ createConnection(connectionOptions).then(async connection => {
res.json(chartData);
});
+
app.get('/positions', async (req, res) => {
const { contractId, isLP } = req.query;
const where: any = {};
diff --git a/packages/data/src/worker.ts b/packages/data/src/worker.ts
index 9ab96238..650846f9 100644
--- a/packages/data/src/worker.ts
+++ b/packages/data/src/worker.ts
@@ -57,15 +57,15 @@ async function createViemPublicClient(providerUrl: string) {
}
async function indexBaseFeePerGasRangeCommand(startBlock: number, endBlock: number, rpcUrl: string, contractAddress: string) {
-console.log(`Indexing base fee per gas from block ${startBlock} to ${endBlock} for contract ${contractAddress} using ${rpcUrl}`);
-const client = await createViemPublicClient(rpcUrl);
-await indexBaseFeePerGasRange(client, startBlock, endBlock, contractAddress);
+ console.log(`Indexing base fee per gas from block ${startBlock} to ${endBlock} for contract ${contractAddress} using ${rpcUrl}`);
+ const client = await createViemPublicClient(rpcUrl);
+ await indexBaseFeePerGasRange(client, startBlock, endBlock, contractAddress);
}
async function indexMarketEventsRangeCommand(startBlock: number, endBlock: number, rpcUrl: string, contractAddress: string, contractAbi: Abi) {
-console.log(`Indexing market events from block ${startBlock} to ${endBlock} for contract ${contractAddress} using ${rpcUrl}`);
-const client = await createViemPublicClient(rpcUrl);
-await indexMarketEventsRange(client, startBlock, endBlock, contractAddress, contractAbi);
+ console.log(`Indexing market events from block ${startBlock} to ${endBlock} for contract ${contractAddress} using ${rpcUrl}`);
+ const client = await createViemPublicClient(rpcUrl);
+ await indexMarketEventsRange(client, startBlock, endBlock, contractAddress, contractAbi);
}
if(process.argv.length < 3) {
@@ -80,19 +80,9 @@ if(process.argv.length < 3) {
} else {
const args = process.argv.slice(2);
if (args[0] === 'index-sepolia') {
- // Index mainnet gas to sepolia contract
- indexBaseFeePerGasRangeCommand(20413376, 20428947, 'https://ethereum-rpc.publicnode.com', `${sepolia.id}:${FoilSepolia.address}`)
- //indexMarketEventsRangeCommand(1722270000, 1722458027, 'https://ethereum-rpc.publicnode.com', FoilSepolia.address, FoilSepolia.abi as Abi)
- } else if (args[0] === 'index-base-fee-per-gas') {
- const [start, end, rpcUrl, contractAddress] = args.slice(1);
- indexBaseFeePerGasRangeCommand(Number(start), Number(end), rpcUrl, contractAddress)
- .then(() => console.log('Indexing completed successfully'))
- .catch(error => console.error('Error indexing base fee per gas range:', error));
- } else if (args[0] === 'index-market-events') {
- const [start, end, rpcUrl, contractAddress, contractAbiPath] = args.slice(1);
- const contractAbi = require(contractAbiPath) as Abi; // Assuming the ABI is provided as a JSON file path
- indexMarketEventsRangeCommand(Number(start), Number(end), rpcUrl, contractAddress, contractAbi)
- .then(() => console.log('Indexing completed successfully'))
- .catch(error => console.error('Error indexing market events range:', error));
+ Promise.all([
+ indexBaseFeePerGasRangeCommand(20413376, 20428947, 'https://ethereum-rpc.publicnode.com', `${sepolia.id}:${FoilSepolia.address}`),
+ indexMarketEventsRangeCommand(1722270000, 1722458027, 'https://ethereum-sepolia-rpc.publicnode.com', FoilSepolia.address, FoilSepolia.abi as Abi)
+ ])
}
}
\ No newline at end of file
diff --git a/packages/protocol/cannonfile.sepolia.toml b/packages/protocol/cannonfile.sepolia.toml
index f150ae99..c856a07d 100644
--- a/packages/protocol/cannonfile.sepolia.toml
+++ b/packages/protocol/cannonfile.sepolia.toml
@@ -1,10 +1,10 @@
name="foil"
-version="0.5"
+version="0.6"
[var.settings]
owner="0xE006B58cA5aB7ba53863012dc3067A14b965C1da"
startTime = "1722270000" # Mon Jul 29 2024 16:20:00 GMT+0000
-endTime = "1722458027" # Wed Jul 31 2024 20:33:47 GMT+0000
+endTime = "1725195600" # Sunday, September 1, 2024 9:00:00 AM GMT-04:00
baseAssetMinPriceTick = "5200" # 1.709
baseAssetMaxPriceTick = "28200" # 17.09
feeRate = "10000" # 1%
diff --git a/packages/protocol/cannonfile.toml b/packages/protocol/cannonfile.toml
index 4d540221..ef5d818a 100644
--- a/packages/protocol/cannonfile.toml
+++ b/packages/protocol/cannonfile.toml
@@ -1,10 +1,10 @@
name="foil"
-version="0.5"
+version="0.6"
[var.settings]
owner = "0xEB045D78d273107348b0300c01D29B7552D622ab"
startTime = "1722270000" # Mon Jul 29 2024 16:20:00 GMT+0000
-endTime = "1725900163" # Fri, 09 Sep 2024 16:42:43 GMT+0000
+endTime = "1725195600" # Sunday, September 1, 2024 9:00:00 AM GMT-04:00
baseAssetMinPriceTick = "5200" # 1.709
baseAssetMaxPriceTick = "28200" # 17.09
feeRate = "10000" # 1%
diff --git a/packages/protocol/deployments/11155111/EpochConfigurationModule.json b/packages/protocol/deployments/11155111/EpochConfigurationModule.json
index 743dd7da..e5c004be 100644
--- a/packages/protocol/deployments/11155111/EpochConfigurationModule.json
+++ b/packages/protocol/deployments/11155111/EpochConfigurationModule.json
@@ -1,5 +1,5 @@
{
- "address": "0x233Fdf7aeD41DADcf21E684048649e84769807C5",
+ "address": "0x35f49BA660F4A65A3e411D6Ee9C97332b9Fb5Ac9",
"abi": [
{
"type": "function",
@@ -233,12 +233,12 @@
],
"constructorArgs": [],
"linkedLibraries": {},
- "deployTxnHash": "0x8c5d6ed2010e2a75000260198e7cb0b009bd1cf3770097874ba764674d995f2b",
- "deployTxnBlockNumber": "6474235",
- "deployTimestamp": "1723307100",
+ "deployTxnHash": "0x784d70f38d9e76198c86767878647a4b7d7aeff268cbce8cb810bafe1d867ed1",
+ "deployTxnBlockNumber": "6506105",
+ "deployTimestamp": "1723741740",
"sourceName": "src/contracts/modules/EpochConfigurationModule.sol",
"contractName": "EpochConfigurationModule",
"deployedOn": "deploy.EpochConfigurationModule",
"gasUsed": 2016847,
- "gasCost": "1001154682"
+ "gasCost": "27304630816"
}
\ No newline at end of file
diff --git a/packages/protocol/deployments/11155111/EpochLiquidityModule.json b/packages/protocol/deployments/11155111/EpochLiquidityModule.json
index f9e558a1..c2e85348 100644
--- a/packages/protocol/deployments/11155111/EpochLiquidityModule.json
+++ b/packages/protocol/deployments/11155111/EpochLiquidityModule.json
@@ -1,5 +1,5 @@
{
- "address": "0x4AB67EE0d21fE2816EBDCD2286c21e0728BF5a0A",
+ "address": "0xBFA7450ef1EE0De783d212F7d5566C8Db4C15F72",
"abi": [
{
"type": "function",
@@ -486,12 +486,12 @@
],
"constructorArgs": [],
"linkedLibraries": {},
- "deployTxnHash": "0x4dbcd6e7c95fe5ed1720baf9be28161605470f785c05c0a9484235b276a39bfa",
- "deployTxnBlockNumber": "6474229",
- "deployTimestamp": "1723307028",
+ "deployTxnHash": "0x49f297cafe18548d5c0431de649a9d9220750eb9390d876626bc040a27a6aa58",
+ "deployTxnBlockNumber": "6506098",
+ "deployTimestamp": "1723741644",
"sourceName": "src/contracts/modules/EpochLiquidityModule.sol",
"contractName": "EpochLiquidityModule",
"deployedOn": "deploy.EpochLiquidityModule",
- "gasUsed": 2082120,
- "gasCost": "1071720725"
+ "gasUsed": 2068518,
+ "gasCost": "37401849570"
}
\ No newline at end of file
diff --git a/packages/protocol/deployments/11155111/EpochNftModule.json b/packages/protocol/deployments/11155111/EpochNftModule.json
index 7081d96b..9a5575cf 100644
--- a/packages/protocol/deployments/11155111/EpochNftModule.json
+++ b/packages/protocol/deployments/11155111/EpochNftModule.json
@@ -1,5 +1,5 @@
{
- "address": "0xde6d24F43752BA9C4bd63fc9942d4384f3af00ac",
+ "address": "0xF1358121F4e1869821839ec04B50624077d4b6D7",
"abi": [
{
"type": "constructor",
@@ -463,12 +463,12 @@
],
"constructorArgs": [],
"linkedLibraries": {},
- "deployTxnHash": "0x7ac01cc6cc6101a034d757bc8a521aa3c2b4e401f8f295cf14b5cb70770b6382",
- "deployTxnBlockNumber": "6474230",
- "deployTimestamp": "1723307040",
+ "deployTxnHash": "0xc3bca3b335b4e296a71644c6072d3f914d87c41d0ea12dffe7f3c60dc885f615",
+ "deployTxnBlockNumber": "6506099",
+ "deployTimestamp": "1723741656",
"sourceName": "src/contracts/modules/EpochNftModule.sol",
"contractName": "EpochNftModule",
"deployedOn": "deploy.EpochNftModule",
"gasUsed": 1190314,
- "gasCost": "1046361736"
+ "gasCost": "35710715745"
}
\ No newline at end of file
diff --git a/packages/protocol/deployments/11155111/EpochTradeModule.json b/packages/protocol/deployments/11155111/EpochTradeModule.json
index 72925e18..097272db 100644
--- a/packages/protocol/deployments/11155111/EpochTradeModule.json
+++ b/packages/protocol/deployments/11155111/EpochTradeModule.json
@@ -1,5 +1,5 @@
{
- "address": "0x7adA6C864c9e0d8DE13334CE8F1F547bB3BF33D4",
+ "address": "0x6F10Da98ba11D9Fcc8aA272B86fd4da999968f4A",
"abi": [
{
"type": "function",
@@ -231,11 +231,6 @@
}
]
},
- {
- "type": "error",
- "name": "OverflowInt24ToUint256",
- "inputs": []
- },
{
"type": "error",
"name": "OverflowInt256ToUint256",
@@ -285,12 +280,12 @@
],
"constructorArgs": [],
"linkedLibraries": {},
- "deployTxnHash": "0x1bbcb9af6846d4cb6c50fd53d8e343be7c27db1dc44184088aa6931c33ee16c8",
- "deployTxnBlockNumber": "6474231",
- "deployTimestamp": "1723307052",
+ "deployTxnHash": "0x9c449c4df523db2c645910233db4bf6fd5d31f28f73d95132967751f6052a466",
+ "deployTxnBlockNumber": "6506101",
+ "deployTimestamp": "1723741692",
"sourceName": "src/contracts/modules/EpochTradeModule.sol",
"contractName": "EpochTradeModule",
"deployedOn": "deploy.EpochTradeModule",
- "gasUsed": 2196653,
- "gasCost": "1040865499"
+ "gasUsed": 1933722,
+ "gasCost": "33339990657"
}
\ No newline at end of file
diff --git a/packages/protocol/deployments/11155111/EpochUMASettlementModule.json b/packages/protocol/deployments/11155111/EpochUMASettlementModule.json
index b517ba86..d456ac2b 100644
--- a/packages/protocol/deployments/11155111/EpochUMASettlementModule.json
+++ b/packages/protocol/deployments/11155111/EpochUMASettlementModule.json
@@ -1,5 +1,5 @@
{
- "address": "0xd2e77ff7a4a001D070E48eC3090219133f8c37d2",
+ "address": "0xEBAd76CA60B0524063aF9049f7E7c2a1DF1E442e",
"abi": [
{
"type": "function",
@@ -157,12 +157,12 @@
],
"constructorArgs": [],
"linkedLibraries": {},
- "deployTxnHash": "0x94a85817e24f209ca231d0d93830420880cf0352e49d850eb163b903135c6d36",
- "deployTxnBlockNumber": "6474232",
- "deployTimestamp": "1723307064",
+ "deployTxnHash": "0xbe91e256e5495a233b6ed50eccb59ac9a939e60189e1edba1f846a61365a108e",
+ "deployTxnBlockNumber": "6506102",
+ "deployTimestamp": "1723741704",
"sourceName": "src/contracts/modules/EpochUMASettlementModule.sol",
"contractName": "EpochUMASettlementModule",
"deployedOn": "deploy.EpochUMASettlementModule",
- "gasUsed": 768926,
- "gasCost": "1025526924"
+ "gasUsed": 798576,
+ "gasCost": "33852750403"
}
\ No newline at end of file
diff --git a/packages/protocol/deployments/11155111/EpochViewsModule.json b/packages/protocol/deployments/11155111/EpochViewsModule.json
index d46dc53e..ec20c1bc 100644
--- a/packages/protocol/deployments/11155111/EpochViewsModule.json
+++ b/packages/protocol/deployments/11155111/EpochViewsModule.json
@@ -1,5 +1,5 @@
{
- "address": "0x0660A1E92Ff10E2fA30fe971812A1Eb99b2d8313",
+ "address": "0xbA2477b4c97bB7BcCB386844F64B87bC8880Bc44",
"abi": [
{
"type": "function",
@@ -232,12 +232,12 @@
],
"constructorArgs": [],
"linkedLibraries": {},
- "deployTxnHash": "0x43fc7637f2e3e32338e8080ec625a984d222c9020f596a068821cc70ef466fc1",
- "deployTxnBlockNumber": "6474233",
- "deployTimestamp": "1723307076",
+ "deployTxnHash": "0x60c704db36efe9f0cecd378d49b2a9ffa5355469433d3900cd3b6c9bdb8d192f",
+ "deployTxnBlockNumber": "6506103",
+ "deployTimestamp": "1723741716",
"sourceName": "src/contracts/modules/EpochViewsModule.sol",
"contractName": "EpochViewsModule",
"deployedOn": "deploy.EpochViewsModule",
- "gasUsed": 443339,
- "gasCost": "1001152855"
+ "gasUsed": 443327,
+ "gasCost": "31085799463"
}
\ No newline at end of file
diff --git a/packages/protocol/deployments/11155111/Foil.json b/packages/protocol/deployments/11155111/Foil.json
index 1db0d023..7eea3827 100644
--- a/packages/protocol/deployments/11155111/Foil.json
+++ b/packages/protocol/deployments/11155111/Foil.json
@@ -1,5 +1,5 @@
{
- "address": "0xa088d9093076894185184c3b1e4ab524fc947720",
+ "address": "0x55f555451e9ca46bbc150e3117209a203b8c64e6",
"abi": [
{
"type": "function",
@@ -1654,11 +1654,11 @@
}
],
"deployedOn": "router.Foil",
- "deployTxnHash": "0xe954629061d99bc95f0a2721a06458214f0f7910574057d59d1d94e007d13c70",
- "deployTxnBlockNumber": "6474236",
- "deployTimestamp": "1723307112",
+ "deployTxnHash": "0x55c6aa35b612646523feefd07b2dc1bf78efb6135f02e52a1cc8aacf26bd0f12",
+ "deployTxnBlockNumber": "6506106",
+ "deployTimestamp": "1723741752",
"contractName": "Foil",
"sourceName": "Foil.sol",
- "gasUsed": 428716,
- "gasCost": "964291468"
+ "gasUsed": 428908,
+ "gasCost": "26803319809"
}
\ No newline at end of file
diff --git a/packages/protocol/deployments/11155111/ReentrancyGuard.json b/packages/protocol/deployments/11155111/ReentrancyGuard.json
index c0c527bc..3952de15 100644
--- a/packages/protocol/deployments/11155111/ReentrancyGuard.json
+++ b/packages/protocol/deployments/11155111/ReentrancyGuard.json
@@ -1,5 +1,5 @@
{
- "address": "0xC3aDB913bDB5d8e964dDc9dcCdBE896349DA63E9",
+ "address": "0x3f5c8BA60B74259Fda75950Ff7d6B60A90852084",
"abi": [
{
"type": "error",
@@ -9,12 +9,12 @@
],
"constructorArgs": [],
"linkedLibraries": {},
- "deployTxnHash": "0x7b86f1de0229aa2981a0f891657e7af744f54c9cfad557f5fe0c529a95800f06",
- "deployTxnBlockNumber": "6474234",
- "deployTimestamp": "1723307088",
+ "deployTxnHash": "0x7c1160055d38235b86b52108105e1b61d04f5650264b55e8835c69b4b0910eac",
+ "deployTxnBlockNumber": "6506104",
+ "deployTimestamp": "1723741728",
"sourceName": "node_modules/@openzeppelin/contracts/utils/ReentrancyGuard.sol",
"contractName": "ReentrancyGuard",
"deployedOn": "deploy.ReentrancyGuard",
"gasUsed": 53000,
- "gasCost": "1000379233"
+ "gasCost": "28981752751"
}
\ No newline at end of file
diff --git a/packages/protocol/src/contracts/modules/EpochTradeModule.sol b/packages/protocol/src/contracts/modules/EpochTradeModule.sol
index 0852bb0c..7399b880 100644
--- a/packages/protocol/src/contracts/modules/EpochTradeModule.sol
+++ b/packages/protocol/src/contracts/modules/EpochTradeModule.sol
@@ -50,6 +50,11 @@ contract EpochTradeModule is IEpochTradeModule {
);
}
+ position.updateCollateral(
+ Market.load().collateralAsset,
+ collateralAmount
+ );
+
// Validate after trading that collateral is enough
position.afterTradeCheck();
}
@@ -126,7 +131,6 @@ contract EpochTradeModule is IEpochTradeModule {
// with the collateral get vEth (Loan)
uint256 vEthLoan = collateralAmount; // 1:1
- position.depositedCollateralAmount += collateralAmount;
position.borrowedVEth += vEthLoan;
if (tokenAmount < 0 || tokenAmountLimit < 0) {
@@ -156,6 +160,11 @@ contract EpochTradeModule is IEpochTradeModule {
// Refund excess vEth sent
position.borrowedVEth -= refundAmountVEth;
+ position.updateCollateral(
+ Market.load().collateralAsset,
+ collateralAmount
+ );
+
position.updateBalance(
tokenAmount,
tokenAmountVEth.toInt(),
@@ -181,7 +190,6 @@ contract EpochTradeModule is IEpochTradeModule {
// with the collateral get vGas (Loan)
uint256 vGasLoan = (tokenAmount * -1).toUint(); //(collateralAmount).divDecimal(getReferencePrice()); // collatera / vEth = 1/1 ; vGas/vEth = 1/currentPrice
- position.depositedCollateralAmount += collateralAmount;
position.borrowedVGas += vGasLoan;
if (tokenAmount > 0 || tokenAmountLimit > 0) {
@@ -202,6 +210,11 @@ contract EpochTradeModule is IEpochTradeModule {
params
);
+ position.updateCollateral(
+ Market.load().collateralAsset,
+ collateralAmount
+ );
+
position.updateBalance(
tokenAmount,
tokenAmountVEth.toInt(),
@@ -242,13 +255,11 @@ contract EpochTradeModule is IEpochTradeModule {
uint256 delta = (tokenAmount - position.currentTokenAmount)
.toUint();
// with the collateral get vEth (Loan)
- position.depositedCollateralAmount += collateralAmount;
- uint256 vEthLoan = position.depositedCollateralAmount; // 1:1
- position.borrowedVEth = vEthLoan;
+ uint256 vEthDeltaLoan = collateralAmount; // 1:1
SwapTokensExactOutParams memory params = SwapTokensExactOutParams({
epochId: position.epochId,
- availableAmountInVEth: vEthLoan,
+ availableAmountInVEth: vEthDeltaLoan,
availableAmountInVGas: 0,
amountInLimitVEth: 0,
amountInLimitVGas: 0,
@@ -263,7 +274,9 @@ contract EpochTradeModule is IEpochTradeModule {
uint256 tokenAmountVEth,
uint256 tokenAmountVGas
) = swapTokensExactOut(params);
- position.borrowedVEth -= refundAmountVEth;
+ // Adjust the delta loan with the refund
+ vEthDeltaLoan -= refundAmountVEth;
+ position.borrowedVEth += vEthDeltaLoan;
position.updateBalance(
delta.toInt(),
@@ -272,11 +285,6 @@ contract EpochTradeModule is IEpochTradeModule {
);
} else {
// Reduce the position (LONG)
- if (collateralAmount > 0) {
- revert Errors.InvalidData(
- "Long Position: Unexpected collateral"
- );
- }
int256 delta = (tokenAmount - position.currentTokenAmount);
@@ -299,6 +307,11 @@ contract EpochTradeModule is IEpochTradeModule {
position.updateBalance(delta, 0, delta);
}
+
+ position.updateCollateral(
+ Market.load().collateralAsset,
+ collateralAmount
+ );
}
/**
@@ -333,18 +346,21 @@ contract EpochTradeModule is IEpochTradeModule {
// Increase the position (SHORT)
int256 delta = (tokenAmount - position.currentTokenAmount);
- int256 deltaLimit = (tokenAmountLimit -
- position.currentTokenAmount);
+ int256 deltaLimit;
+ if (tokenAmountLimit >= position.currentTokenAmount) {
+ deltaLimit = 0;
+ } else {
+ deltaLimit = (tokenAmountLimit - position.currentTokenAmount);
+ }
// with the collateral get vGas (Loan)
- uint256 vGasLoan = (delta * -1).toUint(); //
- position.depositedCollateralAmount += collateralAmount;
+ uint256 vGasLoan = (delta * -1).toUint();
position.borrowedVGas += vGasLoan;
SwapTokensExactInParams memory params = SwapTokensExactInParams({
epochId: position.epochId,
amountInVEth: 0,
- amountInVGas: (delta * -1).toUint(),
+ amountInVGas: vGasLoan,
amountOutLimitVEth: 0,
amountOutLimitVGas: (deltaLimit * -1).toUint()
});
@@ -362,13 +378,8 @@ contract EpochTradeModule is IEpochTradeModule {
);
} else {
// Decrease the position (SHORT)
- if (collateralAmount > 0) {
- revert Errors.InvalidData(
- "Short Position: Unexpected collateral"
- );
- }
-
int256 delta = (position.currentTokenAmount - tokenAmount);
+ position.borrowedVGas -= (delta * -1).toUint();
SwapTokensExactOutParams memory params = SwapTokensExactOutParams({
epochId: position.epochId,
@@ -394,6 +405,11 @@ contract EpochTradeModule is IEpochTradeModule {
0
);
}
+
+ position.updateCollateral(
+ Market.load().collateralAsset,
+ collateralAmount
+ );
}
/**
@@ -428,9 +444,6 @@ contract EpochTradeModule is IEpochTradeModule {
) internal {
// TODO check if after settlement and use the settlement price
- // Add sent collateral
- position.depositedCollateralAmount += collateralAmount;
-
if (position.currentTokenAmount > 0) {
// Close LONG position
SwapTokensExactInParams memory params = SwapTokensExactInParams({
@@ -459,6 +472,8 @@ contract EpochTradeModule is IEpochTradeModule {
position.borrowedVEth = 0;
+ position.updateCollateral(Market.load().collateralAsset, 0);
+
position.resetBalance();
} else {
// Close SHORT position
@@ -523,6 +538,8 @@ contract EpochTradeModule is IEpochTradeModule {
position.borrowedVGas = 0;
+ position.updateCollateral(Market.load().collateralAsset, 0);
+
position.resetBalance();
}
}
diff --git a/packages/protocol/src/contracts/modules/EpochUMASettlementModule.sol b/packages/protocol/src/contracts/modules/EpochUMASettlementModule.sol
index 0bea2202..5b275a80 100644
--- a/packages/protocol/src/contracts/modules/EpochUMASettlementModule.sol
+++ b/packages/protocol/src/contracts/modules/EpochUMASettlementModule.sol
@@ -74,7 +74,7 @@ contract EpochUMASettlementModule is ReentrancyGuard {
epoch.params.assertionLiveness,
IERC20(epoch.params.bondCurrency),
uint64(epoch.params.bondAmount),
- bytes32(0),
+ optimisticOracleV3.defaultIdentifier(),
bytes32(0)
);
@@ -97,10 +97,12 @@ contract EpochUMASettlementModule is ReentrancyGuard {
require(!epoch.settled, "Market already settled");
Epoch.Settlement storage settlement = epoch.settlement;
- epoch.settlementPrice = settlement.settlementPrice;
- epoch.settled = true;
- emit MarketSettled(settlement.settlementPrice);
+ if(!epoch.settlement.disputed) {
+ epoch.settlementPrice = settlement.settlementPrice;
+ epoch.settled = true;
+ emit MarketSettled(settlement.settlementPrice);
+ }
}
function assertionDisputedCallback(
@@ -119,4 +121,4 @@ contract EpochUMASettlementModule is ReentrancyGuard {
emit SettlementDisputed(block.timestamp);
}
-}
+}
\ No newline at end of file
diff --git a/packages/protocol/test/Foil.trade-module.t.sol b/packages/protocol/test/Foil.trade-module.t.sol
index 97d07a2e..7eedd703 100644
--- a/packages/protocol/test/Foil.trade-module.t.sol
+++ b/packages/protocol/test/Foil.trade-module.t.sol
@@ -1,236 +1,463 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.2 <0.9.0;
-import "forge-std/Test.sol";
import "cannon-std/Cannon.sol";
-
-import {IFoil} from "../src/contracts/interfaces/IFoil.sol";
-import {IFoilStructs} from "../src/contracts/interfaces/IFoilStructs.sol";
-import {VirtualToken} from "../src/contracts/external/VirtualToken.sol";
-import {TickMath} from "../src/contracts/external/univ3/TickMath.sol";
-import "../src/contracts/interfaces/external/INonfungiblePositionManager.sol";
-import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
-import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol";
-import "../src/contracts/storage/Position.sol";
-import {IMintableToken} from "../src/contracts/external/IMintableToken.sol";
+import "./FoilTradeTestHelper.sol";
+import "../src/synthetix/utils/DecimalMath.sol";
import "forge-std/console2.sol";
-contract FoilTradeModuleTest is Test {
+contract FoilTradeModuleTest is FoilTradeTestHelper {
using Cannon for Vm;
+ using DecimalMath for uint256;
IFoil foil;
address pool;
address tokenA;
address tokenB;
uint256 epochId;
+ uint256 feeRate;
+ uint256 UNIT = 1e18;
IMintableToken collateralAsset;
+ IUniswapV3Pool uniCastedPool;
function setUp() public {
- console2.log("setUp");
foil = IFoil(vm.getAddress("Foil"));
collateralAsset = IMintableToken(
vm.getAddress("CollateralAsset.Token")
);
- collateralAsset.mint(10_000_000 ether, address(this));
- collateralAsset.approve(address(foil), 10_000_000 ether);
-
- console2.log("getEpoch");
+ collateralAsset.mint(type(uint240).max, address(this));
+ collateralAsset.approve(address(foil), type(uint240).max);
(epochId, , , pool, tokenA, tokenB) = foil.getLatestEpoch();
-
- console2.log("pool", pool);
- console2.log("tokenA", tokenA);
- console2.log("tokenB", tokenB);
- console2.log("epochId", epochId);
+ uniCastedPool = IUniswapV3Pool(pool);
+ feeRate = uint256(uniCastedPool.fee()) * 1e12;
}
- function test_trade_long() public {
- uint256 priceReference;
- uint256 positionId_1;
- priceReference = foil.getReferencePrice(epochId);
-
- console2.log("priceReference", priceReference);
-
- uint256 collateralAmount = 100_000 ether;
- int24 lowerTick = 12200;
- int24 upperTick = 12400;
-
- (
- uint256 amountTokenA,
- uint256 amountTokenB,
- uint256 liquidity
- ) = getTokenAmountsForCollateralAmount(
- collateralAmount,
- lowerTick,
- upperTick
- );
-
- console2.log("amountTokenA", amountTokenA);
- console2.log("amountTokenB", amountTokenB);
- console2.log("liquidity", liquidity);
-
- IFoilStructs.LiquidityPositionParams memory params = IFoilStructs
- .LiquidityPositionParams({
- epochId: epochId,
- amountTokenA: amountTokenA,
- amountTokenB: amountTokenB,
- collateralAmount: collateralAmount,
- lowerTick: lowerTick,
- upperTick: upperTick,
- minAmountTokenA: 0,
- minAmountTokenB: 0
- });
-
- foil.createLiquidityPosition(params);
-
- priceReference = foil.getReferencePrice(epochId);
- console2.log("priceReference", priceReference);
-
- // Create Long position
- console2.log("Create Long position");
- priceReference = foil.getReferencePrice(epochId);
- console2.log("priceReference", priceReference);
- positionId_1 = foil.createTraderPosition(
+ function test_tradeLong_Only() public {
+ uint256 referencePrice;
+ uint256 positionId;
+ uint256 collateralForOrder = 10 ether;
+ int256 positionSize;
+ uint256 tokens;
+ uint256 fee;
+ uint256 accumulatedFee;
+
+ StateData memory expectedStateData;
+ StateData memory currentStateData;
+
+ addLiquidity(foil, pool, epochId, collateralForOrder * 100_000); // enough to keep price stable (no slippage)
+
+ // Set position size
+ positionSize = int256(collateralForOrder / 100);
+
+ fillCollateralStateData(foil, collateralAsset, currentStateData);
+
+ referencePrice = foil.getReferencePrice(epochId);
+ tokens = uint256(positionSize).mulDecimal(referencePrice);
+ fee = tokens.mulDecimal(feeRate);
+ accumulatedFee += fee;
+
+ expectedStateData.userCollateral =
+ currentStateData.userCollateral -
+ collateralForOrder;
+ expectedStateData.foilCollateral =
+ currentStateData.foilCollateral +
+ collateralForOrder;
+ expectedStateData.depositedCollateralAmount = collateralForOrder;
+ expectedStateData.currentTokenAmount = positionSize;
+ expectedStateData.vEthAmount = 0;
+ expectedStateData.vGasAmount = uint256(positionSize);
+ expectedStateData.borrowedVEth = tokens + fee;
+ expectedStateData.borrowedVGas = 0;
+
+ // Create Long position (with enough collateral)
+ positionId = foil.createTraderPosition(
epochId,
- 10 ether,
- .1 ether,
+ collateralForOrder,
+ positionSize,
0
);
- logPositionAndAccount(positionId_1);
+
+ currentStateData = assertPosition(
+ foil,
+ positionId,
+ collateralAsset,
+ expectedStateData,
+ "Create Long"
+ );
// Modify Long position (increase it)
- console2.log("Modify Long position (increase it)");
- priceReference = foil.getReferencePrice(epochId);
- console2.log("priceReference", priceReference);
- foil.modifyTraderPosition(positionId_1, 10 ether, .2 ether, 0);
- logPositionAndAccount(positionId_1);
+ referencePrice = foil.getReferencePrice(epochId);
+ tokens = uint256(positionSize).mulDecimal(referencePrice);
+ fee = tokens.mulDecimal(feeRate);
+ accumulatedFee += fee;
+
+ expectedStateData.userCollateral =
+ currentStateData.userCollateral -
+ collateralForOrder;
+ expectedStateData.foilCollateral =
+ currentStateData.foilCollateral +
+ collateralForOrder;
+ expectedStateData.depositedCollateralAmount = collateralForOrder * 2;
+ expectedStateData.currentTokenAmount = positionSize * 2;
+ expectedStateData.vEthAmount = 0;
+ expectedStateData.vGasAmount = uint256(positionSize) * 2;
+ expectedStateData.borrowedVEth =
+ currentStateData.borrowedVEth +
+ tokens +
+ fee;
+ expectedStateData.borrowedVGas = 0;
- // Modify Long position (decrease it)
- console2.log("Modify Long position (decrease it)");
- priceReference = foil.getReferencePrice(epochId);
- console2.log("priceReference", priceReference);
- foil.modifyTraderPosition(positionId_1, 0 ether, .1 ether, 0);
- logPositionAndAccount(positionId_1);
+ foil.modifyTraderPosition(
+ positionId,
+ 2 * collateralForOrder,
+ 2 * positionSize,
+ 0
+ );
- // Modify Long position (close it)
- console2.log("Modify Long position (close it)");
- priceReference = foil.getReferencePrice(epochId);
- console2.log("priceReference", priceReference);
- foil.modifyTraderPosition(positionId_1, 0 ether, 0, 0);
- logPositionAndAccount(positionId_1);
- }
+ currentStateData = assertPosition(
+ foil,
+ positionId,
+ collateralAsset,
+ expectedStateData,
+ "Increase Long"
+ );
- function test_trade_long_cross_sides() public {
- uint256 priceReference;
- uint256 positionId_3;
- priceReference = foil.getReferencePrice(epochId);
+ // Modify Long position (decrease it)
+ referencePrice = foil.getReferencePrice(epochId);
+ tokens = uint256(positionSize).mulDecimal(referencePrice);
+ fee = tokens.mulDecimal(feeRate);
+ accumulatedFee += fee;
+
+ expectedStateData.userCollateral = currentStateData.userCollateral;
+ expectedStateData.foilCollateral = currentStateData.foilCollateral;
+ expectedStateData.depositedCollateralAmount = currentStateData
+ .depositedCollateralAmount;
+ expectedStateData.currentTokenAmount = positionSize;
+ expectedStateData.vEthAmount = 0;
+ expectedStateData.vGasAmount = uint256(positionSize);
+ expectedStateData.borrowedVEth =
+ currentStateData.borrowedVEth -
+ tokens +
+ fee; // discounting here because we are charging the fee on the vETH we get back
+ expectedStateData.borrowedVGas = 0;
- console2.log("priceReference", priceReference);
- IFoilStructs.LiquidityPositionParams memory params = IFoilStructs
- .LiquidityPositionParams({
- epochId: epochId,
- amountTokenB: 20000 ether,
- amountTokenA: 1000 ether,
- collateralAmount: 100_000 ether,
- lowerTick: 12200,
- upperTick: 12400,
- minAmountTokenA: 0,
- minAmountTokenB: 0
- });
+ foil.modifyTraderPosition(
+ positionId,
+ 2 * collateralForOrder, // keep same collateral
+ positionSize,
+ 0
+ );
- foil.createLiquidityPosition(params);
- params.amountTokenB = 40000 ether;
- params.amountTokenA = 2000 ether;
- foil.createLiquidityPosition(params);
+ currentStateData = assertPosition(
+ foil,
+ positionId,
+ collateralAsset,
+ expectedStateData,
+ "Decrease Long"
+ );
- priceReference = foil.getReferencePrice(epochId);
- console2.log("priceReference", priceReference);
+ // Modify Long position (close it)
+ referencePrice = foil.getReferencePrice(epochId);
+ // fee for closing the position
+ fee = currentStateData.vGasAmount.mulDecimal(referencePrice).mulDecimal(
+ feeRate
+ );
+ accumulatedFee += fee;
+
+ expectedStateData.userCollateral =
+ currentStateData.userCollateral +
+ 2 *
+ collateralForOrder -
+ accumulatedFee;
+ expectedStateData.foilCollateral =
+ currentStateData.foilCollateral -
+ 2 *
+ collateralForOrder +
+ accumulatedFee;
+ expectedStateData.depositedCollateralAmount = 0;
+ expectedStateData.currentTokenAmount = 0;
+ expectedStateData.vEthAmount = 0;
+ expectedStateData.vGasAmount = 0;
+ expectedStateData.borrowedVEth = 0;
+ expectedStateData.borrowedVGas = 0;
+
+ foil.modifyTraderPosition(positionId, 0 ether, 0, 0);
+
+ assertPosition(
+ foil,
+ positionId,
+ collateralAsset,
+ expectedStateData,
+ "Close Long"
+ );
+ }
- // Create Long position (another one)
- console2.log("Create Long position (another one)");
- priceReference = foil.getReferencePrice(epochId);
- console2.log("priceReference", priceReference);
- positionId_3 = foil.createTraderPosition(
+ function test_trade_long_cross_sides_Only() public {
+ uint256 referencePrice;
+ uint256 positionId;
+ uint256 collateralForOrder = 10 ether;
+ int256 positionSize;
+ uint256 tokens;
+ uint256 fee;
+ uint256 accumulatedFee;
+
+ StateData memory expectedStateData;
+ StateData memory currentStateData;
+
+ addLiquidity(foil, pool, epochId, collateralForOrder * 100_000); // enough to keep price stable (no slippage)
+
+ // Set position size
+ positionSize = int256(collateralForOrder / 100);
+
+ fillCollateralStateData(foil, collateralAsset, currentStateData);
+
+ referencePrice = foil.getReferencePrice(epochId);
+ tokens = uint256(positionSize).mulDecimal(referencePrice);
+ fee = tokens.mulDecimal(feeRate);
+ accumulatedFee += fee;
+
+ expectedStateData.userCollateral =
+ currentStateData.userCollateral -
+ collateralForOrder;
+ expectedStateData.foilCollateral =
+ currentStateData.foilCollateral +
+ collateralForOrder;
+ expectedStateData.depositedCollateralAmount = collateralForOrder;
+ expectedStateData.currentTokenAmount = positionSize;
+ expectedStateData.vEthAmount = 0;
+ expectedStateData.vGasAmount = uint256(positionSize);
+ expectedStateData.borrowedVEth = tokens + fee;
+ expectedStateData.borrowedVGas = 0;
+
+ accumulatedFee += uint256(positionSize)
+ .mulDecimal(referencePrice)
+ .mulDecimal(feeRate);
+
+ // Create Long position (with enough collateral)
+ positionId = foil.createTraderPosition(
epochId,
- 10 ether,
- .1 ether,
+ collateralForOrder,
+ positionSize,
0
);
- logPositionAndAccount(positionId_3);
+
+ currentStateData = assertPosition(
+ foil,
+ positionId,
+ collateralAsset,
+ expectedStateData,
+ "Create Long"
+ );
// Modify Long position (change side)
- console2.log("Modify Long position (change side)");
- priceReference = foil.getReferencePrice(epochId);
- console2.log("priceReference", priceReference);
+ referencePrice = foil.getReferencePrice(epochId);
+ // change sides means closing the long order and opening a short order
+ // fee for closing the position
+ fee = currentStateData.vGasAmount.mulDecimal(referencePrice).mulDecimal(
+ feeRate
+ );
+ accumulatedFee += fee;
+
+ // tokens and fee for opening the short order
+ tokens = uint256(positionSize).divDecimal(referencePrice);
+ fee = tokens.mulDecimal(feeRate);
+ accumulatedFee += fee;
+
+ expectedStateData.userCollateral = currentStateData.userCollateral;
+ expectedStateData.foilCollateral = currentStateData.foilCollateral;
+ expectedStateData.depositedCollateralAmount = currentStateData
+ .depositedCollateralAmount;
+ expectedStateData.currentTokenAmount = positionSize * -1;
+ expectedStateData.vEthAmount = tokens - fee;
+ expectedStateData.vGasAmount = 0;
+ expectedStateData.borrowedVEth = 0;
+ expectedStateData.borrowedVGas = uint256(positionSize);
+
foil.modifyTraderPosition(
- positionId_3,
- 0 ether,
- -.05 ether,
- -.01 ether
+ positionId,
+ collateralForOrder,
+ -1 * positionSize,
+ 0
);
- logPositionAndAccount(positionId_3);
- }
- function test_trade_short() public {
- uint256 priceReference;
- uint256 positionId_2;
- priceReference = foil.getReferencePrice(epochId);
+ currentStateData = assertPosition(
+ foil,
+ positionId,
+ collateralAsset,
+ expectedStateData,
+ "Change sides Long"
+ );
+ }
- console2.log("priceReference", priceReference);
- IFoilStructs.LiquidityPositionParams memory params = IFoilStructs
- .LiquidityPositionParams({
- epochId: epochId,
- amountTokenB: 20000 ether,
- amountTokenA: 1000 ether,
- collateralAmount: 100_000 ether,
- lowerTick: 12200,
- upperTick: 12400,
- minAmountTokenA: 0,
- minAmountTokenB: 0
- });
+ function test_trade_short_Only() public {
+ uint256 referencePrice;
+ uint256 positionId;
+ uint256 collateralForOrder = 10 ether;
+ int256 positionSize;
+ uint256 tokens;
+ uint256 fee;
+ uint256 accumulatedFee;
+
+ StateData memory expectedStateData;
+ StateData memory currentStateData;
+
+ addLiquidity(foil, pool, epochId, collateralForOrder * 100_000); // enough to keep price stable (no slippage)
+
+ // Set position size
+ positionSize = int256(collateralForOrder / 100);
+
+ fillCollateralStateData(foil, collateralAsset, currentStateData);
+
+ referencePrice = foil.getReferencePrice(epochId);
+ tokens = uint256(positionSize).mulDecimal(referencePrice);
+ fee = tokens.mulDecimal(feeRate);
+ accumulatedFee += fee;
+
+ expectedStateData.userCollateral =
+ currentStateData.userCollateral -
+ collateralForOrder;
+ expectedStateData.foilCollateral =
+ currentStateData.foilCollateral +
+ collateralForOrder;
+ expectedStateData.depositedCollateralAmount = collateralForOrder;
+ expectedStateData.currentTokenAmount = -1 * positionSize;
+ expectedStateData.vEthAmount = tokens - fee;
+ expectedStateData.vGasAmount = 0;
+ expectedStateData.borrowedVEth = 0;
+ expectedStateData.borrowedVGas = uint256(positionSize);
+
+ // Create Long position (with enough collateral)
+ positionId = foil.createTraderPosition(
+ epochId,
+ collateralForOrder,
+ -1 * positionSize,
+ 0
+ );
- foil.createLiquidityPosition(params);
- params.amountTokenB = 40000 ether;
- params.amountTokenA = 2000 ether;
- foil.createLiquidityPosition(params);
+ currentStateData = assertPosition(
+ foil,
+ positionId,
+ collateralAsset,
+ expectedStateData,
+ "Create Short"
+ );
- priceReference = foil.getReferencePrice(epochId);
- console2.log("priceReference", priceReference);
+ // Modify Short position (increase it)
+ referencePrice = foil.getReferencePrice(epochId);
+ tokens = uint256(positionSize).mulDecimal(referencePrice);
+ fee = tokens.mulDecimal(feeRate);
+ accumulatedFee += fee;
+
+ expectedStateData.userCollateral =
+ currentStateData.userCollateral -
+ collateralForOrder;
+ expectedStateData.foilCollateral =
+ currentStateData.foilCollateral +
+ collateralForOrder;
+ expectedStateData.depositedCollateralAmount = collateralForOrder * 2;
+ expectedStateData.currentTokenAmount = positionSize * -2;
+ expectedStateData.vEthAmount =
+ currentStateData.vEthAmount +
+ tokens -
+ fee;
+ expectedStateData.vGasAmount = 0;
+ expectedStateData.borrowedVEth = 0;
+ expectedStateData.borrowedVGas = uint256(positionSize) * 2;
- // Create Short position
- console2.log("Create Short position");
- positionId_2 = foil.createTraderPosition(
- epochId,
- 10 ether,
- -.1 ether,
+ foil.modifyTraderPosition(
+ positionId,
+ 2 * collateralForOrder,
+ -2 * positionSize,
0
);
- logPositionAndAccount(positionId_2);
- // Modify Short position (increase it)
- console2.log("Modify Short position (increase it)");
- foil.modifyTraderPosition(positionId_2, 10 ether, -.2 ether, -.1 ether);
- logPositionAndAccount(positionId_2);
+ currentStateData = assertPosition(
+ foil,
+ positionId,
+ collateralAsset,
+ expectedStateData,
+ "Increase Short"
+ );
// Modify Short position (decrease it)
- console2.log("Modify Short position (decrease it)");
- foil.modifyTraderPosition(positionId_2, 0, -.05 ether, -.01 ether);
- logPositionAndAccount(positionId_2);
+ referencePrice = foil.getReferencePrice(epochId);
+ tokens = uint256(positionSize).mulDecimal(referencePrice);
+ fee = tokens.mulDecimal(feeRate);
+ accumulatedFee += fee;
+
+ expectedStateData.userCollateral = currentStateData.userCollateral;
+ expectedStateData.foilCollateral = currentStateData.foilCollateral;
+ expectedStateData.depositedCollateralAmount = currentStateData
+ .depositedCollateralAmount;
+ expectedStateData.currentTokenAmount = positionSize * -1;
+ expectedStateData.vEthAmount =
+ currentStateData.vEthAmount -
+ tokens -
+ fee;
+ expectedStateData.vGasAmount = 0;
+ expectedStateData.borrowedVEth = 0;
+ expectedStateData.borrowedVGas = uint256(positionSize);
+
+ foil.modifyTraderPosition(
+ positionId,
+ 2 * collateralForOrder, // keep same collateral
+ -1 * positionSize,
+ 0
+ );
+
+ currentStateData = assertPosition(
+ foil,
+ positionId,
+ collateralAsset,
+ expectedStateData,
+ "Decrease Short"
+ );
// Modify Short position (close it)
- console2.log("Modify Short position (close it)");
- foil.modifyTraderPosition(positionId_2, 0, 0, 0);
- logPositionAndAccount(positionId_2);
+ referencePrice = foil.getReferencePrice(epochId);
+ // fee for closing the position
+ fee = currentStateData.vGasAmount.mulDecimal(referencePrice).mulDecimal(
+ feeRate
+ );
+ accumulatedFee += fee;
+
+ expectedStateData.userCollateral =
+ currentStateData.userCollateral +
+ 2 *
+ collateralForOrder -
+ accumulatedFee;
+ expectedStateData.foilCollateral =
+ currentStateData.foilCollateral -
+ 2 *
+ collateralForOrder +
+ accumulatedFee;
+ expectedStateData.depositedCollateralAmount = 0;
+ expectedStateData.currentTokenAmount = 0;
+ expectedStateData.vEthAmount = 0;
+ expectedStateData.vGasAmount = 0;
+ expectedStateData.borrowedVEth = 0;
+ expectedStateData.borrowedVGas = 0;
+
+ foil.modifyTraderPosition(positionId, 0 ether, 0, 0);
+
+ assertPosition(
+ foil,
+ positionId,
+ collateralAsset,
+ expectedStateData,
+ "Close Short"
+ );
}
- function test_trade_short_cross_sides_Only() public {
- uint256 priceReference;
+ function test_trade_short_cross_sides() public {
+ uint256 referencePrice;
uint256 positionId_4;
- priceReference = foil.getReferencePrice(epochId);
+ referencePrice = foil.getReferencePrice(epochId);
- console2.log("priceReference", priceReference);
+ console2.log("referencePrice", referencePrice);
IFoilStructs.LiquidityPositionParams memory params = IFoilStructs
.LiquidityPositionParams({
epochId: epochId,
@@ -248,8 +475,8 @@ contract FoilTradeModuleTest is Test {
params.amountTokenA = 2000 ether;
foil.createLiquidityPosition(params);
- priceReference = foil.getReferencePrice(epochId);
- console2.log("priceReference", priceReference);
+ referencePrice = foil.getReferencePrice(epochId);
+ console2.log("referencePrice", referencePrice);
// Create Short position (another one)
console2.log("Create Short position (another one)");
@@ -259,60 +486,11 @@ contract FoilTradeModuleTest is Test {
-.1 ether,
0
);
- logPositionAndAccount(positionId_4);
+ logPositionAndAccount(foil, positionId_4);
// Modify Short position (change side)
console2.log("Modify Short position (change side)");
foil.modifyTraderPosition(positionId_4, 0, .05 ether, 0);
- logPositionAndAccount(positionId_4);
- }
-
- function getTokenAmountsForCollateralAmount(
- uint256 collateralAmount,
- int24 lowerTick,
- int24 upperTick
- )
- public
- view
- returns (uint256 loanAmount0, uint256 loanAmount1, uint256 liquidity)
- {
- (uint160 sqrtPriceX96, , , , , , ) = IUniswapV3Pool(pool).slot0();
- console.log("sqrtPriceX96", sqrtPriceX96);
-
- uint160 sqrtPriceAX96 = uint160(TickMath.getSqrtRatioAtTick(lowerTick));
- uint160 sqrtPriceBX96 = uint160(TickMath.getSqrtRatioAtTick(upperTick));
-
- console2.log("sqrtPriceAX96", sqrtPriceAX96);
- console2.log("sqrtPriceBX96", sqrtPriceBX96);
- (loanAmount0, loanAmount1, liquidity) = foil.getTokenAmounts(
- epochId,
- collateralAmount,
- sqrtPriceX96,
- sqrtPriceAX96,
- sqrtPriceBX96
- );
- }
-
- function logPositionAndAccount(uint256 positionId) public {
- Position.Data memory position = foil.getPosition(positionId);
- console2.log(" >>> Position", positionId);
- console2.log(" >>> Ids");
- console2.log(" >> tokenId : ", position.tokenId);
- console2.log(" >> epochId : ", position.epochId);
- // console2.log(" >> kind : ", position.kind);
- console2.log(" >>> Accounting data (debt and deposited collateral)");
- console2.log(
- " >> depositedCollateralAmount : ",
- position.depositedCollateralAmount
- );
- console2.log(" >> borrowedVEth : ", position.borrowedVEth);
- console2.log(" >> borrowedVGas : ", position.borrowedVGas);
- console2.log(" >>> Position data (owned tokens and position size)");
- console2.log(" >> vEthAmount : ", position.vEthAmount);
- console2.log(" >> vGasAmount : ", position.vGasAmount);
- console2.log(
- " >> currentTokenAmount: ",
- position.currentTokenAmount
- );
+ logPositionAndAccount(foil, positionId_4);
}
}
diff --git a/packages/protocol/test/FoilTradeTestHelper.sol b/packages/protocol/test/FoilTradeTestHelper.sol
new file mode 100644
index 00000000..a8953ac6
--- /dev/null
+++ b/packages/protocol/test/FoilTradeTestHelper.sol
@@ -0,0 +1,204 @@
+// SPDX-License-Identifier: MIT
+pragma solidity >=0.8.2 <0.9.0;
+
+import "forge-std/Test.sol";
+
+import {TickMath} from "../src/contracts/external/univ3/TickMath.sol";
+import {IFoil} from "../src/contracts/interfaces/IFoil.sol";
+import {IFoilStructs} from "../src/contracts/interfaces/IFoilStructs.sol";
+import {Position} from "../src/contracts/storage/Position.sol";
+import {IMintableToken} from "../src/contracts/external/IMintableToken.sol";
+
+import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
+
+abstract contract FoilTradeTestHelper is Test {
+ struct StateData {
+ uint256 userCollateral;
+ uint256 foilCollateral;
+ uint256 borrowedVEth;
+ uint256 borrowedVGas;
+ uint256 vEthAmount;
+ uint256 vGasAmount;
+ int256 currentTokenAmount; // position size
+ uint256 depositedCollateralAmount;
+ }
+
+ function getTokenAmountsForCollateralAmount(
+ IFoil foil,
+ address pool,
+ uint256 epochId,
+ uint256 collateralAmount,
+ int24 lowerTick,
+ int24 upperTick
+ )
+ public
+ view
+ returns (uint256 loanAmount0, uint256 loanAmount1, uint256 liquidity)
+ {
+ (uint160 sqrtPriceX96, , , , , , ) = IUniswapV3Pool(pool).slot0();
+
+ uint160 sqrtPriceAX96 = uint160(TickMath.getSqrtRatioAtTick(lowerTick));
+ uint160 sqrtPriceBX96 = uint160(TickMath.getSqrtRatioAtTick(upperTick));
+
+ (loanAmount0, loanAmount1, liquidity) = foil.getTokenAmounts(
+ epochId,
+ collateralAmount,
+ sqrtPriceX96,
+ sqrtPriceAX96,
+ sqrtPriceBX96
+ );
+ }
+
+ function fillPositionState(
+ IFoil foil,
+ uint256 positionId,
+ StateData memory stateData
+ ) public {
+ Position.Data memory position = foil.getPosition(positionId);
+ stateData.depositedCollateralAmount = position
+ .depositedCollateralAmount;
+ stateData.vEthAmount = position.vEthAmount;
+ stateData.vGasAmount = position.vGasAmount;
+ stateData.currentTokenAmount = position.currentTokenAmount;
+ stateData.borrowedVEth = position.borrowedVEth;
+ stateData.borrowedVGas = position.borrowedVGas;
+ }
+
+ function fillCollateralStateData(
+ IFoil foil,
+ IMintableToken collateralAsset,
+ StateData memory stateData
+ ) public {
+ stateData.userCollateral = collateralAsset.balanceOf(address(this));
+ stateData.foilCollateral = collateralAsset.balanceOf(address(foil));
+ }
+
+ function assertPosition(
+ IFoil foil,
+ uint256 positionId,
+ IMintableToken collateralAsset,
+ StateData memory expectedStateData,
+ string memory stage
+ ) public returns (StateData memory currentStateData) {
+ fillCollateralStateData(foil, collateralAsset, currentStateData);
+ fillPositionState(foil, positionId, currentStateData);
+
+ assertApproxEqRel(
+ currentStateData.userCollateral,
+ expectedStateData.userCollateral,
+ 0.0000001 ether,
+ string.concat(stage, " userCollateral")
+ );
+ assertApproxEqRel(
+ currentStateData.foilCollateral,
+ expectedStateData.foilCollateral,
+ 0.0000001 ether,
+ string.concat(stage, " foilCollateral")
+ );
+ assertEq(
+ currentStateData.depositedCollateralAmount,
+ expectedStateData.depositedCollateralAmount,
+ string.concat(stage, " depositedCollateralAmount")
+ );
+ assertApproxEqRel(
+ currentStateData.currentTokenAmount,
+ expectedStateData.currentTokenAmount,
+ 0.01 ether,
+ string.concat(stage, " currentTokenAmount")
+ );
+ assertApproxEqRel(
+ currentStateData.vEthAmount,
+ expectedStateData.vEthAmount,
+ 0.01 ether,
+ string.concat(stage, " vEthAmount")
+ );
+ assertApproxEqRel(
+ currentStateData.vGasAmount,
+ expectedStateData.vGasAmount,
+ 0.01 ether,
+ string.concat(stage, " vGasAmount")
+ );
+ assertApproxEqRel(
+ currentStateData.borrowedVEth,
+ expectedStateData.borrowedVEth,
+ 0.01 ether,
+ string.concat(stage, " borrowedVEth")
+ );
+ assertApproxEqRel(
+ currentStateData.borrowedVGas,
+ expectedStateData.borrowedVGas,
+ 0.01 ether,
+ string.concat(stage, " borrowedVGas")
+ );
+ }
+
+ function logPositionAndAccount(IFoil foil, uint256 positionId) public {
+ Position.Data memory position = foil.getPosition(positionId);
+ console2.log(" >>> Position", positionId);
+ console2.log(" >>> Ids");
+ console2.log(" >> tokenId : ", position.tokenId);
+ console2.log(" >> epochId : ", position.epochId);
+ // console2.log(" >> kind : ", position.kind);
+ console2.log(" >>> Accounting data (debt and deposited collateral)");
+ console2.log(
+ " >> depositedCollateralAmount : ",
+ position.depositedCollateralAmount
+ );
+ console2.log(" >> borrowedVEth : ", position.borrowedVEth);
+ console2.log(" >> borrowedVGas : ", position.borrowedVGas);
+ console2.log(" >>> Position data (owned tokens and position size)");
+ console2.log(" >> vEthAmount : ", position.vEthAmount);
+ console2.log(" >> vGasAmount : ", position.vGasAmount);
+ console2.log(
+ " >> currentTokenAmount: ",
+ position.currentTokenAmount
+ );
+ }
+
+ function addLiquidity(
+ IFoil foil,
+ address pool,
+ uint256 epochId,
+ uint256 collateralRequired
+ ) internal {
+ int24 lowerTick = 12200;
+ int24 upperTick = 12400;
+
+ (
+ uint256 amountTokenA,
+ uint256 amountTokenB,
+
+ ) = getTokenAmountsForCollateralAmount(
+ foil,
+ pool,
+ epochId,
+ collateralRequired,
+ lowerTick,
+ upperTick
+ );
+
+ IFoilStructs.LiquidityPositionParams memory params = IFoilStructs
+ .LiquidityPositionParams({
+ epochId: epochId,
+ amountTokenA: amountTokenA,
+ amountTokenB: amountTokenB,
+ collateralAmount: collateralRequired,
+ lowerTick: lowerTick,
+ upperTick: upperTick,
+ minAmountTokenA: 0,
+ minAmountTokenB: 0
+ });
+
+ foil.createLiquidityPosition(params);
+ }
+
+ function addPreTrade(
+ IFoil foil,
+ uint256 epochId,
+ uint256 collateral
+ ) internal {
+ int256 positionSize = int256(collateral / 100);
+
+ foil.createTraderPosition(epochId, collateral, positionSize, 0);
+ }
+}