diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d549182..403e144 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -52,7 +52,7 @@ jobs: run: | bash -x start_ocean.sh --with-thegraph --skip-subgraph-deploy --no-dashboard 2>&1 > start_ocean.log & env: - CONTRACTS_VERSION: v1.0.0-alpha.28 + CONTRACTS_VERSION: v1.1.3 - run: npm ci @@ -69,6 +69,8 @@ jobs: sleep 20 env: ADDRESS_FILE: /home/runner/.ocean/ocean-contracts/artifacts/address.json + BARGE_FOLDER: /home/runner/.ocean/ - run: npm run test-integration env: ADDRESS_FILE: /home/runner/.ocean/ocean-contracts/artifacts/address.json + BARGE_FOLDER: /home/runner/.ocean/ diff --git a/docs/README-local-veSubgraph.md b/docs/README-local-veSubgraph.md new file mode 100644 index 0000000..93c85e4 --- /dev/null +++ b/docs/README-local-veSubgraph.md @@ -0,0 +1,166 @@ +# Add contract as dataSource and use local Subgraph + Ganache + Brownie +To develop new features on top of `ocean-subgraph`, it pays to deploy a local Subgraph that consumes from Ganache, so you can deploy your Contracts, execute their functionality, and query the Subgraph to verify Events are being Handled correctly, w/ the right Schema being yielded. + +The following doc takes you through: +1. Configuring `docker-compose.yml` to run an internal Subgraph that consumes from a local Ganache. +2. Configuring `df-py` + Brownie to connect to the `ocean-subgraph` Ganache so we can deploy our local contracts. +3. Adding our smart contract as a dataSource for our internal Subgraph. We can then handle contract events, and transform that data into queryable entities using GQL. +4. Finally, we can hook our contracts into Testing & verifying that your subgraph is working as intended. + +Note 1: For this tutorial, you should be using multiple terminal windows. These will be referred to at the top of each section. + +Note 2: For this example, we're going to use the `df-py` repository and integrate `veAllocate` contract into `ocean-subgraph` + +Note 3: For the sake of versatility, the example below is being executed using `df-py` while verification is being done by hand by viewing & querying GQL on the browser. + +### 1. ocean-subgraph - Add & Connect Ganache +Section 1 takes place inside `ocean-subgraph` terminal window. + +You should have this repository configured and working on your machine. + +We now need to update `./docker/docker-compose.yml` to implement a local instance of Ganache. Our `graph-node` needs to listen to Ganache for events, so we update the ethereum url. +``` +services: + ganache: + image: trufflesuite/ganache-cli:latest + ports: + - 8545:8545 + entrypoint: ["node", "/app/ganache-core.docker.cli.js", "--db", "./ganache_cache","--chainId","0x2324","--networkId","0x2324","--gasLimit","10000000000","--gasPrice","1","---hardfork","istanbul","--mnemonic","${GANACHE_MNEMONIC}", "-e", "100", "-a", "20"] + graph-node: + environment: + ethereum: 'development:http://ganache:8545' +``` + +Great, our docker environment is now setup. We can finally deploy it by typing `docker-compose up` inside of `./docker/`. + +### 2a. df-py - Configure to connect to Subgraph +Section 2 takes place inside `df-py` terminal window. + +You should have this repository configured and working on your machine. + +First, we make sure that we're inside our venv by typing `source venv/bin/activate/`. Again, we're assuming your requirements.txt, and other dependencies have been properly initialized. + +We now `df-py` connect to Ganache via Brownie. To do this, we're going to add a network so Brownie can listen to our Ganache service. +``` +brownie networks add Ethereum subgraph-ganache host=http://127.0.0.1:8545, chainid=8996 +``` + +We then update our `brownie-config.yaml` so that it uses the network above by default. +``` +networks: + default: subgraph-ganache +``` + +You should now be able to verify that the accounts from `subgraph-ganache` are working from inside `df-py` brownie. +``` +brownie console --network subgraph-ganache +>>> accounts[0].balance() +100000000000000000000 +``` + +### 2b. df-py - Configure dftools and deploy contracts +This section continues from Section 2. + +We can now, also configure dftools to use accounts from Ganache, and deploy contracts into `ocean-subgraph`. + +Look inside the `ocean-subgraph` terminal for the private accounts that were generated for you inside of Ganache. Copy one of their private keys, and do the following inside of `df-py/(venv)/` +``` +export DFTOOL_KEY=0xYourUserPrivateKeyFromGanache +export WEB3_INFURA_PROJECT_ID=YourInfuraKey +``` + +You should now be able to deploy a newToken + veOCEAN + veAllocate contracts. +``` + // deploy a new token + dftool newToken 8996 + + // use the token address from above to deploy veOcean + dftool newVeOcean 8996 token_address + + // deploy veAllocate + dftool newVeAllocate 8996 +``` + +You will need the address from the new `veAllocate` contract you just deployed for the next section. This contract will be used in the future so our subgraph can listen for events. + +### 3a. ocean-subgraph - Initialize the project +Section 3 takes place inside `ocean-subgraph` terminal. + +We're going to now generate `subgraph.yaml` so we can start connecting the veOCEAN contract into the logic of `ocean-subpgrah`. + +We begin by running the following node script from the root folder. +``` +node ./scripts/generatenetworkssubgraphs.js +``` + +Here, we're going to configure our local `subgraph.yaml` file so we can serve our local ganache, contracts, and everything that `df-py` depends on. + +Use this file for all of your development until you have things sorted out. If you make any mistakes, you can just recreate the file. You should make your final changes inside of `subgraph.template.yaml` before submitting a PR. + +### 3b. ocean-subgraph - Configure dataSources + +We now configure our `subgraph.yaml` to talk to ganache and add our veAllocate Contract as a dataSource. + +Our first step is to reset our `subgraph.yaml` so that it can consume all the events. Do a search for `startBlock` and make sure all params are initialized to 0. +``` +- kind: ethereum/contract + source: + startBlock: 0 +``` + +We then add our contract as a dataSource. We get our address from Section 2b `dftool newVeAllocate 8996` and enter it in the `dataSources` section of the `subgraph.yaml` file. +``` +dataSources: + - kind: ethereum/contract + name: veAllocate + network: development + source: + address: 0x0000000000000000000000000000000 + abi: veAllocate + startBlock: 0 + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + file: ./src/mappings/veAllocate.ts + entities: + - veAllocate + abis: + - name: veAllocate + file: ./abis/veAllocate.json + eventHandlers: + - event: AllocationSet(indexed address,indexed address,indexed uint256,uint256) + handler: handleAllocationSet +``` + +As you can see, we have also imported our `veAllocate.json ABI` file into the project, added the contract events we want to handle, and created a mapping script `./src/mappings/veAllocate.ts` so we can handle all the vents for the Subgraph. + +Note: This tutorial will not go into details of how to do this work. Someone can create a separate guide on how to handle internal logic, define GraphQL entities, and save records into GraphQL. + +_Please review the PR associated with this README for more intuition on this_ + +### 4a. ocean-subgraph - run: `npm run codegen` +From the root folder run this command. + +You'll have to do this again every time you change `schema.graphql` + +### 4b. ocean-subgraph - run: `npm run create:local` +From the root folder run this command. + +You'll have to do this again every time you run step 5a. + +### 4c. ocean-subgraph - run: `npm run deploy:local` +From the root folder run this command. + +You'll have to do this again when you write any new subgraph code (mappings, utils, etc...). + +### 5. ocean-subgraph - restart docker +Restart docker containers via `docker-compose up` + +### 6. df-py - Execute onchain events + query subgraph +You can now start performing your onchain + contract tests and see records inside of your local subgraph. + +As an example, you may use the following command inside `dftools` to generate many onchain events for Ganache. +``` +dftool manyrandom 8996 +``` \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index ab8ecd1..5706be0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,9 +5,11 @@ "requires": true, "packages": { "": { + "name": "ocean-subgraph", "version": "2.0.3", "license": "Apache-2.0", "dependencies": { + "@oceanprotocol/contracts": "^1.1.3", "@oceanprotocol/lib": "^1.1.8", "cross-fetch": "^3.1.4" }, @@ -858,9 +860,9 @@ } }, "node_modules/@oceanprotocol/contracts": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@oceanprotocol/contracts/-/contracts-1.0.0.tgz", - "integrity": "sha512-rDCIooe1WHipLejuGhx2Wv/88SB7bWrN3+XHCWxXyPKTmmSQsgxKZPPzbIVBQ0ESChQZqGSBBJyqErqwwW4eBw==" + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@oceanprotocol/contracts/-/contracts-1.1.3.tgz", + "integrity": "sha512-pn0rm4QKF8sVfDeJVlt18TV4Qj5oGgR/qQNO7O0GO2DQ3q8KHXRS15uRjmLTr5wW1kGcCHcTqEndXEEC7Elzkw==" }, "node_modules/@oceanprotocol/lib": { "version": "1.1.8", @@ -15275,9 +15277,9 @@ } }, "@oceanprotocol/contracts": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@oceanprotocol/contracts/-/contracts-1.0.0.tgz", - "integrity": "sha512-rDCIooe1WHipLejuGhx2Wv/88SB7bWrN3+XHCWxXyPKTmmSQsgxKZPPzbIVBQ0ESChQZqGSBBJyqErqwwW4eBw==" + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@oceanprotocol/contracts/-/contracts-1.1.3.tgz", + "integrity": "sha512-pn0rm4QKF8sVfDeJVlt18TV4Qj5oGgR/qQNO7O0GO2DQ3q8KHXRS15uRjmLTr5wW1kGcCHcTqEndXEEC7Elzkw==" }, "@oceanprotocol/lib": { "version": "1.1.8", diff --git a/package.json b/package.json index 8b1d85e..862c4f5 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "typescript": "^4.8.2" }, "dependencies": { + "@oceanprotocol/contracts": "^1.1.3", "@oceanprotocol/lib": "^1.1.8", "cross-fetch": "^3.1.4" }, diff --git a/schema.graphql b/schema.graphql index 2777df2..8568e13 100644 --- a/schema.graphql +++ b/schema.graphql @@ -360,7 +360,6 @@ type NftUpdate @entity { tx: String! } - type Template @entity{ id: ID! fixedRateTemplates: [String!] @@ -368,4 +367,98 @@ type Template @entity{ ssTemplates: [String!] } +# Not tracking allocationToId or idToAllocation +type VeAllocateUser @entity{ + "id = {user}" + id: ID! + + veAllocation: [VeAllocation!] @derivedFrom(field: "allocationUser") + allocatedTotal: BigDecimal! + block: Int! + firstContact: Int! + lastContact: Int! + tx: String! +} + +type VeAllocateId @entity{ + "id = {DataNFT Address}-{chain id}" + id: ID! + + veAllocation: [VeAllocation!] @derivedFrom(field: "allocationId") + allocatedTotal: BigDecimal! + + block: Int! + firstContact: Int! + lastContact: Int! + tx: String! +} + +# we need to track allocation of user to id +type VeAllocation @entity { + "id = {user}-{DataNFT Address}-{chain id}" + id: ID! + + allocationUser: VeAllocateUser! + allocationId: VeAllocateId! + + updates: [VeAllocationUpdate!] @derivedFrom(field: "veAllocation") + allocated: BigDecimal! + chainId: BigInt! + nftAddress: String! + + block: Int! + firstContact: Int! + lastContact: Int! + tx: String! +} + +enum veAllocationUpdateType { + SET, + REMOVED +} + +type VeAllocationUpdate @entity { + "{tx}-{VeAllocation id}" + id: ID! + + veAllocation: VeAllocation! + type: veAllocationUpdateType! + allocatedTotal: BigDecimal! + + block: Int! + timestamp: Int! + tx: String! +} + +type VeDelegation @entity { + "id = tokenId" + id: ID! + delegator: VeOCEAN! + receiver: VeOCEAN! + tokenId: BigInt! + amount: BigInt! + cancelTime: BigInt! + expireTime: BigInt! + block: Int! +} + +type VeOCEAN @entity { + "id = {user address}" + id: ID! + lockedAmount: BigDecimal! + unlockTime: BigInt! + delegation: [VeDelegation!] @derivedFrom(field: "delegator") + delegates: [VeDelegation!] @derivedFrom(field: "receiver") + block: Int! +} + +type VeDeposit @entity { + "id = {user address}-{timestamp}" + id: ID! + provider:String! + value: BigDecimal! + unlockTime: BigInt! + type:BigInt! + timestamp: BigInt! +} \ No newline at end of file diff --git a/scripts/generatenetworkssubgraphs.js b/scripts/generatenetworkssubgraphs.js index 0e8e343..a4aa7d9 100644 --- a/scripts/generatenetworkssubgraphs.js +++ b/scripts/generatenetworkssubgraphs.js @@ -20,6 +20,14 @@ async function replaceContractAddresses() { } console.log('Creating subgraph.yaml for ' + network) let subgraph = fs.readFileSync('./subgraph.template.yaml', 'utf8') + const subgraphVe = fs.readFileSync('./subgraph_ve.template.yaml', 'utf8') + if (addresses[network].veOCEAN) { + // fix identation , due to vs auto format (subgraph_ve.template is moved to left) + const lines = subgraphVe.split('\n') + for (let line = 0; line < lines.length; line++) { + subgraph += ' ' + lines[line] + '\n' + } + } subgraph = subgraph.replace(/__NETWORK__/g, network) subgraph = subgraph.replace( @@ -34,6 +42,26 @@ async function replaceContractAddresses() { /__FACTORYROUTERADDRESS__/g, "'" + addresses[network].Router + "'" ) + + subgraph = subgraph.replace( + /__VEALLOCATEADDRESS__/g, + "'" + addresses[network].veAllocate + "'" + ) + + subgraph = subgraph.replace( + /__VEOCEANADDRESS__/g, + "'" + addresses[network].veOCEAN + "'" + ) + + subgraph = subgraph.replace( + /__VEDELEGATIONADDRESS__/g, + "'" + addresses[network].veDelegation + "'" + ) + + subgraph = subgraph.replace( + /__DFREWARDSADDRESS__/g, + "'" + addresses[network].DFRewards + "'" + ) fs.writeFileSync('subgraph.yaml', subgraph, 'utf8') } } diff --git a/src/mappings/utils/constants.ts b/src/mappings/utils/constants.ts index 5741b9e..b5dd09c 100644 --- a/src/mappings/utils/constants.ts +++ b/src/mappings/utils/constants.ts @@ -28,3 +28,8 @@ export namespace NftUpdateType { export const STATE_UPDATED = 'STATE_UPDATED' export const TOKENURI_UPDATED = 'TOKENURI_UPDATED' } + +export namespace veAllocationUpdateType { + export const SET = 'SET' + export const REMOVED = 'REMOVED' +} diff --git a/src/mappings/utils/veUtils.ts b/src/mappings/utils/veUtils.ts new file mode 100644 index 0000000..554fc64 --- /dev/null +++ b/src/mappings/utils/veUtils.ts @@ -0,0 +1,194 @@ +import { BigDecimal, ethereum, BigInt } from '@graphprotocol/graph-ts' +import { + VeAllocateUser, + VeAllocateId, + VeAllocation, + VeAllocationUpdate, + VeDelegation, + VeOCEAN, + VeDeposit +} from '../../@types/schema' +import { veAllocationUpdateType } from './constants' + +export function getveAllocateUser( + event: ethereum.Event, + sender: string +): VeAllocateUser { + let allocateUser = VeAllocateUser.load(sender) + if (allocateUser === null) { + allocateUser = new VeAllocateUser(sender) + allocateUser.allocatedTotal = BigDecimal.zero() + + allocateUser.firstContact = event.block.timestamp.toI32() + allocateUser.tx = event.transaction.hash.toHex() + allocateUser.block = event.block.number.toI32() + allocateUser.lastContact = 0 + + allocateUser.save() + } + + return allocateUser +} + +export function getveAllocateId( + event: ethereum.Event, + id: string +): VeAllocateId { + let allocateId = VeAllocateId.load(id) + if (allocateId === null) { + allocateId = new VeAllocateId(id) + allocateId.allocatedTotal = BigDecimal.zero() + + allocateId.firstContact = event.block.timestamp.toI32() + allocateId.tx = event.transaction.hash.toHex() + allocateId.block = event.block.number.toI32() + allocateId.lastContact = 0 + + allocateId.save() + } + + return allocateId +} + +export function getveAllocation( + event: ethereum.Event, + sender: string, + id: string +): VeAllocation { + let veAllocation = VeAllocation.load(sender + '-' + id) + if (veAllocation === null) { + veAllocation = new VeAllocation(sender + '-' + id) + veAllocation.allocationUser = getveAllocateUser(event, sender).id + veAllocation.allocationId = getveAllocateId(event, id).id + veAllocation.allocated = BigDecimal.zero() + veAllocation.chainId = BigInt.zero() + veAllocation.nftAddress = '' + + veAllocation.firstContact = event.block.timestamp.toI32() + veAllocation.tx = event.transaction.hash.toHex() + veAllocation.block = event.block.number.toI32() + veAllocation.lastContact = 0 + + veAllocation.save() + } + + return veAllocation +} + +// Pass veAllocation being updated +export function writeveAllocationUpdate( + event: ethereum.Event, + veAllocationId: string, + allocationType: string, + amount: BigDecimal +): VeAllocationUpdate { + const tx = event.transaction.hash.toHex() + let allocationUpdate = VeAllocationUpdate.load(tx + '-' + veAllocationId) + if (allocationUpdate === null) { + allocationUpdate = new VeAllocationUpdate(tx + '-' + veAllocationId) + allocationUpdate.veAllocation = veAllocationId + allocationUpdate.type = allocationType + allocationUpdate.allocatedTotal = amount + + allocationUpdate.timestamp = event.block.timestamp.toI32() + allocationUpdate.tx = event.transaction.hash.toHex() + allocationUpdate.block = event.block.number.toI32() + + allocationUpdate.save() + } + + return allocationUpdate +} + +export function getveDelegation(id: string): VeDelegation { + let veDelegation = VeDelegation.load(id) + + if (veDelegation === null) { + veDelegation = new VeDelegation(id) + veDelegation.cancelTime = BigInt.zero() + veDelegation.expireTime = BigInt.zero() + veDelegation.tokenId = BigInt.zero() + veDelegation.amount = BigInt.zero() + veDelegation.receiver = '' + veDelegation.delegator = '' + veDelegation.block = 0 + veDelegation.save() + } + return veDelegation +} + +export function getveOCEAN(id: string): VeOCEAN { + let ve = VeOCEAN.load(id) + + if (ve === null) { + ve = new VeOCEAN(id) + ve.unlockTime = BigInt.zero() + ve.lockedAmount = BigDecimal.zero() + ve.block = 0 + ve.save() + } + + return ve +} + +export function getDeposit(id: string): VeDeposit { + let deposit = VeDeposit.load(id) + + if (deposit === null) { + deposit = new VeDeposit(id) + deposit.provider = '' + deposit.value = BigDecimal.zero() + deposit.unlockTime = BigInt.zero() + deposit.type = BigInt.zero() + deposit.timestamp = BigInt.zero() + deposit.save() + } + return deposit +} + +export function handleOneAllocation( + eventSender: string, + nftAddress: string, + chainId: BigInt, + allocationAmount: BigDecimal, + event: ethereum.Event +): void { + const eventId = nftAddress + '-' + chainId.toString() + + const allocateUser = getveAllocateUser(event, eventSender) + const allocateId = getveAllocateId(event, eventId) + const veAllocation = getveAllocation(event, eventSender, eventId) + + // Update user allocation + const newUserAllocation = allocateUser.allocatedTotal.minus( + veAllocation.allocated + ) + allocateUser.allocatedTotal = newUserAllocation.plus(allocationAmount) + + // Update id allocation + const newIdAllocation = allocateId.allocatedTotal.minus( + veAllocation.allocated + ) + allocateId.allocatedTotal = newIdAllocation.plus(allocationAmount) + + veAllocation.allocated = allocationAmount + veAllocation.chainId = chainId + veAllocation.nftAddress = nftAddress + + allocateUser.lastContact = event.block.timestamp.toI32() + allocateId.lastContact = event.block.timestamp.toI32() + veAllocation.lastContact = event.block.timestamp.toI32() + + // register allocation update event + writeveAllocationUpdate( + event, + veAllocation.id, + veAllocationUpdateType.SET, + allocationAmount + ) + + // save entities + allocateUser.save() + allocateId.save() + veAllocation.save() +} diff --git a/src/mappings/veAllocate.ts b/src/mappings/veAllocate.ts new file mode 100644 index 0000000..17e0117 --- /dev/null +++ b/src/mappings/veAllocate.ts @@ -0,0 +1,35 @@ +import { + AllocationSet, + AllocationSetMultiple +} from '../@types/veAllocate/veAllocate' + +import { handleOneAllocation } from './utils/veUtils' + +export function handleAllocationSet(event: AllocationSet): void { + // get allocation entities + const eventSender = event.params.sender.toHexString() + const nftAddress = event.params.nft.toHexString() + const chainId = event.params.chainId + const allocationAmount = event.params.amount.toBigDecimal() + + handleOneAllocation(eventSender, nftAddress, chainId, allocationAmount, event) +} + +export function handleAllocationSetMultiple( + event: AllocationSetMultiple +): void { + // loop + for (let i = 0; i < event.params.nft.length; i++) { + const eventSender = event.params.sender.toHexString() + const nftAddress = event.params.nft[i].toHexString() + const chainId = event.params.chainId[i] + const allocationAmount = event.params.amount[i].toBigDecimal() + handleOneAllocation( + eventSender, + nftAddress, + chainId, + allocationAmount, + event + ) + } +} diff --git a/src/mappings/veDelegation.ts b/src/mappings/veDelegation.ts new file mode 100644 index 0000000..b69bc7c --- /dev/null +++ b/src/mappings/veDelegation.ts @@ -0,0 +1,63 @@ +import { BigInt } from '@graphprotocol/graph-ts' +import { + BurnBoost, + DelegateBoost, + ExtendBoost, + TransferBoost +} from '../@types/veDelegation/veDelegation' +import { getveDelegation, getveOCEAN } from './utils/veUtils' + +export function handleDelegation(event: DelegateBoost): void { + const _delegator = event.params._delegator.toHex() + const _receiver = event.params._receiver.toHex() + const _tokenId = event.params._token_id + const _amount = event.params._amount + const _cancelTime = event.params._cancel_time + const _expireTime = event.params._expire_time + + const veDelegation = getveDelegation(_tokenId.toHex()) + veDelegation.delegator = _delegator + getveOCEAN(_receiver) + veDelegation.receiver = _receiver + veDelegation.tokenId = _tokenId + veDelegation.amount = _amount + veDelegation.cancelTime = _cancelTime + veDelegation.expireTime = _expireTime + veDelegation.block = event.block.number.toI32() + veDelegation.save() +} + +export function handleExtendBoost(event: ExtendBoost): void { + const _delegator = event.params._delegator.toHex() + const _receiver = event.params._receiver.toHex() + const _tokenId = event.params._token_id + const _amount = event.params._amount + const _cancelTime = event.params._cancel_time + const _expireTime = event.params._expire_time + + const veDelegation = getveDelegation(_tokenId.toHex()) + veDelegation.delegator = _delegator + veDelegation.receiver = _receiver + veDelegation.tokenId = _tokenId + veDelegation.amount = _amount + veDelegation.cancelTime = _cancelTime + veDelegation.expireTime = _expireTime + veDelegation.save() +} + +export function handleTransferBoost(event: TransferBoost): void { + // TODO not sure if we need this + // -------------------------------- + // const _from = event.params._from + // const _to = event.params._to + // const _tokenId = event.params._token_id + // const _amount = event.params._amount + // const _expireTime = event.params._expire_time +} +export function handleBurnBoost(event: BurnBoost): void { + const _tokenId = event.params._token_id + + // delete + const veDelegation = getveDelegation(_tokenId.toHex()) + veDelegation.amount = BigInt.zero() +} diff --git a/src/mappings/veOCEAN.ts b/src/mappings/veOCEAN.ts new file mode 100644 index 0000000..7e865c0 --- /dev/null +++ b/src/mappings/veOCEAN.ts @@ -0,0 +1,30 @@ +import { Deposit, Supply, Withdraw } from '../@types/veOCEAN/veOCEAN' +import { weiToDecimal } from './utils/generic' +import { getDeposit, getveOCEAN } from './utils/veUtils' + +export function handleDeposit(event: Deposit): void { + const provider = event.params.provider + const value = event.params.value + const locktime = event.params.locktime + const type = event.params.type + const ts = event.params.ts + + // Create new Deposit entity + const deposit = getDeposit(provider.toHex() + '-' + locktime.toString()) + deposit.provider = provider.toHex() + deposit.value = weiToDecimal(value.toBigDecimal(), 18) + deposit.unlockTime = locktime + deposit.type = type + deposit.timestamp = ts + deposit.save() + // -------------------------------------------- + + const veOCEAN = getveOCEAN(provider.toHex()) + const lockedAmount = weiToDecimal(value.toBigDecimal(), 18) + veOCEAN.unlockTime = locktime + veOCEAN.lockedAmount = veOCEAN.lockedAmount.plus(lockedAmount) + veOCEAN.block = event.block.number.toI32() + veOCEAN.save() +} +export function handleSupply(event: Supply): void {} +export function handleWithdraw(event: Withdraw): void {} diff --git a/subgraph.template.yaml b/subgraph.template.yaml index 8a9cea7..83dcce6 100644 --- a/subgraph.template.yaml +++ b/subgraph.template.yaml @@ -3,71 +3,6 @@ description: Ocean provides data sharing through IDOs repository: https://github.com/oceanprotocol/ocean-subgraph schema: file: ./schema.graphql -dataSources: - - kind: ethereum/contract - name: ERC721Factory - network: __NETWORK__ - source: - address: __ERC721FACTORYADDRESS__ - abi: ERC721Factory - startBlock: __STARTBLOCK__ - mapping: - kind: ethereum/events - apiVersion: 0.0.6 - language: wasm/assemblyscript - file: ./src/mappings/erc721Factory.ts - entities: - - ERC721Factory - abis: - - name: ERC721Factory - file: ./node_modules/@oceanprotocol/contracts/artifacts/contracts/ERC721Factory.sol/ERC721Factory.json - - name: ERC20 - file: ./abis/ERC20.json - eventHandlers: - - event: NFTCreated(address,indexed address,string,indexed address,string,string,bool,indexed address) - handler: handleNftCreated - - event: TokenCreated(indexed address,indexed address,string,string,uint256,address) - handler: handleNewToken - - - kind: ethereum/contract - name: FactoryRouter - network: __NETWORK__ - source: - address: __FACTORYROUTERADDRESS__ - abi: FactoryRouter - startBlock: __STARTBLOCK__ - mapping: - kind: ethereum/events - apiVersion: 0.0.6 - language: wasm/assemblyscript - file: ./src/mappings/factoryRouter.ts - entities: - - FactoryRouter - abis: - - name: FactoryRouter - file: ./node_modules/@oceanprotocol/contracts/artifacts/contracts/pools/FactoryRouter.sol/FactoryRouter.json - - name: ERC20 - file: ./abis/ERC20.json - eventHandlers: - - event: TokenAdded(indexed address,indexed address) - handler: handleTokenAdded - - event: TokenRemoved(indexed address,indexed address) - handler: handleTokenRemoved - - event: OPCFeeChanged(indexed address,uint256,uint256,uint256,uint256) - handler: handleOPCFeeChanged - - event: SSContractAdded(indexed address,indexed address) - handler: handleSSContractAdded - - event: SSContractRemoved(indexed address,indexed address) - handler: handleSSContractRemoved - - event: FixedRateContractAdded(indexed address,indexed address) - handler: handleFixedRateContractAdded - - event: FixedRateContractRemoved(indexed address,indexed address) - handler: handleFixedRateContractRemoved - - event: DispenserContractAdded(indexed address,indexed address) - handler: handleDispenserContractAdded - - event: DispenserContractRemoved(indexed address,indexed address) - handler: handleDispenserContractRemoved - templates: - name: ERC20Template kind: ethereum/contract @@ -230,3 +165,68 @@ templates: handler: handlePublishMarketFeeChanged - event: TokenCollected(indexed bytes32,indexed address,indexed address,uint256) handler: handleTokenCollected + +dataSources: + - kind: ethereum/contract + name: ERC721Factory + network: __NETWORK__ + source: + address: __ERC721FACTORYADDRESS__ + abi: ERC721Factory + startBlock: __STARTBLOCK__ + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + file: ./src/mappings/erc721Factory.ts + entities: + - ERC721Factory + abis: + - name: ERC721Factory + file: ./node_modules/@oceanprotocol/contracts/artifacts/contracts/ERC721Factory.sol/ERC721Factory.json + - name: ERC20 + file: ./abis/ERC20.json + eventHandlers: + - event: NFTCreated(address,indexed address,string,indexed address,string,string,bool,indexed address) + handler: handleNftCreated + - event: TokenCreated(indexed address,indexed address,string,string,uint256,address) + handler: handleNewToken + + - kind: ethereum/contract + name: FactoryRouter + network: __NETWORK__ + source: + address: __FACTORYROUTERADDRESS__ + abi: FactoryRouter + startBlock: __STARTBLOCK__ + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + file: ./src/mappings/factoryRouter.ts + entities: + - FactoryRouter + abis: + - name: FactoryRouter + file: ./node_modules/@oceanprotocol/contracts/artifacts/contracts/pools/FactoryRouter.sol/FactoryRouter.json + - name: ERC20 + file: ./abis/ERC20.json + eventHandlers: + - event: TokenAdded(indexed address,indexed address) + handler: handleTokenAdded + - event: TokenRemoved(indexed address,indexed address) + handler: handleTokenRemoved + - event: OPCFeeChanged(indexed address,uint256,uint256,uint256,uint256) + handler: handleOPCFeeChanged + - event: SSContractAdded(indexed address,indexed address) + handler: handleSSContractAdded + - event: SSContractRemoved(indexed address,indexed address) + handler: handleSSContractRemoved + - event: FixedRateContractAdded(indexed address,indexed address) + handler: handleFixedRateContractAdded + - event: FixedRateContractRemoved(indexed address,indexed address) + handler: handleFixedRateContractRemoved + - event: DispenserContractAdded(indexed address,indexed address) + handler: handleDispenserContractAdded + - event: DispenserContractRemoved(indexed address,indexed address) + handler: handleDispenserContractRemoved diff --git a/subgraph_ve.template.yaml b/subgraph_ve.template.yaml new file mode 100644 index 0000000..a86c48a --- /dev/null +++ b/subgraph_ve.template.yaml @@ -0,0 +1,68 @@ +- name: veAllocate + kind: ethereum/contract + network: __NETWORK__ + source: + abi: veAllocate + address: __VEALLOCATEADDRESS__ + startBlock: __STARTBLOCK__ + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + file: ./src/mappings/veAllocate.ts + entities: + - veAllocate + abis: + - name: veAllocate + file: ./node_modules/@oceanprotocol/contracts/artifacts/contracts/ve/veAllocate.sol/veAllocate.json + eventHandlers: + - event: AllocationSet(indexed address,indexed address,indexed uint256,uint256) + handler: handleAllocationSet + - event: AllocationSetMultiple(indexed address,address[],uint256[],uint256[]) + handler: handleAllocationSetMultiple + +- name: veOCEAN + kind: ethereum/contract + network: __NETWORK__ + source: + abi: veOCEAN + address: __VEOCEANADDRESS__ + startBlock: __STARTBLOCK__ + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + file: ./src/mappings/veOCEAN.ts + entities: + - veOCEAN + abis: + - name: veOCEAN + file: ./node_modules/@oceanprotocol/contracts/artifacts/contracts/ve/veOCEAN.vy/veOCEAN.json + eventHandlers: + - event: Deposit(indexed address,uint256,indexed uint256,int128,uint256) + handler: handleDeposit + - event: Withdraw(indexed address,uint256,uint256) + handler: handleSupply + - event: Supply(uint256,uint256) + handler: handleWithdraw + +- name: veDelegation + kind: ethereum/contract + network: __NETWORK__ + source: + abi: veDelegation + address: __VEDELEGATIONADDRESS__ + startBlock: __STARTBLOCK__ + mapping: + kind: ethereum/events + apiVersion: 0.0.6 + language: wasm/assemblyscript + file: ./src/mappings/veDelegation.ts + entities: + - veDelegation + abis: + - name: veDelegation + file: ./node_modules/@oceanprotocol/contracts/artifacts/contracts/ve/veDelegation.vy/veDelegation.json + eventHandlers: + - event: DelegateBoost(indexed address,indexed address,indexed uint256,uint256,uint256,uint256) + handler: handleDelegation diff --git a/test/integration/SimpleSubgraph.test.ts b/test/integration/SimpleSubgraph.test.ts new file mode 100644 index 0000000..d7d88aa --- /dev/null +++ b/test/integration/SimpleSubgraph.test.ts @@ -0,0 +1,124 @@ +import { + Erc20CreateParams, + Nft, + NftFactory, + NftCreateData, + sleep +} from '@oceanprotocol/lib' +import { assert } from 'chai' +import Web3 from 'web3' +import { homedir } from 'os' +import fs from 'fs' +import { fetch } from 'cross-fetch' + +const data = JSON.parse( + fs.readFileSync( + process.env.ADDRESS_FILE || + `${homedir}/.ocean/ocean-contracts/artifacts/address.json`, + 'utf8' + ) +) + +const addresses = data.development +const web3 = new Web3('http://127.0.0.1:8545') + +const subgraphUrl = + 'http://127.0.0.1:9000/subgraphs/name/oceanprotocol/ocean-subgraph' + +describe('Tests coverage without provider/aquarius', async () => { + let nft: Nft + let Factory: NftFactory + let accounts: string[] + let publisherAccount: string + let newOwnerAccount: string + + before(async () => { + nft = new Nft(web3) + Factory = new NftFactory(addresses.ERC721Factory, web3) + accounts = await web3.eth.getAccounts() + publisherAccount = accounts[0] + newOwnerAccount = accounts[1].toLowerCase() + }) + + it('should publish a dataset (create NFT + ERC20)', async () => { + const nftParams: NftCreateData = { + name: 'testNFT', + symbol: 'TST', + templateIndex: 1, + tokenURI: '', + transferable: true, + owner: publisherAccount + } + const erc20Params: Erc20CreateParams = { + templateIndex: 1, + cap: '100000', + feeAmount: '0', + paymentCollector: '0x0000000000000000000000000000000000000000', + feeToken: '0x0000000000000000000000000000000000000000', + minter: publisherAccount, + mpFeeAddress: '0x0000000000000000000000000000000000000000' + } + const result = await Factory.createNftWithErc20( + publisherAccount, + nftParams, + erc20Params + ) + const erc721Address = result.events.NFTCreated.returnValues[0] + + // graph tests here + await sleep(2000) + const graphNftToken = erc721Address.toLowerCase() + const query = { + query: `query { + nft(id:"${graphNftToken}"){symbol,id}}` + } + const response = await fetch(subgraphUrl, { + method: 'POST', + body: JSON.stringify(query) + }) + const queryResult = await response.json() + assert(queryResult.data.nft.id === graphNftToken) + }) + + it('should publish and transfer an NFT', async () => { + const nftParams: NftCreateData = { + name: 'testNFT', + symbol: 'TST', + templateIndex: 1, + tokenURI: '', + transferable: true, + owner: publisherAccount + } + const erc20Params: Erc20CreateParams = { + templateIndex: 1, + cap: '100000', + feeAmount: '0', + paymentCollector: '0x0000000000000000000000000000000000000000', + feeToken: '0x0000000000000000000000000000000000000000', + minter: publisherAccount, + mpFeeAddress: '0x0000000000000000000000000000000000000000' + } + const result = await Factory.createNftWithErc20( + publisherAccount, + nftParams, + erc20Params + ) + await sleep(2000) + const erc721Address = result.events.NFTCreated.returnValues[0] + const graphNftToken = erc721Address.toLowerCase() + + // Transfer the NFT + await nft.transferNft(graphNftToken, publisherAccount, newOwnerAccount) + await sleep(2000) + const query2 = { + query: `query { + nft(id:"${graphNftToken}"){symbol,id,owner, transferable}}` + } + const response = await fetch(subgraphUrl, { + method: 'POST', + body: JSON.stringify(query2) + }) + const queryResult = await response.json() + assert(queryResult.data.nft.owner === newOwnerAccount) + }) +})