diff --git a/packages/examples/subgraph-notification/.gitignore b/packages/examples/subgraph-notification/.gitignore new file mode 100644 index 000000000..3c3629e64 --- /dev/null +++ b/packages/examples/subgraph-notification/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/packages/examples/subgraph-notification/README.md b/packages/examples/subgraph-notification/README.md new file mode 100644 index 000000000..5f98144e3 --- /dev/null +++ b/packages/examples/subgraph-notification/README.md @@ -0,0 +1,2 @@ +# push-notitifcations-on-subgraph-mainnet +Integrating Push Protocol notifications on to The Graph Network diff --git a/packages/examples/subgraph-notification/abis/EPNSCore.json b/packages/examples/subgraph-notification/abis/EPNSCore.json new file mode 100644 index 000000000..850552bd0 --- /dev/null +++ b/packages/examples/subgraph-notification/abis/EPNSCore.json @@ -0,0 +1,126 @@ +[ + { + "inputs": [ + { "internalType": "address", "name": "_logic", "type": "address" }, + { "internalType": "address", "name": "_governance", "type": "address" }, + { + "internalType": "address", + "name": "_pushChannelAdmin", + "type": "address" + }, + { + "internalType": "address", + "name": "_pushTokenAddress", + "type": "address" + }, + { "internalType": "address", "name": "_wethAddress", "type": "address" }, + { + "internalType": "address", + "name": "_uniswapRouterAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "_lendingPoolProviderAddress", + "type": "address" + }, + { "internalType": "address", "name": "_daiAddress", "type": "address" }, + { "internalType": "address", "name": "_aDaiAddress", "type": "address" }, + { "internalType": "uint256", "name": "_referralCode", "type": "uint256" } + ], + "stateMutability": "payable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "previousAdmin", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "AdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { "stateMutability": "payable", "type": "fallback" }, + { + "inputs": [], + "name": "admin", + "outputs": [ + { "internalType": "address", "name": "admin_", "type": "address" } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "newAdmin", "type": "address" } + ], + "name": "changeAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "implementation", + "outputs": [ + { + "internalType": "address", + "name": "implementation_", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + } + ], + "name": "upgradeTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { "internalType": "bytes", "name": "data", "type": "bytes" } + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { "stateMutability": "payable", "type": "receive" } +] diff --git a/packages/examples/subgraph-notification/abis/PushToken.json b/packages/examples/subgraph-notification/abis/PushToken.json new file mode 100644 index 000000000..bf76cd649 --- /dev/null +++ b/packages/examples/subgraph-notification/abis/PushToken.json @@ -0,0 +1,193 @@ +[ + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [{ "name": "", "type": "string" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "spender", "type": "address" }, + { "name": "tokens", "type": "uint256" } + ], + "name": "approve", + "outputs": [{ "name": "success", "type": "bool" }], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "from", "type": "address" }, + { "name": "to", "type": "address" }, + { "name": "tokens", "type": "uint256" } + ], + "name": "transferFrom", + "outputs": [{ "name": "success", "type": "bool" }], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [{ "name": "", "type": "uint8" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "_totalSupply", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [{ "name": "tokenOwner", "type": "address" }], + "name": "balanceOf", + "outputs": [{ "name": "balance", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [{ "name": "", "type": "string" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { "name": "a", "type": "uint256" }, + { "name": "b", "type": "uint256" } + ], + "name": "safeSub", + "outputs": [{ "name": "c", "type": "uint256" }], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "to", "type": "address" }, + { "name": "tokens", "type": "uint256" } + ], + "name": "transfer", + "outputs": [{ "name": "success", "type": "bool" }], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { "name": "a", "type": "uint256" }, + { "name": "b", "type": "uint256" } + ], + "name": "safeDiv", + "outputs": [{ "name": "c", "type": "uint256" }], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "spender", "type": "address" }, + { "name": "tokens", "type": "uint256" }, + { "name": "data", "type": "bytes" } + ], + "name": "approveAndCall", + "outputs": [{ "name": "success", "type": "bool" }], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { "name": "a", "type": "uint256" }, + { "name": "b", "type": "uint256" } + ], + "name": "safeMul", + "outputs": [{ "name": "c", "type": "uint256" }], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { "name": "tokenOwner", "type": "address" }, + { "name": "spender", "type": "address" } + ], + "name": "allowance", + "outputs": [{ "name": "remaining", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { "name": "a", "type": "uint256" }, + { "name": "b", "type": "uint256" } + ], + "name": "safeAdd", + "outputs": [{ "name": "c", "type": "uint256" }], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { "payable": true, "stateMutability": "payable", "type": "fallback" }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "name": "from", "type": "address" }, + { "indexed": true, "name": "to", "type": "address" }, + { "indexed": false, "name": "tokens", "type": "uint256" } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "name": "tokenOwner", "type": "address" }, + { "indexed": true, "name": "spender", "type": "address" }, + { "indexed": false, "name": "tokens", "type": "uint256" } + ], + "name": "Approval", + "type": "event" + } +] diff --git a/packages/examples/subgraph-notification/contracts/PushToken.sol b/packages/examples/subgraph-notification/contracts/PushToken.sol new file mode 100644 index 000000000..424a53444 --- /dev/null +++ b/packages/examples/subgraph-notification/contracts/PushToken.sol @@ -0,0 +1,113 @@ +pragma solidity ^0.4.24; + +//Safe Math Interface + +contract SafeMath { + + function safeAdd(uint a, uint b) public pure returns (uint c) { + c = a + b; + require(c >= a); + } + + function safeSub(uint a, uint b) public pure returns (uint c) { + require(b <= a); + c = a - b; + } + + function safeMul(uint a, uint b) public pure returns (uint c) { + c = a * b; + require(a == 0 || c / a == b); + } + + function safeDiv(uint a, uint b) public pure returns (uint c) { + require(b > 0); + c = a / b; + } +} + + +//ERC Token Standard #20 Interface + +contract ERC20Interface { + function totalSupply() public constant returns (uint); + function balanceOf(address tokenOwner) public constant returns (uint balance); + function allowance(address tokenOwner, address spender) public constant returns (uint remaining); + function transfer(address to, uint tokens) public returns (bool success); + function approve(address spender, uint tokens) public returns (bool success); + function transferFrom(address from, address to, uint tokens) public returns (bool success); + + event Transfer(address indexed from, address indexed to, uint tokens); + event Approval(address indexed tokenOwner, address indexed spender, uint tokens); +} + + +//Contract function to receive approval and execute function in one call + +contract ApproveAndCallFallBack { + function receiveApproval(address from, uint256 tokens, address token, bytes data) public; +} + +//Actual token contract + +contract PushToken is ERC20Interface, SafeMath { + string public symbol; + string public name; + uint8 public decimals; + uint public _totalSupply; + + mapping(address => uint) balances; + mapping(address => mapping(address => uint)) allowed; + + constructor() public { + symbol = "PUSH"; + name = "Push Token"; + decimals = 2; + _totalSupply = 100000; + balances[msg.sender] = _totalSupply; + emit Transfer(address(0), msg.sender, _totalSupply); + } + + function totalSupply() public constant returns (uint) { + return _totalSupply - balances[address(0)]; + } + + function balanceOf(address tokenOwner) public constant returns (uint balance) { + return balances[tokenOwner]; + } + + function transfer(address to, uint tokens) public returns (bool success) { + balances[msg.sender] = safeSub(balances[msg.sender], tokens); + balances[to] = safeAdd(balances[to], tokens); + emit Transfer(msg.sender, to, tokens); + return true; + } + + function approve(address spender, uint tokens) public returns (bool success) { + allowed[msg.sender][spender] = tokens; + emit Approval(msg.sender, spender, tokens); + return true; + } + + function transferFrom(address from, address to, uint tokens) public returns (bool success) { + balances[from] = safeSub(balances[from], tokens); + allowed[from][msg.sender] = safeSub(allowed[from][msg.sender], tokens); + balances[to] = safeAdd(balances[to], tokens); + emit Transfer(from, to, tokens); + return true; + } + + function allowance(address tokenOwner, address spender) public constant returns (uint remaining) { + return allowed[tokenOwner][spender]; + } + + function approveAndCall(address spender, uint tokens, bytes data) public returns (bool success) { + allowed[msg.sender][spender] = tokens; + emit Approval(msg.sender, spender, tokens); + ApproveAndCallFallBack(spender).receiveApproval(msg.sender, tokens, this, data); + return true; + } + + function () public payable { + revert(); + } +} \ No newline at end of file diff --git a/packages/examples/subgraph-notification/generated/PushToken/PushToken.ts b/packages/examples/subgraph-notification/generated/PushToken/PushToken.ts new file mode 100644 index 000000000..080653c32 --- /dev/null +++ b/packages/examples/subgraph-notification/generated/PushToken/PushToken.ts @@ -0,0 +1,619 @@ +// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. + +import { + ethereum, + JSONValue, + TypedMap, + Entity, + Bytes, + Address, + BigInt +} from "@graphprotocol/graph-ts"; + +export class Transfer extends ethereum.Event { + get params(): Transfer__Params { + return new Transfer__Params(this); + } +} + +export class Transfer__Params { + _event: Transfer; + + constructor(event: Transfer) { + this._event = event; + } + + get from(): Address { + return this._event.parameters[0].value.toAddress(); + } + + get to(): Address { + return this._event.parameters[1].value.toAddress(); + } + + get tokens(): BigInt { + return this._event.parameters[2].value.toBigInt(); + } +} + +export class Approval extends ethereum.Event { + get params(): Approval__Params { + return new Approval__Params(this); + } +} + +export class Approval__Params { + _event: Approval; + + constructor(event: Approval) { + this._event = event; + } + + get tokenOwner(): Address { + return this._event.parameters[0].value.toAddress(); + } + + get spender(): Address { + return this._event.parameters[1].value.toAddress(); + } + + get tokens(): BigInt { + return this._event.parameters[2].value.toBigInt(); + } +} + +export class PushToken extends ethereum.SmartContract { + static bind(address: Address): PushToken { + return new PushToken("PushToken", address); + } + + name(): string { + let result = super.call("name", "name():(string)", []); + + return result[0].toString(); + } + + try_name(): ethereum.CallResult { + let result = super.tryCall("name", "name():(string)", []); + if (result.reverted) { + return new ethereum.CallResult(); + } + let value = result.value; + return ethereum.CallResult.fromValue(value[0].toString()); + } + + approve(spender: Address, tokens: BigInt): boolean { + let result = super.call("approve", "approve(address,uint256):(bool)", [ + ethereum.Value.fromAddress(spender), + ethereum.Value.fromUnsignedBigInt(tokens) + ]); + + return result[0].toBoolean(); + } + + try_approve(spender: Address, tokens: BigInt): ethereum.CallResult { + let result = super.tryCall("approve", "approve(address,uint256):(bool)", [ + ethereum.Value.fromAddress(spender), + ethereum.Value.fromUnsignedBigInt(tokens) + ]); + if (result.reverted) { + return new ethereum.CallResult(); + } + let value = result.value; + return ethereum.CallResult.fromValue(value[0].toBoolean()); + } + + totalSupply(): BigInt { + let result = super.call("totalSupply", "totalSupply():(uint256)", []); + + return result[0].toBigInt(); + } + + try_totalSupply(): ethereum.CallResult { + let result = super.tryCall("totalSupply", "totalSupply():(uint256)", []); + if (result.reverted) { + return new ethereum.CallResult(); + } + let value = result.value; + return ethereum.CallResult.fromValue(value[0].toBigInt()); + } + + transferFrom(from: Address, to: Address, tokens: BigInt): boolean { + let result = super.call( + "transferFrom", + "transferFrom(address,address,uint256):(bool)", + [ + ethereum.Value.fromAddress(from), + ethereum.Value.fromAddress(to), + ethereum.Value.fromUnsignedBigInt(tokens) + ] + ); + + return result[0].toBoolean(); + } + + try_transferFrom( + from: Address, + to: Address, + tokens: BigInt + ): ethereum.CallResult { + let result = super.tryCall( + "transferFrom", + "transferFrom(address,address,uint256):(bool)", + [ + ethereum.Value.fromAddress(from), + ethereum.Value.fromAddress(to), + ethereum.Value.fromUnsignedBigInt(tokens) + ] + ); + if (result.reverted) { + return new ethereum.CallResult(); + } + let value = result.value; + return ethereum.CallResult.fromValue(value[0].toBoolean()); + } + + decimals(): i32 { + let result = super.call("decimals", "decimals():(uint8)", []); + + return result[0].toI32(); + } + + try_decimals(): ethereum.CallResult { + let result = super.tryCall("decimals", "decimals():(uint8)", []); + if (result.reverted) { + return new ethereum.CallResult(); + } + let value = result.value; + return ethereum.CallResult.fromValue(value[0].toI32()); + } + + _totalSupply(): BigInt { + let result = super.call("_totalSupply", "_totalSupply():(uint256)", []); + + return result[0].toBigInt(); + } + + try__totalSupply(): ethereum.CallResult { + let result = super.tryCall("_totalSupply", "_totalSupply():(uint256)", []); + if (result.reverted) { + return new ethereum.CallResult(); + } + let value = result.value; + return ethereum.CallResult.fromValue(value[0].toBigInt()); + } + + balanceOf(tokenOwner: Address): BigInt { + let result = super.call("balanceOf", "balanceOf(address):(uint256)", [ + ethereum.Value.fromAddress(tokenOwner) + ]); + + return result[0].toBigInt(); + } + + try_balanceOf(tokenOwner: Address): ethereum.CallResult { + let result = super.tryCall("balanceOf", "balanceOf(address):(uint256)", [ + ethereum.Value.fromAddress(tokenOwner) + ]); + if (result.reverted) { + return new ethereum.CallResult(); + } + let value = result.value; + return ethereum.CallResult.fromValue(value[0].toBigInt()); + } + + symbol(): string { + let result = super.call("symbol", "symbol():(string)", []); + + return result[0].toString(); + } + + try_symbol(): ethereum.CallResult { + let result = super.tryCall("symbol", "symbol():(string)", []); + if (result.reverted) { + return new ethereum.CallResult(); + } + let value = result.value; + return ethereum.CallResult.fromValue(value[0].toString()); + } + + safeSub(a: BigInt, b: BigInt): BigInt { + let result = super.call("safeSub", "safeSub(uint256,uint256):(uint256)", [ + ethereum.Value.fromUnsignedBigInt(a), + ethereum.Value.fromUnsignedBigInt(b) + ]); + + return result[0].toBigInt(); + } + + try_safeSub(a: BigInt, b: BigInt): ethereum.CallResult { + let result = super.tryCall( + "safeSub", + "safeSub(uint256,uint256):(uint256)", + [ + ethereum.Value.fromUnsignedBigInt(a), + ethereum.Value.fromUnsignedBigInt(b) + ] + ); + if (result.reverted) { + return new ethereum.CallResult(); + } + let value = result.value; + return ethereum.CallResult.fromValue(value[0].toBigInt()); + } + + transfer(to: Address, tokens: BigInt): boolean { + let result = super.call("transfer", "transfer(address,uint256):(bool)", [ + ethereum.Value.fromAddress(to), + ethereum.Value.fromUnsignedBigInt(tokens) + ]); + + return result[0].toBoolean(); + } + + try_transfer(to: Address, tokens: BigInt): ethereum.CallResult { + let result = super.tryCall("transfer", "transfer(address,uint256):(bool)", [ + ethereum.Value.fromAddress(to), + ethereum.Value.fromUnsignedBigInt(tokens) + ]); + if (result.reverted) { + return new ethereum.CallResult(); + } + let value = result.value; + return ethereum.CallResult.fromValue(value[0].toBoolean()); + } + + safeDiv(a: BigInt, b: BigInt): BigInt { + let result = super.call("safeDiv", "safeDiv(uint256,uint256):(uint256)", [ + ethereum.Value.fromUnsignedBigInt(a), + ethereum.Value.fromUnsignedBigInt(b) + ]); + + return result[0].toBigInt(); + } + + try_safeDiv(a: BigInt, b: BigInt): ethereum.CallResult { + let result = super.tryCall( + "safeDiv", + "safeDiv(uint256,uint256):(uint256)", + [ + ethereum.Value.fromUnsignedBigInt(a), + ethereum.Value.fromUnsignedBigInt(b) + ] + ); + if (result.reverted) { + return new ethereum.CallResult(); + } + let value = result.value; + return ethereum.CallResult.fromValue(value[0].toBigInt()); + } + + approveAndCall(spender: Address, tokens: BigInt, data: Bytes): boolean { + let result = super.call( + "approveAndCall", + "approveAndCall(address,uint256,bytes):(bool)", + [ + ethereum.Value.fromAddress(spender), + ethereum.Value.fromUnsignedBigInt(tokens), + ethereum.Value.fromBytes(data) + ] + ); + + return result[0].toBoolean(); + } + + try_approveAndCall( + spender: Address, + tokens: BigInt, + data: Bytes + ): ethereum.CallResult { + let result = super.tryCall( + "approveAndCall", + "approveAndCall(address,uint256,bytes):(bool)", + [ + ethereum.Value.fromAddress(spender), + ethereum.Value.fromUnsignedBigInt(tokens), + ethereum.Value.fromBytes(data) + ] + ); + if (result.reverted) { + return new ethereum.CallResult(); + } + let value = result.value; + return ethereum.CallResult.fromValue(value[0].toBoolean()); + } + + safeMul(a: BigInt, b: BigInt): BigInt { + let result = super.call("safeMul", "safeMul(uint256,uint256):(uint256)", [ + ethereum.Value.fromUnsignedBigInt(a), + ethereum.Value.fromUnsignedBigInt(b) + ]); + + return result[0].toBigInt(); + } + + try_safeMul(a: BigInt, b: BigInt): ethereum.CallResult { + let result = super.tryCall( + "safeMul", + "safeMul(uint256,uint256):(uint256)", + [ + ethereum.Value.fromUnsignedBigInt(a), + ethereum.Value.fromUnsignedBigInt(b) + ] + ); + if (result.reverted) { + return new ethereum.CallResult(); + } + let value = result.value; + return ethereum.CallResult.fromValue(value[0].toBigInt()); + } + + allowance(tokenOwner: Address, spender: Address): BigInt { + let result = super.call( + "allowance", + "allowance(address,address):(uint256)", + [ + ethereum.Value.fromAddress(tokenOwner), + ethereum.Value.fromAddress(spender) + ] + ); + + return result[0].toBigInt(); + } + + try_allowance( + tokenOwner: Address, + spender: Address + ): ethereum.CallResult { + let result = super.tryCall( + "allowance", + "allowance(address,address):(uint256)", + [ + ethereum.Value.fromAddress(tokenOwner), + ethereum.Value.fromAddress(spender) + ] + ); + if (result.reverted) { + return new ethereum.CallResult(); + } + let value = result.value; + return ethereum.CallResult.fromValue(value[0].toBigInt()); + } + + safeAdd(a: BigInt, b: BigInt): BigInt { + let result = super.call("safeAdd", "safeAdd(uint256,uint256):(uint256)", [ + ethereum.Value.fromUnsignedBigInt(a), + ethereum.Value.fromUnsignedBigInt(b) + ]); + + return result[0].toBigInt(); + } + + try_safeAdd(a: BigInt, b: BigInt): ethereum.CallResult { + let result = super.tryCall( + "safeAdd", + "safeAdd(uint256,uint256):(uint256)", + [ + ethereum.Value.fromUnsignedBigInt(a), + ethereum.Value.fromUnsignedBigInt(b) + ] + ); + if (result.reverted) { + return new ethereum.CallResult(); + } + let value = result.value; + return ethereum.CallResult.fromValue(value[0].toBigInt()); + } +} + +export class ApproveCall extends ethereum.Call { + get inputs(): ApproveCall__Inputs { + return new ApproveCall__Inputs(this); + } + + get outputs(): ApproveCall__Outputs { + return new ApproveCall__Outputs(this); + } +} + +export class ApproveCall__Inputs { + _call: ApproveCall; + + constructor(call: ApproveCall) { + this._call = call; + } + + get spender(): Address { + return this._call.inputValues[0].value.toAddress(); + } + + get tokens(): BigInt { + return this._call.inputValues[1].value.toBigInt(); + } +} + +export class ApproveCall__Outputs { + _call: ApproveCall; + + constructor(call: ApproveCall) { + this._call = call; + } + + get success(): boolean { + return this._call.outputValues[0].value.toBoolean(); + } +} + +export class TransferFromCall extends ethereum.Call { + get inputs(): TransferFromCall__Inputs { + return new TransferFromCall__Inputs(this); + } + + get outputs(): TransferFromCall__Outputs { + return new TransferFromCall__Outputs(this); + } +} + +export class TransferFromCall__Inputs { + _call: TransferFromCall; + + constructor(call: TransferFromCall) { + this._call = call; + } + + get from(): Address { + return this._call.inputValues[0].value.toAddress(); + } + + get to(): Address { + return this._call.inputValues[1].value.toAddress(); + } + + get tokens(): BigInt { + return this._call.inputValues[2].value.toBigInt(); + } +} + +export class TransferFromCall__Outputs { + _call: TransferFromCall; + + constructor(call: TransferFromCall) { + this._call = call; + } + + get success(): boolean { + return this._call.outputValues[0].value.toBoolean(); + } +} + +export class TransferCall extends ethereum.Call { + get inputs(): TransferCall__Inputs { + return new TransferCall__Inputs(this); + } + + get outputs(): TransferCall__Outputs { + return new TransferCall__Outputs(this); + } +} + +export class TransferCall__Inputs { + _call: TransferCall; + + constructor(call: TransferCall) { + this._call = call; + } + + get to(): Address { + return this._call.inputValues[0].value.toAddress(); + } + + get tokens(): BigInt { + return this._call.inputValues[1].value.toBigInt(); + } +} + +export class TransferCall__Outputs { + _call: TransferCall; + + constructor(call: TransferCall) { + this._call = call; + } + + get success(): boolean { + return this._call.outputValues[0].value.toBoolean(); + } +} + +export class ApproveAndCallCall extends ethereum.Call { + get inputs(): ApproveAndCallCall__Inputs { + return new ApproveAndCallCall__Inputs(this); + } + + get outputs(): ApproveAndCallCall__Outputs { + return new ApproveAndCallCall__Outputs(this); + } +} + +export class ApproveAndCallCall__Inputs { + _call: ApproveAndCallCall; + + constructor(call: ApproveAndCallCall) { + this._call = call; + } + + get spender(): Address { + return this._call.inputValues[0].value.toAddress(); + } + + get tokens(): BigInt { + return this._call.inputValues[1].value.toBigInt(); + } + + get data(): Bytes { + return this._call.inputValues[2].value.toBytes(); + } +} + +export class ApproveAndCallCall__Outputs { + _call: ApproveAndCallCall; + + constructor(call: ApproveAndCallCall) { + this._call = call; + } + + get success(): boolean { + return this._call.outputValues[0].value.toBoolean(); + } +} + +export class ConstructorCall extends ethereum.Call { + get inputs(): ConstructorCall__Inputs { + return new ConstructorCall__Inputs(this); + } + + get outputs(): ConstructorCall__Outputs { + return new ConstructorCall__Outputs(this); + } +} + +export class ConstructorCall__Inputs { + _call: ConstructorCall; + + constructor(call: ConstructorCall) { + this._call = call; + } +} + +export class ConstructorCall__Outputs { + _call: ConstructorCall; + + constructor(call: ConstructorCall) { + this._call = call; + } +} + +export class DefaultCall extends ethereum.Call { + get inputs(): DefaultCall__Inputs { + return new DefaultCall__Inputs(this); + } + + get outputs(): DefaultCall__Outputs { + return new DefaultCall__Outputs(this); + } +} + +export class DefaultCall__Inputs { + _call: DefaultCall; + + constructor(call: DefaultCall) { + this._call = call; + } +} + +export class DefaultCall__Outputs { + _call: DefaultCall; + + constructor(call: DefaultCall) { + this._call = call; + } +} diff --git a/packages/examples/subgraph-notification/generated/schema.ts b/packages/examples/subgraph-notification/generated/schema.ts new file mode 100644 index 000000000..71b007f31 --- /dev/null +++ b/packages/examples/subgraph-notification/generated/schema.ts @@ -0,0 +1,428 @@ +// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. + +import { + TypedMap, + Entity, + Value, + ValueKind, + store, + Bytes, + BigInt, + BigDecimal +} from "@graphprotocol/graph-ts"; + +export class User extends Entity { + constructor(id: string) { + super(); + this.set("id", Value.fromString(id)); + } + + save(): void { + let id = this.get("id"); + assert(id != null, "Cannot save User entity without an ID"); + if (id) { + assert( + id.kind == ValueKind.STRING, + `Entities of type User must have an ID of type String but the id '${id.displayData()}' is of type ${id.displayKind()}` + ); + store.set("User", id.toString(), this); + } + } + + static load(id: string): User | null { + return changetype(store.get("User", id)); + } + + get id(): string { + let value = this.get("id"); + return value!.toString(); + } + + set id(value: string) { + this.set("id", Value.fromString(value)); + } + + get address(): string { + let value = this.get("address"); + return value!.toString(); + } + + set address(value: string) { + this.set("address", Value.fromString(value)); + } + + get balance(): BigInt { + let value = this.get("balance"); + return value!.toBigInt(); + } + + set balance(value: BigInt) { + this.set("balance", Value.fromBigInt(value)); + } + + get transactionCount(): i32 { + let value = this.get("transactionCount"); + return value!.toI32(); + } + + set transactionCount(value: i32) { + this.set("transactionCount", Value.fromI32(value)); + } +} + +export class Minter extends Entity { + constructor(id: string) { + super(); + this.set("id", Value.fromString(id)); + } + + save(): void { + let id = this.get("id"); + assert(id != null, "Cannot save Minter entity without an ID"); + if (id) { + assert( + id.kind == ValueKind.STRING, + `Entities of type Minter must have an ID of type String but the id '${id.displayData()}' is of type ${id.displayKind()}` + ); + store.set("Minter", id.toString(), this); + } + } + + static load(id: string): Minter | null { + return changetype(store.get("Minter", id)); + } + + get id(): string { + let value = this.get("id"); + return value!.toString(); + } + + set id(value: string) { + this.set("id", Value.fromString(value)); + } + + get address(): string { + let value = this.get("address"); + return value!.toString(); + } + + set address(value: string) { + this.set("address", Value.fromString(value)); + } + + get totalMinted(): BigInt { + let value = this.get("totalMinted"); + return value!.toBigInt(); + } + + set totalMinted(value: BigInt) { + this.set("totalMinted", Value.fromBigInt(value)); + } + + get totalBurned(): BigInt { + let value = this.get("totalBurned"); + return value!.toBigInt(); + } + + set totalBurned(value: BigInt) { + this.set("totalBurned", Value.fromBigInt(value)); + } +} + +export class UserCounter extends Entity { + constructor(id: string) { + super(); + this.set("id", Value.fromString(id)); + } + + save(): void { + let id = this.get("id"); + assert(id != null, "Cannot save UserCounter entity without an ID"); + if (id) { + assert( + id.kind == ValueKind.STRING, + `Entities of type UserCounter must have an ID of type String but the id '${id.displayData()}' is of type ${id.displayKind()}` + ); + store.set("UserCounter", id.toString(), this); + } + } + + static load(id: string): UserCounter | null { + return changetype(store.get("UserCounter", id)); + } + + get id(): string { + let value = this.get("id"); + return value!.toString(); + } + + set id(value: string) { + this.set("id", Value.fromString(value)); + } + + get count(): i32 { + let value = this.get("count"); + return value!.toI32(); + } + + set count(value: i32) { + this.set("count", Value.fromI32(value)); + } +} + +export class MinterCounter extends Entity { + constructor(id: string) { + super(); + this.set("id", Value.fromString(id)); + } + + save(): void { + let id = this.get("id"); + assert(id != null, "Cannot save MinterCounter entity without an ID"); + if (id) { + assert( + id.kind == ValueKind.STRING, + `Entities of type MinterCounter must have an ID of type String but the id '${id.displayData()}' is of type ${id.displayKind()}` + ); + store.set("MinterCounter", id.toString(), this); + } + } + + static load(id: string): MinterCounter | null { + return changetype(store.get("MinterCounter", id)); + } + + get id(): string { + let value = this.get("id"); + return value!.toString(); + } + + set id(value: string) { + this.set("id", Value.fromString(value)); + } + + get count(): i32 { + let value = this.get("count"); + return value!.toI32(); + } + + set count(value: i32) { + this.set("count", Value.fromI32(value)); + } +} + +export class TransferCounter extends Entity { + constructor(id: string) { + super(); + this.set("id", Value.fromString(id)); + } + + save(): void { + let id = this.get("id"); + assert(id != null, "Cannot save TransferCounter entity without an ID"); + if (id) { + assert( + id.kind == ValueKind.STRING, + `Entities of type TransferCounter must have an ID of type String but the id '${id.displayData()}' is of type ${id.displayKind()}` + ); + store.set("TransferCounter", id.toString(), this); + } + } + + static load(id: string): TransferCounter | null { + return changetype(store.get("TransferCounter", id)); + } + + get id(): string { + let value = this.get("id"); + return value!.toString(); + } + + set id(value: string) { + this.set("id", Value.fromString(value)); + } + + get count(): i32 { + let value = this.get("count"); + return value!.toI32(); + } + + set count(value: i32) { + this.set("count", Value.fromI32(value)); + } + + get totalTransferred(): BigInt { + let value = this.get("totalTransferred"); + return value!.toBigInt(); + } + + set totalTransferred(value: BigInt) { + this.set("totalTransferred", Value.fromBigInt(value)); + } +} + +export class TotalSupply extends Entity { + constructor(id: string) { + super(); + this.set("id", Value.fromString(id)); + } + + save(): void { + let id = this.get("id"); + assert(id != null, "Cannot save TotalSupply entity without an ID"); + if (id) { + assert( + id.kind == ValueKind.STRING, + `Entities of type TotalSupply must have an ID of type String but the id '${id.displayData()}' is of type ${id.displayKind()}` + ); + store.set("TotalSupply", id.toString(), this); + } + } + + static load(id: string): TotalSupply | null { + return changetype(store.get("TotalSupply", id)); + } + + get id(): string { + let value = this.get("id"); + return value!.toString(); + } + + set id(value: string) { + this.set("id", Value.fromString(value)); + } + + get supply(): BigInt { + let value = this.get("supply"); + return value!.toBigInt(); + } + + set supply(value: BigInt) { + this.set("supply", Value.fromBigInt(value)); + } + + get minted(): BigInt { + let value = this.get("minted"); + return value!.toBigInt(); + } + + set minted(value: BigInt) { + this.set("minted", Value.fromBigInt(value)); + } + + get burned(): BigInt { + let value = this.get("burned"); + return value!.toBigInt(); + } + + set burned(value: BigInt) { + this.set("burned", Value.fromBigInt(value)); + } +} + +export class EpnsNotificationCounter extends Entity { + constructor(id: string) { + super(); + this.set("id", Value.fromString(id)); + } + + save(): void { + let id = this.get("id"); + assert( + id != null, + "Cannot save EpnsNotificationCounter entity without an ID" + ); + if (id) { + assert( + id.kind == ValueKind.STRING, + `Entities of type EpnsNotificationCounter must have an ID of type String but the id '${id.displayData()}' is of type ${id.displayKind()}` + ); + store.set("EpnsNotificationCounter", id.toString(), this); + } + } + + static load(id: string): EpnsNotificationCounter | null { + return changetype( + store.get("EpnsNotificationCounter", id) + ); + } + + get id(): string { + let value = this.get("id"); + return value!.toString(); + } + + set id(value: string) { + this.set("id", Value.fromString(value)); + } + + get totalCount(): BigInt { + let value = this.get("totalCount"); + return value!.toBigInt(); + } + + set totalCount(value: BigInt) { + this.set("totalCount", Value.fromBigInt(value)); + } +} + +export class EpnsPushNotification extends Entity { + constructor(id: string) { + super(); + this.set("id", Value.fromString(id)); + } + + save(): void { + let id = this.get("id"); + assert(id != null, "Cannot save EpnsPushNotification entity without an ID"); + if (id) { + assert( + id.kind == ValueKind.STRING, + `Entities of type EpnsPushNotification must have an ID of type String but the id '${id.displayData()}' is of type ${id.displayKind()}` + ); + store.set("EpnsPushNotification", id.toString(), this); + } + } + + static load(id: string): EpnsPushNotification | null { + return changetype( + store.get("EpnsPushNotification", id) + ); + } + + get id(): string { + let value = this.get("id"); + return value!.toString(); + } + + set id(value: string) { + this.set("id", Value.fromString(value)); + } + + get notificationNumber(): BigInt { + let value = this.get("notificationNumber"); + return value!.toBigInt(); + } + + set notificationNumber(value: BigInt) { + this.set("notificationNumber", Value.fromBigInt(value)); + } + + get recipient(): string { + let value = this.get("recipient"); + return value!.toString(); + } + + set recipient(value: string) { + this.set("recipient", Value.fromString(value)); + } + + get notification(): string { + let value = this.get("notification"); + return value!.toString(); + } + + set notification(value: string) { + this.set("notification", Value.fromString(value)); + } +} diff --git a/packages/examples/subgraph-notification/networks.json b/packages/examples/subgraph-notification/networks.json new file mode 100644 index 000000000..4b22acd17 --- /dev/null +++ b/packages/examples/subgraph-notification/networks.json @@ -0,0 +1,7 @@ +{ + "goerli": { + "EPNSCore": { + "address": "0xd4E3ceC407cD36d9e3767cD189ccCaFBF549202C" + } + } +} \ No newline at end of file diff --git a/packages/examples/subgraph-notification/package.json b/packages/examples/subgraph-notification/package.json new file mode 100644 index 000000000..7a3236c12 --- /dev/null +++ b/packages/examples/subgraph-notification/package.json @@ -0,0 +1,18 @@ +{ + "name": "push-graph-test", + "license": "UNLICENSED", + "scripts": { + "codegen": "graph codegen", + "build": "graph build", + "deploy": "graph deploy --node https://api.studio.thegraph.com/deploy/ aiswaryawalter/push-graph-test", + "create-local": "graph create --node http://localhost:8020/ aiswaryawalter/push-graph-test", + "remove-local": "graph remove --node http://localhost:8020/ aiswaryawalter/push-graph-test", + "deploy-local": "graph deploy --node http://localhost:8020/ --ipfs http://localhost:5001 aiswaryawalter/push-graph-test", + "test": "graph test" + }, + "dependencies": { + "@graphprotocol/graph-cli": "0.34.0", + "@graphprotocol/graph-ts": "0.28.0" + }, + "devDependencies": { "matchstick-as": "0.5.0" } +} diff --git a/packages/examples/subgraph-notification/schema.graphql b/packages/examples/subgraph-notification/schema.graphql new file mode 100644 index 000000000..4114b9d84 --- /dev/null +++ b/packages/examples/subgraph-notification/schema.graphql @@ -0,0 +1,49 @@ +type User @entity { + id: ID! + address: String! + balance: BigInt! + transactionCount: Int! +} + +type Minter @entity { + id: ID! + address: String! + totalMinted: BigInt! + totalBurned: BigInt! +} + +type UserCounter @entity { + id: ID! + count: Int! +} + +type MinterCounter @entity { + id: ID! + count: Int! +} + +type TransferCounter @entity { + id: ID! + count: Int! + totalTransferred: BigInt! +} + +type TotalSupply @entity { + id: ID! + supply: BigInt! + minted: BigInt! + burned: BigInt! +} + +type EpnsNotificationCounter @entity { + id: ID! + totalCount: BigInt! +} + +type EpnsPushNotification @entity { + id: ID! + notificationNumber: BigInt! + recipient: String! + notification: String! +} + diff --git a/packages/examples/subgraph-notification/src/PushNotification.ts b/packages/examples/subgraph-notification/src/PushNotification.ts new file mode 100644 index 000000000..61a387b1a --- /dev/null +++ b/packages/examples/subgraph-notification/src/PushNotification.ts @@ -0,0 +1,30 @@ +import { + BigInt, + log } from "@graphprotocol/graph-ts" +import { EpnsNotificationCounter, EpnsPushNotification } from '../generated/schema' +import { subgraphID } from "./push-token" + +export function sendPushNotification(recipient: string, notification: string): void +{ + let id1 = subgraphID + log.info('New id of EpnsNotificationCounter is: {}', [id1]) + let epnsNotificationCounter = EpnsNotificationCounter.load(id1) + if (epnsNotificationCounter == null) { + epnsNotificationCounter = new EpnsNotificationCounter(id1) + epnsNotificationCounter.totalCount = BigInt.fromI32(0) + } + epnsNotificationCounter.totalCount = (epnsNotificationCounter.totalCount).plus(BigInt.fromI32(1)) + + let count = epnsNotificationCounter.totalCount.toHexString() + let id2 = `${subgraphID}+${count}` + log.info('New id of EpnsPushNotification is: {}', [id2]) + let epnsPushNotification = EpnsPushNotification.load(id2) + if (epnsPushNotification == null) { + epnsPushNotification = new EpnsPushNotification(id2) + } + epnsPushNotification.recipient = recipient + epnsPushNotification.notification = notification + epnsPushNotification.notificationNumber = epnsNotificationCounter.totalCount + epnsPushNotification.save() + epnsNotificationCounter.save() +} \ No newline at end of file diff --git a/packages/examples/subgraph-notification/src/push-token.ts b/packages/examples/subgraph-notification/src/push-token.ts new file mode 100644 index 000000000..c66216cda --- /dev/null +++ b/packages/examples/subgraph-notification/src/push-token.ts @@ -0,0 +1,90 @@ +import { BigInt } from "@graphprotocol/graph-ts" +import { PushToken, Transfer, Approval } from "../generated/PushToken/PushToken" +import { User, UserCounter, TransferCounter } from "../generated/schema" +import { sendPushNotification } from "./PushNotification" +export const subgraphID = "aiswaryawalter/push-protocol-goerli" + +let ZERO_BI = BigInt.fromI32(0) +let ONE_BI = BigInt.fromI32(1) + +export function handleTransfer(event: Transfer): void { + let contract = PushToken.bind(event.address) + let decimals = BigInt.fromString(contract.decimals().toString()) + let power = exponentToBigInt(decimals) + let day = (event.block.timestamp.div(BigInt.fromI32(60 * 60 * 24))) + + let userFrom = User.load(event.params.from.toHex()) + if (userFrom == null) { + userFrom = newUser(event.params.from.toHex(), event.params.from.toHex()); + } + userFrom.balance = (userFrom.balance).minus(event.params.tokens) + userFrom.transactionCount = userFrom.transactionCount + 1 + userFrom.save() + + let userTo = User.load(event.params.to.toHex()) + if (userTo == null) { + userTo = newUser(event.params.to.toHex(), event.params.to.toHex()); + + // UserCounter + let userCounter = UserCounter.load('singleton') + if (userCounter == null) { + userCounter = new UserCounter('singleton') + userCounter.count = 1 + } else { + userCounter.count = userCounter.count + 1 + } + userCounter.save() + userCounter.id = day.toString() + userCounter.save() + } + userTo.balance = (userTo.balance).plus(event.params.tokens) + userTo.transactionCount = userTo.transactionCount + 1 + userTo.save() + + // Transfer counter total and historical + let transferCounter = TransferCounter.load('singleton') + if (transferCounter == null) { + transferCounter = new TransferCounter('singleton') + transferCounter.count = 0 + transferCounter.totalTransferred = BigInt.fromI32(0) + } + transferCounter.count = transferCounter.count + 1 + transferCounter.totalTransferred = transferCounter.totalTransferred.plus(event.params.tokens) + transferCounter.save() + transferCounter.id = day.toString() + transferCounter.save() + + let recipient = event.params.to.toHexString(), + type = "3", + title = "PUSH Received", + body = `Received ${event.params.tokens.div(power)} PUSH from ${event.params.from.toHexString()}`, + subject = "PUSH Received", + message = `Received ${event.params.tokens.div(power)} PUSH from ${event.params.from.toHexString()}`, + image = "https://play-lh.googleusercontent.com/i911_wMmFilaAAOTLvlQJZMXoxBF34BMSzRmascHezvurtslYUgOHamxgEnMXTklsF-S", + secret = "null", + cta = "https://epns.io/", + + notification = `{\"type\": \"${type}\", \"title\": \"${title}\", \"body\": \"${body}\", \"subject\": \"${subject}\", \"message\": \"${message}\", \"image\": \"${image}\", \"secret\": \"${secret}\", \"cta\": \"${cta}\"}` + + sendPushNotification( + recipient, + notification + ) +} + +function newUser(id: string, address: string): User { + let user = new User(id); + user.address = address + user.balance = BigInt.fromI32(0) + user.transactionCount = 0 + return user +} +function exponentToBigInt(decimals: BigInt): BigInt { + let bd = BigInt.fromString('1') + for (let i = ZERO_BI; i.lt(decimals as BigInt); i = i.plus(ONE_BI)) { + bd = bd.times(BigInt.fromString('10')) + } + return bd +} + +// export function handleApproval(event: Approval): void {} diff --git a/packages/examples/subgraph-notification/subgraph.yaml b/packages/examples/subgraph-notification/subgraph.yaml new file mode 100644 index 000000000..94e3702ae --- /dev/null +++ b/packages/examples/subgraph-notification/subgraph.yaml @@ -0,0 +1,27 @@ +specVersion: 0.0.4 +schema: + file: ./schema.graphql +dataSources: + - kind: ethereum + name: PushToken + network: goerli + source: + address: "0x9120041585424b7c830f7A0b2DC3245da019a4BF" + abi: PushToken + startBlock: 7750861 + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + entities: + - Transfer + - Approval + abis: + - name: PushToken + file: ./abis/PushToken.json + eventHandlers: + - event: Transfer(indexed address,indexed address,uint256) + handler: handleTransfer + - event: Approval(indexed address,indexed address,uint256) + handler: handleApproval + file: ./src/push-token.ts diff --git a/packages/examples/subgraph-notification/tests/epns-core-utils.ts b/packages/examples/subgraph-notification/tests/epns-core-utils.ts new file mode 100644 index 000000000..267d00259 --- /dev/null +++ b/packages/examples/subgraph-notification/tests/epns-core-utils.ts @@ -0,0 +1,39 @@ +import { newMockEvent } from "matchstick-as" +import { ethereum, Address } from "@graphprotocol/graph-ts" +import { AdminChanged, Upgraded } from "../generated/EPNSCore/EPNSCore" + +export function createAdminChangedEvent( + previousAdmin: Address, + newAdmin: Address +): AdminChanged { + let adminChangedEvent = changetype(newMockEvent()) + + adminChangedEvent.parameters = new Array() + + adminChangedEvent.parameters.push( + new ethereum.EventParam( + "previousAdmin", + ethereum.Value.fromAddress(previousAdmin) + ) + ) + adminChangedEvent.parameters.push( + new ethereum.EventParam("newAdmin", ethereum.Value.fromAddress(newAdmin)) + ) + + return adminChangedEvent +} + +export function createUpgradedEvent(implementation: Address): Upgraded { + let upgradedEvent = changetype(newMockEvent()) + + upgradedEvent.parameters = new Array() + + upgradedEvent.parameters.push( + new ethereum.EventParam( + "implementation", + ethereum.Value.fromAddress(implementation) + ) + ) + + return upgradedEvent +} diff --git a/packages/examples/subgraph-notification/tests/epns-core.test.ts b/packages/examples/subgraph-notification/tests/epns-core.test.ts new file mode 100644 index 000000000..3dc4b3183 --- /dev/null +++ b/packages/examples/subgraph-notification/tests/epns-core.test.ts @@ -0,0 +1,57 @@ +import { + assert, + describe, + test, + clearStore, + beforeAll, + afterAll +} from "matchstick-as/assembly/index" +import { Address } from "@graphprotocol/graph-ts" +import { ExampleEntity } from "../generated/schema" +import { AdminChanged } from "../generated/EPNSCore/EPNSCore" +import { handleAdminChanged } from "../src/epns-core" +import { createAdminChangedEvent } from "./epns-core-utils" + +// Tests structure (matchstick-as >=0.5.0) +// https://thegraph.com/docs/en/developer/matchstick/#tests-structure-0-5-0 + +describe("Describe entity assertions", () => { + beforeAll(() => { + let previousAdmin = Address.fromString( + "0x0000000000000000000000000000000000000001" + ) + let newAdmin = Address.fromString( + "0x0000000000000000000000000000000000000001" + ) + let newAdminChangedEvent = createAdminChangedEvent(previousAdmin, newAdmin) + handleAdminChanged(newAdminChangedEvent) + }) + + afterAll(() => { + clearStore() + }) + + // For more test scenarios, see: + // https://thegraph.com/docs/en/developer/matchstick/#write-a-unit-test + + test("ExampleEntity created and stored", () => { + assert.entityCount("ExampleEntity", 1) + + // 0xa16081f360e3847006db660bae1c6d1b2e17ec2a is the default address used in newMockEvent() function + assert.fieldEquals( + "ExampleEntity", + "0xa16081f360e3847006db660bae1c6d1b2e17ec2a", + "previousAdmin", + "0x0000000000000000000000000000000000000001" + ) + assert.fieldEquals( + "ExampleEntity", + "0xa16081f360e3847006db660bae1c6d1b2e17ec2a", + "newAdmin", + "0x0000000000000000000000000000000000000001" + ) + + // More assert options: + // https://thegraph.com/docs/en/developer/matchstick/#asserts + }) +}) diff --git a/packages/examples/subgraph-notification/tsconfig.json b/packages/examples/subgraph-notification/tsconfig.json new file mode 100644 index 000000000..5c5d17c92 --- /dev/null +++ b/packages/examples/subgraph-notification/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "@graphprotocol/graph-ts/types/tsconfig.base.json", + "include": ["src"] +}