Skip to content

Commit

Permalink
Merge pull request #73 from Secured-Finance/SF-962
Browse files Browse the repository at this point in the history
feat: add transfer entity and deposit amount fields in user [SF-962]
  • Loading branch information
NetFreak26 authored Dec 17, 2023
2 parents 9357b88 + 3dba124 commit 6ce64dc
Show file tree
Hide file tree
Showing 7 changed files with 298 additions and 28 deletions.
25 changes: 25 additions & 0 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ type User @entity {
orders: [Order!]! @derivedFrom(field: "maker")
liquidationCount: BigInt!
liquidations: [Liquidation!]! @derivedFrom(field: "user")
transferCount: BigInt!
transfers: [Transfer!]! @derivedFrom(field: "user")
depositETH: BigInt!
depositUSDC: BigInt!
depositWBTC: BigInt!
depositWFIL: BigInt!
depositAUSDC: BigInt!
depositAXLFIL: BigInt!
}

type DailyVolume @entity {
Expand Down Expand Up @@ -116,3 +124,20 @@ type Liquidation @entity {
blockNumber: BigInt!
txHash: Bytes!
}

enum TransferType {
Deposit
Withdraw
}

type Transfer @entity {
id: ID!
user: User!
currency: Bytes!
amount: BigInt!
transferType: TransferType!

timestamp: BigInt!
blockNumber: BigInt!
txHash: Bytes!
}
52 changes: 52 additions & 0 deletions src/helper/initializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
Transaction,
User,
Liquidation,
Transfer,
} from '../../generated/schema';
import { getDailyVolumeEntityId } from '../utils/id-generation';
import { buildLendingMarketId } from '../utils/string';
Expand Down Expand Up @@ -61,6 +62,13 @@ export const getOrInitUser = (address: Bytes, createdAt: BigInt): User => {
user.transactionCount = BigInt.fromI32(0);
user.orderCount = BigInt.fromI32(0);
user.liquidationCount = BigInt.fromI32(0);
user.transferCount = BigInt.fromI32(0);
user.depositETH = BigInt.fromI32(0);
user.depositUSDC = BigInt.fromI32(0);
user.depositWBTC = BigInt.fromI32(0);
user.depositWFIL = BigInt.fromI32(0);
user.depositAUSDC = BigInt.fromI32(0);
user.depositAXLFIL = BigInt.fromI32(0);
user.createdAt = createdAt;
user.save();

Expand Down Expand Up @@ -211,3 +219,47 @@ export const initLiquidation = (
user.liquidationCount = user.liquidationCount.plus(BigInt.fromI32(1));
user.save();
};

export const initTransfer = (
id: string,
userAddress: Address,
currency: Bytes,
amount: BigInt,
transferType: string,
timestamp: BigInt,
blockNumber: BigInt,
txHash: Bytes
): void => {
const transfer = new Transfer(id);

const user = getOrInitUser(userAddress, timestamp);

transfer.user = user.id;
transfer.currency = currency;
transfer.amount = amount;
transfer.transferType = transferType;
transfer.timestamp = timestamp;
transfer.blockNumber = blockNumber;
transfer.txHash = txHash;
transfer.save();

const currencyString = currency.toString();

if (currencyString == 'ETH') {
user.depositETH = user.depositETH.plus(amount);
} else if (currencyString == 'USDC') {
user.depositUSDC = user.depositUSDC.plus(amount);
} else if (currencyString == 'WBTC') {
user.depositWBTC = user.depositWBTC.plus(amount);
} else if (currencyString == 'WFIL') {
user.depositWFIL = user.depositWFIL.plus(amount);
} else if (currencyString == 'aUSDC') {
user.depositAUSDC = user.depositAUSDC.plus(amount);
} else if (currencyString == 'axlFIL') {
user.depositAXLFIL = user.depositAXLFIL.plus(amount);
} else {
log.error('Invalid currency: ', currencyString.split(','));
}
user.transferCount = user.transferCount.plus(BigInt.fromI32(1));
user.save();
};
32 changes: 29 additions & 3 deletions src/token-vault.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,32 @@
import { Deposit } from '../generated/TokenVault/TokenVault';
import { getOrInitUser } from './helper/initializer';
import { Deposit, Withdraw } from '../generated/TokenVault/TokenVault';
import { initTransfer } from './helper/initializer';

export function handleDeposit(event: Deposit): void {
getOrInitUser(event.params.user, event.block.timestamp);
const id =
event.transaction.hash.toHexString() + ':' + event.logIndex.toString();
initTransfer(
id,
event.params.user,
event.params.ccy,
event.params.amount,
'Deposit',
event.block.timestamp,
event.block.number,
event.transaction.hash
);
}

export function handleWithdraw(event: Withdraw): void {
const id =
event.transaction.hash.toHexString() + ':' + event.logIndex.toString();
initTransfer(
id,
event.params.user,
event.params.ccy,
event.params.amount,
'Withdraw',
event.block.timestamp,
event.block.number,
event.transaction.hash
);
}
2 changes: 2 additions & 0 deletions subgraph.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ dataSources:
eventHandlers:
- event: Deposit(indexed address,bytes32,uint256)
handler: handleDeposit
- event: Withdraw(indexed address,bytes32,uint256)
handler: handleWithdraw
file: ./src/token-vault.ts
- kind: ethereum
name: FundManagementLogic
Expand Down
1 change: 1 addition & 0 deletions test/mocks/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './lending-controller';
export * from './lending-market';
export * from './liquidation';
export * from './token-vault';
70 changes: 70 additions & 0 deletions test/mocks/token-vault.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/* eslint-disable @typescript-eslint/ban-types */
import { Address, BigInt, Bytes, ethereum } from '@graphprotocol/graph-ts';
import { newMockEvent } from 'matchstick-as/assembly/index';
import { Deposit, Withdraw } from '../../generated/TokenVault/TokenVault';

export function createDepositEvent(
user: Address,
ccy: Bytes,
amount: BigInt
): Deposit {
const mockEvent = newMockEvent();
const event = new Deposit(
mockEvent.address,
mockEvent.logIndex,
mockEvent.transactionLogIndex,
mockEvent.logType,
mockEvent.block,
mockEvent.transaction,
mockEvent.parameters,
mockEvent.receipt
);

event.parameters = new Array();
event.parameters.push(
new ethereum.EventParam('user', ethereum.Value.fromAddress(user))
);
event.parameters.push(
new ethereum.EventParam('ccy', ethereum.Value.fromBytes(ccy))
);
event.parameters.push(
new ethereum.EventParam(
'amount',
ethereum.Value.fromUnsignedBigInt(amount)
)
);
return event;
}

export function createWithdrawEvent(
user: Address,
ccy: Bytes,
amount: BigInt
): Withdraw {
const mockEvent = newMockEvent();
const event = new Withdraw(
mockEvent.address,
mockEvent.logIndex,
mockEvent.transactionLogIndex,
mockEvent.logType,
mockEvent.block,
mockEvent.transaction,
mockEvent.parameters,
mockEvent.receipt
);

event.parameters = new Array();
event.parameters.push(
new ethereum.EventParam('user', ethereum.Value.fromAddress(user))
);
event.parameters.push(
new ethereum.EventParam('ccy', ethereum.Value.fromBytes(ccy))
);
event.parameters.push(
new ethereum.EventParam(
'amount',
ethereum.Value.fromUnsignedBigInt(amount)
)
);
return event;
}
144 changes: 119 additions & 25 deletions test/token-vault.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Address, ethereum } from '@graphprotocol/graph-ts';
import { Address, BigInt, ethereum } from '@graphprotocol/graph-ts';
import {
afterEach,
assert,
Expand All @@ -8,42 +8,136 @@ import {
test,
} from 'matchstick-as/assembly/index';
import { Deposit } from '../generated/TokenVault/TokenVault';
import { handleDeposit } from '../src/token-vault';
import { toBytes20 } from '../src/utils/string';
import { handleDeposit, handleWithdraw } from '../src/token-vault';
import { toBytes32, toBytes20 } from '../src/utils/string';
import { ALICE, BOB } from './utils/createEntities';
import { createDepositEvent, createWithdrawEvent } from './mocks';

describe('User Entity', () => {
const ccy = toBytes32('ETH');

describe('Deposit & Withdraw', () => {
afterEach(() => {
clearStore();
});

test('Should create an user when the Deposit Event is raised', () => {
const userWallet = '0x0000000000000000000000000000000000000000';
const mockEvent = changetype<Deposit>(newMockEvent());
mockEvent.parameters.push(
new ethereum.EventParam(
'user',
ethereum.Value.fromAddress(Address.fromString(userWallet))
)
);
handleDeposit(mockEvent);
assert.fieldEquals('User', userWallet, 'id', userWallet);
test('Should create an user and a transfer when the Deposit Event is raised', () => {
const amount = BigInt.fromI32(10000000);
const event = createDepositEvent(ALICE, ccy, amount);

const id =
event.transaction.hash.toHexString() +
':' +
event.logIndex.toString();

handleDeposit(event);

assert.entityCount('User', 1);
assert.fieldEquals('Protocol', 'ethereum', 'totalUsers', '1');
assert.fieldEquals('User', ALICE.toHexString(), 'transferCount', '1');
assert.fieldEquals(
'User',
ALICE.toHexString(),
'depositETH',
amount.toString()
);

assert.entityCount('Transfer', 1);
assert.fieldEquals('Transfer', id, 'user', ALICE.toHexString());
assert.fieldEquals('Transfer', id, 'currency', ccy.toHexString());
assert.fieldEquals('Transfer', id, 'amount', amount.toString());
assert.fieldEquals('Transfer', id, 'transferType', 'Deposit');

assert.stringEquals(ccy.toString(), 'ETH');
});

test('Should increment the totalUsers when a new user is created', () => {
test('Should increment the totalUsers when a new user deposits', () => {
const amount = BigInt.fromI32(10000000);
for (let i = 0; i < 100; i++) {
const mockEvent = changetype<Deposit>(newMockEvent());
mockEvent.parameters.push(
new ethereum.EventParam(
'user',
ethereum.Value.fromAddress(
Address.fromBytes(toBytes20(i.toString()))
)
)
const event = createDepositEvent(
Address.fromBytes(toBytes20(i.toString())),
ccy,
amount
);
handleDeposit(mockEvent);
event.logIndex = BigInt.fromI32(i);
handleDeposit(event);
}
assert.entityCount('User', 100);
assert.fieldEquals('Protocol', 'ethereum', 'totalUsers', '100');
assert.entityCount('Transfer', 100);
});

test('Should increment the transferCount when same user deposits', () => {
const amount = BigInt.fromI32(10000000);
for (let i = 0; i < 10; i++) {
const event = createDepositEvent(ALICE, ccy, amount);
event.logIndex = BigInt.fromI32(i);
handleDeposit(event);
}
assert.entityCount('User', 1);
assert.fieldEquals('Protocol', 'ethereum', 'totalUsers', '1');
assert.fieldEquals('User', ALICE.toHexString(), 'transferCount', '10');
assert.entityCount('Transfer', 10);
});

test('Should increment the transferCount and add a transfer when user withdraws', () => {
const amount = BigInt.fromI32(10000000);
const depositEvent = createDepositEvent(ALICE, ccy, amount);
handleDeposit(depositEvent);

const withdrawEvent = createWithdrawEvent(ALICE, ccy, amount);
withdrawEvent.logIndex = BigInt.fromI32(2);
handleWithdraw(withdrawEvent);

const id =
withdrawEvent.transaction.hash.toHexString() +
':' +
withdrawEvent.logIndex.toString();

assert.entityCount('User', 1);
assert.fieldEquals('Protocol', 'ethereum', 'totalUsers', '1');
assert.fieldEquals('User', ALICE.toHexString(), 'transferCount', '2');

assert.entityCount('Transfer', 2);
assert.fieldEquals('Transfer', id, 'user', ALICE.toHexString());
assert.fieldEquals('Transfer', id, 'currency', ccy.toHexString());
assert.fieldEquals('Transfer', id, 'amount', amount.toString());
assert.fieldEquals('Transfer', id, 'transferType', 'Withdraw');
});

test('Should increment the deposit amount for each currency', () => {
const amount1 = BigInt.fromI32(10000000);
const amount2 = BigInt.fromI32(20000000);
const amount3 = BigInt.fromI32(50000000);
const wfil = toBytes32('WFIL');
const usdc = toBytes32('USDC');
const axlFIL = toBytes32('axlFIL');

const event1 = createDepositEvent(ALICE, wfil, amount1);
const event2 = createDepositEvent(ALICE, usdc, amount2);
const event3 = createDepositEvent(ALICE, axlFIL, amount3);

handleDeposit(event1);
handleDeposit(event2);
handleDeposit(event3);

assert.fieldEquals('User', ALICE.toHexString(), 'depositETH', '0');
assert.fieldEquals(
'User',
ALICE.toHexString(),
'depositWFIL',
amount1.toString()
);
assert.fieldEquals(
'User',
ALICE.toHexString(),
'depositUSDC',
amount2.toString()
);
assert.fieldEquals(
'User',
ALICE.toHexString(),
'depositAXLFIL',
amount3.toString()
);
});
});

0 comments on commit 6ce64dc

Please sign in to comment.