Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Matchstick Tests #56

Open
wants to merge 16 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@ generated/
node_modules/
.env
subgraph.yaml
tests/.bin
tests/.latest.json
tests/*/.bin
tests/*/.latest.json
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ There is also a GP v1 subgraph here: https://github.com/gnosis/dex-subgraph

## Model

Further information about the model [here](./model.md)
Further information about the model [here](./docs/model.md)

## Setup of your own test subgraph

Expand Down Expand Up @@ -55,3 +55,13 @@ yarn deploy
If everything went well you'll have a copy of this subgraph running on your hosted service account indexing your desired network.

Please notice a subgraph can only index a single network, if you want to index another network you should create a new subgraph and do same steps starting from step 3.

## Tests

For running all tests execute:

```bash
yarn test
```

Further information about creating tests, tests organization and running them [here](./docs/tests.md)
File renamed without changes.
39 changes: 39 additions & 0 deletions docs/tests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Tests explained

### About test framework

We are using **matchstick** for testing subgraphs. For being able to run the matchstick tests you need to install *postgresql*. You can find more information [here](https://thegraph.com/docs/en/developing/unit-testing-framework/)

### About folder organization

There are 3 different folders for organizing the tests:

1. **gpv2settlement:** this folder will contain tests related to settlement contract. It doesn't matter where it's deployed which code will be execueted in that case

2. **gc:** this folder will contain tests related to price calculation that's being done on Gnosis Chain only. At the moment we are using honeyswap (Uniswap v2 pools) to estimate prices.

3. **mainnet:** this folder will containt tests related to price calculation in mainnet. In mainnet Uniswap v3 is being indexed, it's pools and tokens.

Inside each folder we will put the tests replicating `src` folder structure of what the test aim to test.
It's important to notice all files should contain .test. string on it's name.
There's also a `utils.js` file that will contain utilities and helpers for creating entities or mocks that are common to different tests.

### About writting tests

All files will contain different `describe` functions nested and a `test` function at the end:

``` Javascript
describe(FileName, () => { // FileName will be replaced by it's file name
describe(FunctionName () => { // FunctionName will be replaced by it's function name
describe(SetupExpectations, () => { // SetupExpectations is the stage we need to build to make possible the test to run
test(WhatAreWeTesting, () => { // Here we will name the test using what's the result we are waiting after test is executed.
```

### About test running.

- `yarn test`: running this command all tests on folder `tests` will be run.
- `yarn test:env`: running this .env variable will be read to run that sepcific set of tests
- `yarn test:gc`: we filter the tests to the gc folder only
- `yarn test:mainnet`: we filter the tests to the mainnet folder only
- `yarn test:cow`: runing this we filter by gpv2settlement contract only.

8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
"scripts": {
"codegen": "graph codegen",
"build": "graph build",
"test": "graph test",
"test:env": "node src/scripts/test.js",
"test:gc": "graph test gc",
"test:mainnet": "graph test mainnet",
"test:cow": "graph test gpv2settlement",
"deploy": "node src/scripts/deploy.js",
"deploy:mainnet": "cross-env NETWORK=mainnet SUBGRAPH=gnosis/cow yarn deploy",
"deploy:rinkeby": "cross-env NETWORK=rinkeby SUBGRAPH=gnosis/cow-rinkeby yarn deploy",
Expand All @@ -17,6 +22,7 @@
"devDependencies": {
"chalk": "^2.4.1",
"cross-env": "^7.0.3",
"dotenv": "^16.0.0"
"dotenv": "^16.0.0",
"matchstick-as": "^0.5.0"
}
}
7 changes: 7 additions & 0 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,8 @@ type Total @entity {
feesUsd: BigDecimal
"Total fees in Eth"
feesEth: BigDecimal
"times settle function was called"
settleCalls: BigInt!
}

type DailyTotal @entity {
Expand Down Expand Up @@ -405,3 +407,8 @@ type PairHourly @entity {
"Total volume in Usd"
volumeTradedUsd: BigDecimal
}

type Receiver @entity {
id: ID!
address: Bytes!
}
42 changes: 39 additions & 3 deletions src/mapping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ import {
OrderInvalidated,
PreSignature,
Settlement,
Trade
Trade,
SettleCall
} from "../generated/GPV2Settlement/GPV2Settlement"
import { tokens, trades, orders, users } from "./modules"
import { tokens, trades, orders, users, totals } from "./modules"
import { getPrices } from "./utils/getPrices"
import { MINUS_ONE_BD } from "./utils/constants"
import { BigDecimal, BigInt, dataSource } from "@graphprotocol/graph-ts"
import { convertTokenToDecimal } from "./utils"
import { UniswapToken } from "../generated/schema"
import { Receiver, UniswapToken } from "../generated/schema"

export function handleInteraction(event: Interaction): void { }

Expand Down Expand Up @@ -138,3 +139,38 @@ export function handleTrade(event: Trade): void {
order.save()

}

export function handleSettle(call: SettleCall): void {

// function: settle(
// address[], <- Tokens
// uint256[], <- clearingPrices
// (uint256,uint256,address,uint256,uint256,uint32,bytes32,uint256,uint256,uint256,bytes)[], <- Trades
// uint256, sellTokenIndex
// uint256, buyTokenIndex
// address, receiver
// uint256, sellAmount
// uint256, buyAmount
// uint32, validTo
// bytes32, appData
// uint256, feeAmount
// uint256, flags
// uint256, executedAmount
// bytes) signature
// (address,uint256,bytes)[] <- interactions
// )

// Need to create trade id to bind settle with the trade
// tradeId => orderId + "|" + txHashString + "|" + eventIndex // think on a fix changing tradeId

totals.addSettleCall()

let trades = call.inputs.trades

for(let i = 0; trades.length > i; i++) {
let receiverAddress = trades[i].receiver
let receiver = new Receiver(receiverAddress.toHexString())
receiver.address = receiverAddress
receiver.save()
}
}
8 changes: 5 additions & 3 deletions src/modules/settlements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { getEthPriceInUSD } from "../utils/pricing"

export namespace settlements {

export function getOrCreateSettlement(txHash: Bytes, tradeTimestamp: i32, solver: Address, txGasPrice: BigInt, feeAmountUsd: BigDecimal): void {
export function getOrCreateSettlement(txHash: Bytes, tradeTimestamp: i32, solver: Address, txGasPrice: BigInt, feeAmountUsd: BigDecimal | null): void {

let settlementId = txHash.toHexString()
let network = dataSource.network()
Expand Down Expand Up @@ -36,8 +36,10 @@ export namespace settlements {
settlement.profitability = ZERO_BD
totals.addSettlementCount(tradeTimestamp)
}
let prevFeeAmountUsd = settlement.aggregatedFeeAmountUsd
settlement.aggregatedFeeAmountUsd = prevFeeAmountUsd.plus(feeAmountUsd)
if(feeAmountUsd) {
let prevFeeAmountUsd = settlement.aggregatedFeeAmountUsd
settlement.aggregatedFeeAmountUsd = prevFeeAmountUsd.plus(feeAmountUsd)
}
settlement.profitability = settlement.aggregatedFeeAmountUsd.minus(settlement.txCostUsd)
settlement.save()
}
Expand Down
8 changes: 8 additions & 0 deletions src/modules/totals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,19 @@ export namespace totals {
total.feesEth = ZERO_BD
total.feesUsd = ZERO_BD
total.numberOfTrades = ZERO_BI
total.settleCalls = ZERO_BI
}

return total as Total
}

export function addSettleCall(): void {
let totals = getOrCreateTotals()
let prevSettleCalls = totals.settleCalls
totals.settleCalls = prevSettleCalls.plus(ONE_BI);
totals.save()
}

function getOrCreateDailyTotals(timestamp: i32): DailyTotal {
let totalId = timestamp.toString()
let total = DailyTotal.load(totalId)
Expand Down
4 changes: 2 additions & 2 deletions src/modules/trades.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ export namespace trades {
let buyTokenId = buyToken.id
let sellTokenId = sellToken.id

let buyTokenPriceUsd = buyToken.priceUsd as BigDecimal
let sellTokenPriceUsd = sellToken.priceUsd as BigDecimal
let buyTokenPriceUsd = _buyTokenPriceUsd ? _buyTokenPriceUsd as BigDecimal : null
let sellTokenPriceUsd = _sellTokenPriceUsd ? _sellTokenPriceUsd as BigDecimal : null

tokens.createTokenTradingEvent(timestamp, buyTokenId, tradeId, buyAmount, buyAmountEth, buyAmountUsd, buyTokenPriceUsd)
tokens.createTokenTradingEvent(timestamp, sellTokenId, tradeId, sellAmount, sellAmountEth, sellAmountUsd, sellTokenPriceUsd)
Expand Down
11 changes: 11 additions & 0 deletions src/scripts/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const { exec } = require('child_process')
const { series } = require('async')
require('dotenv').config()

const network = process.env.NETWORK

series([
(callback) => exec(`mustache config/${network}.json subgraph.yaml.mustache > subgraph.yaml`, null, callback),
(callback) => exec(`yarn codegen`, null, callback),
() => exec(`yarn test:${network} && yarn test:cow`, null, (_err, stdout, _stderr) => { console.log(stdout) }),
])
1 change: 1 addition & 0 deletions src/uniswapMappings/uniswapPools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export function handleInitialize(event: Initialize): void {
let pool = UniswapPool.load(event.address.toHexString())
if (pool) {
pool.tick = BigInt.fromI32(event.params.tick)
pool.save()
}

let token0Id = pool ? pool.token0 : null
Expand Down
4 changes: 2 additions & 2 deletions src/utils/getPrices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ function getUniswapPricesForPair(token0: Address, token1: Address, isEthPriceCal
let reservesTry = pair.try_getReserves()
let reserves = reservesTry.reverted ? EMPTY_RESERVES_RESULT : reservesTry.value
let pairToken0Try = pair.try_token0()
let pairToken0 = pairToken0Try.reverted ? ZERO_ADDRESS as Address : pairToken0Try.value
let pairToken0 = pairToken0Try.reverted ? changetype<Address>(ZERO_ADDRESS) : pairToken0Try.value
let pairToken1Try = pair.try_token1()
let pairToken1 = pairToken1Try.reverted ? ZERO_ADDRESS as Address : pairToken1Try.value
let pairToken1 = pairToken1Try.reverted ? changetype<Address>(ZERO_ADDRESS) : pairToken1Try.value

if (reserves.value0 == ZERO_BI ||
reserves.value1 == ZERO_BI ||
Expand Down
3 changes: 3 additions & 0 deletions subgraph.yaml.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ dataSources:
handler: handleSettlement
- event: Trade(indexed address,address,address,uint256,uint256,uint256,bytes)
handler: handleTrade
callHandlers:
- function: settle(address[],uint256[],(uint256,uint256,address,uint256,uint256,uint32,bytes32,uint256,uint256,uint256,bytes)[],(address,uint256,bytes)[])
handler: handleSettle
file: ./src/mapping.ts
{{#uniV3Factory}}
- kind: ethereum/contract
Expand Down
123 changes: 123 additions & 0 deletions tests/gc/mapping.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { Address, BigInt, Bytes } from "@graphprotocol/graph-ts";
import {
afterEach,
assert,
clearStore,
describe,
test,
dataSourceMock,
} from "matchstick-as";
import { handleTrade } from "../../src/mapping";
import {
STABLECOIN_ADDRESS_GC,
UNISWAP_FACTORY,
WETH_ADDRESS_GC,
} from "../../src/utils/constants";
import {
mockErc20,
mockUniswapFactoryGetPair,
mockUniswapV2Pair,
createTradeEvent,
} from "./utils";

describe("Mapping", () => {
afterEach(() => {
clearStore();
});

describe("handleTrade", () => {
describe("when sellToken is a stablecoin", () => {
test("GetPrice should return one dollar price", () => {
let owner = Address.fromString(
"0x0000000000000000000000000000000000000001"
);
let sellToken = STABLECOIN_ADDRESS_GC
let buyToken = Address.fromString(
"0x0000000000000000000000000000000000000020"
);

let sellAmount = BigInt.fromI32(100);
let buyAmount = BigInt.fromI32(50);
let feeAmount = BigInt.fromI32(5);

let orderUid = Bytes.fromHexString(
"0x0000000000000000000000000000000000000022"
);

dataSourceMock.setNetwork("xdai");

mockErc20(sellToken, "DAI", "DAI", BigInt.fromI32(6));
mockErc20(buyToken, "Token 2", "TK2", BigInt.fromI32(18));
mockErc20(WETH_ADDRESS_GC, "WETH", "WETH", BigInt.fromI32(6));
mockErc20(STABLECOIN_ADDRESS_GC, "DAI", "DAI", BigInt.fromI32(6));

let pair = Address.fromString(
"0x0000000000000000000000000000000000000100"
);

let pair2 = Address.fromString(
"0x0000000000000000000000000000000000000101"
);

let pair3 = Address.fromString(
"0x0000000000000000000000000000000000000102"
);

mockUniswapFactoryGetPair(
UNISWAP_FACTORY,
[sellToken, WETH_ADDRESS_GC],
pair
);

mockUniswapV2Pair(
pair,
[BigInt.fromI32(100), BigInt.fromI32(100), BigInt.fromI32(100)],
sellToken,
WETH_ADDRESS_GC
);

mockUniswapFactoryGetPair(
UNISWAP_FACTORY,
[WETH_ADDRESS_GC, STABLECOIN_ADDRESS_GC],
pair2
);

mockUniswapV2Pair(
pair2,
[BigInt.fromI32(50), BigInt.fromI32(50), BigInt.fromI32(50)],
WETH_ADDRESS_GC,
STABLECOIN_ADDRESS_GC
);

mockUniswapFactoryGetPair(
UNISWAP_FACTORY,
[buyToken, WETH_ADDRESS_GC],
pair3
);

mockUniswapV2Pair(
pair3,
[BigInt.fromI32(10), BigInt.fromI32(10), BigInt.fromI32(10)],
buyToken,
WETH_ADDRESS_GC
);

let event = createTradeEvent(
owner,
sellToken,
buyToken,
sellAmount,
buyAmount,
feeAmount,
orderUid
);

handleTrade(event);

let sellTokenId = sellToken.toHexString();

assert.fieldEquals("Token", sellTokenId, "priceUsd", "1");
});
});
});
});
Loading