Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions .github/workflows/deploy-subgraph.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ jobs:

- run: >
(cd lib/rain.interpreter/lib/rain.interpreter.interface/lib/rain.math.float && nix develop -c rainix-sol-prelude)
- run: >
(nix develop -c bash -c "cd lib/rain.interpreter/lib/rain.interpreter.interface/lib/forge-std && forge build")

- run: nix develop -c bash -c rainix-sol-prelude

Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/test-subgraph.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ jobs:

- run: |
(cd lib/rain.interpreter/lib/rain.interpreter.interface/lib/rain.math.float && nix develop -c rainix-sol-prelude)
- run: >
(nix develop -c bash -c "cd lib/rain.interpreter/lib/rain.interpreter.interface/lib/forge-std && forge build")

- name: Build subgraph
run: nix develop -c subgraph-build
Expand Down
7 changes: 7 additions & 0 deletions subgraph/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ type Vault @entity {
balance: Bytes!
"All balance changes for this vault"
balanceChanges: [VaultBalanceChange!]! @derivedFrom(field: "vault")
"reference VaultList singleton record to derive the vaults list from"
vaultList: VaultList!
}

type VaultList @entity {
id: String!
vaults: [Vault!]! @derivedFrom(field: "vaultList") # list of all vaults
}

interface VaultBalanceChange {
Expand Down
6 changes: 6 additions & 0 deletions subgraph/src/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import {
} from "./clear";
import { createTransactionEntity } from "./transaction";
import { createOrderbookEntity } from "./orderbook";
import { ethereum } from "@graphprotocol/graph-ts";
import { handleVaultlessBalance } from "./vault";

export function handleDeposit(event: DepositV2): void {
createTransactionEntity(event);
Expand Down Expand Up @@ -70,3 +72,7 @@ export function handleAfterClear(event: AfterClearV2): void {
createOrderbookEntity(event);
_handleAfterClear(event);
}

export function vaultBlockHandler(_block: ethereum.Block): void {
handleVaultlessBalance();
}
103 changes: 98 additions & 5 deletions subgraph/src/vault.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { Bytes, crypto } from "@graphprotocol/graph-ts";
import { Vault } from "../generated/schema";
import { Address, Bytes, crypto, dataSource } from "@graphprotocol/graph-ts";
import { Vault, VaultList } from "../generated/schema";
import { getERC20Entity } from "./erc20";
import { Float, getCalculator } from "./float";
import { ethereum } from "@graphprotocol/graph-ts"
import { Multicall3 } from "../generated/OrderBook/Multicall3";

export const VAULT_LIST_ID = "SINGLETON";
export const MUTLICALL3_ADDRESS = "0xcA11bde05977b3631167028862bE2a173976CA11";
export const ZERO_BYTES_32 = "0x0000000000000000000000000000000000000000000000000000000000000000";

export type VaultId = Bytes;

Expand All @@ -21,14 +27,14 @@ export function createEmptyVault(
vaultId: VaultId,
token: Bytes
): Vault {
getVaultList(); // make sure vault list exists
let vault = new Vault(vaultEntityId(orderbook, owner, vaultId, token));
vault.orderbook = orderbook;
vault.vaultId = vaultId;
vault.token = getERC20Entity(token);
vault.owner = owner;
vault.balance = Bytes.fromHexString(
"0x0000000000000000000000000000000000000000000000000000000000000000"
);
vault.balance = Bytes.fromHexString(ZERO_BYTES_32);
vault.vaultList = VAULT_LIST_ID;
vault.save();
return vault;
}
Expand Down Expand Up @@ -70,3 +76,90 @@ export function handleVaultBalanceChange(
newVaultBalance: vault.balance,
};
}

export function getVaultList(): VaultList {
let vaultList = VaultList.load(VAULT_LIST_ID);
if (!vaultList) {
vaultList = new VaultList(VAULT_LIST_ID);
vaultList.save();
}
return vaultList;
}

// updates vaultless for all vautless vaults using multicall
// this is used in block handler and is updated at each block
export function handleVaultlessBalance(): void {
// Get the OrderBook and multicall3 contract instance
const orderBookAddress = dataSource.address()
const multicall3 = Multicall3.bind(Address.fromString(MUTLICALL3_ADDRESS));

// Load all vaults from the store
const BATCH_SIZE = 1000;
const vaultList = getVaultList().vaults.load();
const vaultlessVaultsBatch: Vault[][] = [[]];

for (let i = 0; i < vaultList.length; i++) {
const vault = vaultList[i];

// skip non vautless vaults
if (vault.vaultId.notEqual(Bytes.fromHexString(ZERO_BYTES_32))) continue;

if (vaultlessVaultsBatch[vaultlessVaultsBatch.length - 1].length < BATCH_SIZE) {
vaultlessVaultsBatch[vaultlessVaultsBatch.length - 1].push(vault)
} else {
vaultlessVaultsBatch.push([vault]);
}
}

// Batch calls using tryAggregate for better performance
const batchCalls: ethereum.Tuple[][] = [];
for (let i = 0; i < vaultlessVaultsBatch.length; i++) {
const vaultlessVaults = vaultlessVaultsBatch[i];
const calls: ethereum.Tuple[] = [];
for (let j = 0; j < vaultlessVaults.length; j++) {
let vault = vaultlessVaults[j];

// Encode vaultBalance2(address owner, address token, bytes32 vaultId) call
const callData = ethereum.encode(
ethereum.Value.fromTuple(
changetype<ethereum.Tuple>([
ethereum.Value.fromAddress(Address.fromBytes(vault.owner)),
ethereum.Value.fromAddress(Address.fromBytes(vault.token)),
ethereum.Value.fromFixedBytes(vault.vaultId)
])
)
)!;
const call3 = changetype<ethereum.Tuple>([
ethereum.Value.fromAddress(orderBookAddress),
ethereum.Value.fromBoolean(true), // allowFailure = true
ethereum.Value.fromBytes(callData)
]);
calls.push(call3);
}
batchCalls.push(calls);
}

for (let i = 0; i < batchCalls.length; i++) {
// Make multicall
const result = multicall3.tryCall(
"aggregate3",
"aggregate3((address,bool,bytes)[]):((bool,bytes)[])",
[ethereum.Value.fromTupleArray(batchCalls[i])]
);

if (result.reverted) continue;
const results = result.value[0].toTupleArray<ethereum.Tuple>();
if (results.length !== vaultlessVaultsBatch[i].length) continue;

for (let j = 0; j < vaultlessVaultsBatch[i].length; j++) {
const success = results[j][0].toBoolean();
const returnData = results[j][1].toBytes();
if (!success) continue;
const decoded = ethereum.decode('bytes32', returnData);
if (decoded) {
vaultlessVaultsBatch[i][j].balance = decoded.toBytes();
vaultlessVaultsBatch[i][j].save();
}
}
}
}
4 changes: 4 additions & 0 deletions subgraph/subgraph.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ dataSources:
file: ../out/ERC20.sol/ERC20.json
- name: DecimalFloat
file: ../lib/rain.interpreter/lib/rain.interpreter.interface/lib/rain.math.float/out/DecimalFloat.sol/DecimalFloat.json
- name: Multicall3
file: ../lib/rain.interpreter/lib/rain.interpreter.interface/lib/forge-std/out/IMulticall3.sol/IMulticall3.json
eventHandlers:
- event: DepositV2(address,address,bytes32,uint256)
handler: handleDeposit
Expand All @@ -41,4 +43,6 @@ dataSources:
handler: handleClear
- event: AfterClearV2(address,(bytes32,bytes32,bytes32,bytes32))
handler: handleAfterClear
blockHandlers:
- handler: vaultBlockHandler
file: ./src/handlers.ts
Loading
Loading