add dispenser (#106)

* add dispenser


Co-authored-by: mihaisc <mihai.scarlat@smartcontrol.ro>
Co-authored-by: Jamie Hewitt <jamie.hewitt15@gmail.com>
This commit is contained in:
Alex Coseru 2021-05-13 09:19:21 +03:00 committed by GitHub
parent 07b02c2b5a
commit f47ec9bb09
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 18298 additions and 206 deletions

3
.gitignore vendored
View File

@ -65,3 +65,6 @@ typings/
# next.js build output # next.js build output
.next .next
# auto generated barge yaml
subgraph.barge.yaml

View File

@ -133,6 +133,7 @@ export ADDRESS_FILE="${HOME}/.ocean/ocean-contracts/artifacts/address.json"
4. Generate the subgraph 4. Generate the subgraph
```bash ```bash
npm run codegen
npm run bargesetup npm run bargesetup
``` ```

17468
abis/Dispenser.json Normal file

File diff suppressed because one or more lines are too long

82
package-lock.json generated
View File

@ -399,17 +399,17 @@
} }
}, },
"@oceanprotocol/contracts": { "@oceanprotocol/contracts": {
"version": "0.5.16", "version": "0.6.2",
"resolved": "https://registry.npmjs.org/@oceanprotocol/contracts/-/contracts-0.5.16.tgz", "resolved": "https://registry.npmjs.org/@oceanprotocol/contracts/-/contracts-0.6.2.tgz",
"integrity": "sha512-p7aFIUT8RVoMzdPP7ML8G08BnQ09syywKjOT16hqJm0GmofunEuVffUXbryG4EkQ+qRbf/zeoxSmesi79kQXlA==" "integrity": "sha512-J6amHsmVbdc2rAwbUYOaY7inLV13GxPIiqbsLF78nmdIvhhGDhT2LYMyfQtxkMwQzYDP6EzD4albCgOXlWM15g=="
}, },
"@oceanprotocol/lib": { "@oceanprotocol/lib": {
"version": "0.14.8", "version": "0.14.9",
"resolved": "https://registry.npmjs.org/@oceanprotocol/lib/-/lib-0.14.8.tgz", "resolved": "https://registry.npmjs.org/@oceanprotocol/lib/-/lib-0.14.9.tgz",
"integrity": "sha512-eqab5iEgowyIM/LcDDs6xhZo/KToOmVw0betjXLG0+g70zS8R6XL2RHzCpFyutSdf/cH0w/ltPUfR8ZBElIyhQ==", "integrity": "sha512-PUNbsryrm4614CbvalP2CZS9LZjvWfqopTdb91Rz4vLbnqinioELSFXNq+hiSkyuFqAy0szG0wueBcW8mwFbSg==",
"requires": { "requires": {
"@ethereum-navigator/navigator": "^0.5.2", "@ethereum-navigator/navigator": "^0.5.2",
"@oceanprotocol/contracts": "0.5.16", "@oceanprotocol/contracts": "^0.6.2",
"@types/crypto-js": "^4.0.1", "@types/crypto-js": "^4.0.1",
"cross-fetch": "^3.1.2", "cross-fetch": "^3.1.2",
"crypto-js": "^4.0.0", "crypto-js": "^4.0.0",
@ -5163,9 +5163,9 @@
"integrity": "sha512-WRRyllsGXJM7ZN7gPTCCQ/6wNPTRDwiWdPK66l5sJzcU/oOzcIcRRf0Rux8bkpox/1yjt0F6VJRsQOIG2qz5sg==" "integrity": "sha512-WRRyllsGXJM7ZN7gPTCCQ/6wNPTRDwiWdPK66l5sJzcU/oOzcIcRRf0Rux8bkpox/1yjt0F6VJRsQOIG2qz5sg=="
}, },
"is-bigint": { "is-bigint": {
"version": "1.0.1", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.1.tgz", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz",
"integrity": "sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg==" "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA=="
}, },
"is-binary-path": { "is-binary-path": {
"version": "2.1.0", "version": "2.1.0",
@ -5182,11 +5182,32 @@
"integrity": "sha1-o9fZb+HD/wZex84nwsIea6ksGDI=" "integrity": "sha1-o9fZb+HD/wZex84nwsIea6ksGDI="
}, },
"is-boolean-object": { "is-boolean-object": {
"version": "1.1.0", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.0.tgz", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz",
"integrity": "sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA==", "integrity": "sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==",
"requires": { "requires": {
"call-bind": "^1.0.0" "call-bind": "^1.0.2"
},
"dependencies": {
"call-bind": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
"requires": {
"function-bind": "^1.1.1",
"get-intrinsic": "^1.0.2"
}
},
"get-intrinsic": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
"integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
"requires": {
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1"
}
}
} }
}, },
"is-buffer": { "is-buffer": {
@ -5252,9 +5273,9 @@
"integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==" "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ=="
}, },
"is-generator-function": { "is-generator-function": {
"version": "1.0.8", "version": "1.0.9",
"resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.8.tgz", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.9.tgz",
"integrity": "sha512-2Omr/twNtufVZFr1GhxjOMFPAj2sjc/dKaIqBhvo4qciXfJmITGH6ZGd8eZYNHza8t1y0e01AuqRhJwfWp26WQ==" "integrity": "sha512-ZJ34p1uvIfptHCN7sFTjGibB9/oBg17sHqzDLfuwhvmN/qLVvIQXRQ8licZQ35WJ8KuEQt/etnnzQFI9C9Ue/A=="
}, },
"is-glob": { "is-glob": {
"version": "4.0.1", "version": "4.0.1",
@ -5413,9 +5434,9 @@
"dev": true "dev": true
}, },
"is-number-object": { "is-number-object": {
"version": "1.0.4", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz",
"integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==" "integrity": "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw=="
}, },
"is-obj": { "is-obj": {
"version": "2.0.0", "version": "2.0.0",
@ -5566,12 +5587,19 @@
"integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==" "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ=="
}, },
"is-regex": { "is-regex": {
"version": "1.1.2", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz",
"integrity": "sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg==", "integrity": "sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==",
"requires": { "requires": {
"call-bind": "^1.0.2", "call-bind": "^1.0.2",
"has-symbols": "^1.0.1" "has-symbols": "^1.0.2"
},
"dependencies": {
"has-symbols": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
"integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw=="
}
} }
}, },
"string.prototype.trimend": { "string.prototype.trimend": {
@ -9818,9 +9846,9 @@
} }
}, },
"utf-8-validate": { "utf-8-validate": {
"version": "5.0.4", "version": "5.0.5",
"resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.4.tgz", "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.5.tgz",
"integrity": "sha512-MEF05cPSq3AwJ2C7B7sHAA6i53vONoZbMGX8My5auEVm6W+dJ2Jd/TZPyGJ5CH42V2XtbI5FD28HeHeqlPzZ3Q==", "integrity": "sha512-+pnxRYsS/axEpkrrEpzYfNZGXp0IjC/9RIxwM5gntY4Koi8SHmUGSfxfWqxZdRxrtaoVstuOzUp/rbs3JSPELQ==",
"requires": { "requires": {
"node-gyp-build": "^4.2.0" "node-gyp-build": "^4.2.0"
} }

View File

@ -54,7 +54,7 @@
"typescript": "^4.2.4" "typescript": "^4.2.4"
}, },
"dependencies": { "dependencies": {
"@oceanprotocol/lib": "^0.14.8", "@oceanprotocol/lib": "^0.14.9",
"cross-fetch": "^3.1.4" "cross-fetch": "^3.1.4"
}, },
"repository": { "repository": {

View File

@ -1,239 +1,255 @@
type PoolFactory @entity { type PoolFactory @entity {
id: ID! id: ID!
totalValueLocked: BigDecimal # total value from all pools expressed in OCEAN totalValueLocked: BigDecimal # total value from all pools expressed in OCEAN
totalOceanLiquidity: BigDecimal! # Total of OCEAN liquidity from all pools
totalOceanLiquidity: BigDecimal! # Total of OCEAN liquidity from all pools totalSwapVolume: BigDecimal! # All the swap volume in Ocean
totalSwapVolume: BigDecimal! # All the swap volume in Ocean totalSwapFee: BigDecimal! # All the swap fee in Ocean
totalSwapFee: BigDecimal! # All the swap fee in Ocean poolCount: Int! # Number of pools
finalizedPoolCount: Int! # Number of finalized pools
poolCount: Int! # Number of pools pools: [Pool!] @derivedFrom(field: "factoryID")
finalizedPoolCount: Int! # Number of finalized pools
pools: [Pool!] @derivedFrom(field: "factoryID")
} }
type Pool @entity { type Pool @entity {
id: ID! # Pool address id: ID! # Pool address
factoryID: PoolFactory! factoryID: PoolFactory!
controller: Bytes! # Controller address controller: Bytes! # Controller address
publicSwap: Boolean! # isPublicSwap publicSwap: Boolean! # isPublicSwap
finalized: Boolean! # isFinalized finalized: Boolean! # isFinalized
symbol: String # Pool token symbol symbol: String # Pool token symbol
name: String # Pool token name name: String # Pool token name
cap: BigInt # Maximum supply if any cap: BigInt # Maximum supply if any
active: Boolean! # isActive active: Boolean! # isActive
swapFee: BigDecimal! # Swap Fees swapFee: BigDecimal! # Swap Fees
totalWeight: BigDecimal!
totalShares: BigDecimal! # Total pool token shares
totalSwapVolume: BigDecimal! # Total swap volume in OCEAN
totalSwapFee: BigDecimal! # Total swap fee in OCEAN
valueLocked: BigDecimal! # value locked in pool expressed in OCEAN (captures both Ocean and Datatoken)
datatokenReserve: BigDecimal! # Total pool reserve of Datatoken
oceanReserve: BigDecimal! # Total pool reserve of OCEAN
spotPrice: BigDecimal!
consumePrice: BigDecimal!
totalWeight: BigDecimal! tokenCount: BigInt! # Number of tokens in the pool
totalShares: BigDecimal! # Total pool token shares holderCount: BigInt! # Number of addresses holding a positive balance of pool shares
totalSwapVolume: BigDecimal! # Total swap volume in OCEAN joinCount: BigInt! # liquidity has been added
totalSwapFee: BigDecimal! # Total swap fee in OCEAN exitCount: BigInt! # liquidity has been removed
swapCount: BigInt!
valueLocked: BigDecimal! # value locked in pool expressed in OCEAN (captures both Ocean and Datatoken) transactionCount: BigInt! # Number of transactions in this pool involving liquidity changes
datatokenReserve: BigDecimal! # Total pool reserve of Datatoken datatokenAddress: String!
oceanReserve: BigDecimal! # Total pool reserve of OCEAN createTime: Int! # Block time pool was created
spotPrice: BigDecimal! tx: Bytes # Pool creation transaction id
consumePrice: BigDecimal! tokens: [PoolToken!] @derivedFrom(field: "poolId")
shares: [PoolShare!] @derivedFrom(field: "poolId")
tokenCount: BigInt! # Number of tokens in the pool transactions: [PoolTransaction!] @derivedFrom(field: "poolAddress")
holderCount: BigInt! # Number of addresses holding a positive balance of pool shares transactionsTokenValues: [PoolTransactionTokenValues!]
joinCount: BigInt! # liquidity has been added @derivedFrom(field: "poolAddress")
exitCount: BigInt! # liquidity has been removed
swapCount: BigInt!
transactionCount: BigInt! # Number of transactions in this pool involving liquidity changes
datatokenAddress: String!
createTime: Int! # Block time pool was created
tx: Bytes # Pool creation transaction id
tokens: [PoolToken!] @derivedFrom(field: "poolId")
shares: [PoolShare!] @derivedFrom(field: "poolId")
transactions: [PoolTransaction!] @derivedFrom(field: "poolAddress")
transactionsTokenValues: [PoolTransactionTokenValues!] @derivedFrom(field: "poolAddress")
} }
type PoolToken @entity { type PoolToken @entity {
id: ID! # poolId + token address id: ID! # poolId + token address
poolId: Pool! poolId: Pool!
tokenId: Datatoken tokenId: Datatoken
tokenAddress: String tokenAddress: String
balance: BigDecimal! balance: BigDecimal!
denormWeight: BigDecimal! denormWeight: BigDecimal!
} }
type PoolShare @entity { type PoolShare @entity {
id: ID! # poolId + userAddress id: ID! # poolId + userAddress
userAddress: User! userAddress: User!
poolId: Pool! poolId: Pool!
balance: BigDecimal! balance: BigDecimal!
} }
type PoolTransactionTokenValues @entity { type PoolTransactionTokenValues @entity {
id: ID! # pool tx + tokenAddress id: ID! # pool tx + tokenAddress
txId: PoolTransaction! txId: PoolTransaction!
poolToken: PoolToken! poolToken: PoolToken!
poolAddress: Pool! poolAddress: Pool!
userAddress: User! userAddress: User!
tokenAddress: String! tokenAddress: String!
value: BigDecimal! value: BigDecimal!
tokenReserve: BigDecimal! tokenReserve: BigDecimal!
feeValue: BigDecimal! # Swap fee value in OCEAN feeValue: BigDecimal! # Swap fee value in OCEAN
type: String! type: String!
} }
type PoolTransaction @entity { type PoolTransaction @entity {
id: ID! # pool tx id: ID! # pool tx
poolAddress: Pool poolAddress: Pool
userAddress: User # User address that initiates the swap userAddress: User # User address that initiates the swap
poolAddressStr: String! poolAddressStr: String!
userAddressStr: String! userAddressStr: String!
sharesTransferAmount: BigDecimal! # sharesTransferAmount: BigDecimal! #
sharesBalance: BigDecimal! sharesBalance: BigDecimal!
spotPrice: BigDecimal! spotPrice: BigDecimal!
consumePrice: BigDecimal! consumePrice: BigDecimal!
tx: Bytes! tx: Bytes!
event: String event: String
block: Int! block: Int!
timestamp: Int! timestamp: Int!
gasUsed: BigDecimal! gasUsed: BigDecimal!
gasPrice: BigDecimal! gasPrice: BigDecimal!
oceanReserve: BigDecimal! oceanReserve: BigDecimal!
datatokenReserve: BigDecimal! datatokenReserve: BigDecimal!
tokens: [PoolTransactionTokenValues!] @derivedFrom(field: "txId") tokens: [PoolTransactionTokenValues!] @derivedFrom(field: "txId")
} }
type DatatokenFactory @entity { type DatatokenFactory @entity {
id: ID! id: ID!
tokenCount: Int! # Number of datatokens tokenCount: Int! # Number of datatokens
datatokens: [Datatoken!] @derivedFrom(field: "factoryID")
datatokens: [Datatoken!] @derivedFrom(field: "factoryID")
} }
type Datatoken @entity { type Datatoken @entity {
id: ID! # token address id: ID! # token address
factoryID: DatatokenFactory! factoryID: DatatokenFactory!
symbol: String symbol: String
name: String name: String
decimals: Int! decimals: Int!
address: String! address: String!
cap: BigDecimal! cap: BigDecimal!
supply: BigDecimal! supply: BigDecimal!
minter: User! minter: User!
publisher: String! publisher: String!
holderCount: BigInt! # Number of addresses holding a balance of datatoken holderCount: BigInt! # Number of addresses holding a balance of datatoken
orderCount: BigInt! # Number of orders executed for this dataset orderCount: BigInt! # Number of orders executed for this dataset
metadataUpdateCount: BigInt! metadataUpdateCount: BigInt!
createTime: Int! # Block time datatoken was created createTime: Int! # Block time datatoken was created
tx: Bytes # Datatoken creation transaction id tx: Bytes # Datatoken creation transaction id
balances: [TokenBalance!] @derivedFrom(field: "datatokenId")
balances: [TokenBalance!] @derivedFrom(field: "datatokenId") orders: [TokenOrder!] @derivedFrom(field: "datatokenId")
orders: [TokenOrder!] @derivedFrom(field: "datatokenId") updates: [MetadataUpdate!] @derivedFrom(field: "datatokenId") # list of MetadataUpdate objects
updates: [MetadataUpdate!] @derivedFrom(field: "datatokenId") # list of MetadataUpdate objects
} }
type MetadataUpdate @entity { type MetadataUpdate @entity {
id: ID! # update tx + datatokenAddress id: ID! # update tx + datatokenAddress
datatokenId: Datatoken! datatokenId: Datatoken!
datatokenAddress: String! datatokenAddress: String!
userAddress: String! userAddress: String!
block: Int! block: Int!
timestamp: Int! timestamp: Int!
tx: Bytes! tx: Bytes!
} }
type TokenOrder @entity { type TokenOrder @entity {
id: ID! # datatokenId + userAddress + tx id: ID! # datatokenId + userAddress + tx
datatokenId: Datatoken!
datatokenId: Datatoken! consumer: User!
payer: User!
amount: BigDecimal!
serviceId: Int!
marketFeeCollector: User
marketFee: BigDecimal!
consumer: User! timestamp: Int!
payer: User! tx: Bytes
amount: BigDecimal! block: Int!
serviceId: Int!
marketFeeCollector: User
marketFee: BigDecimal!
timestamp: Int!
tx: Bytes
block: Int!
} }
type TokenBalance @entity { type TokenBalance @entity {
id: ID! # datatokenId + userAddress id: ID! # datatokenId + userAddress
userAddress: User! userAddress: User!
datatokenId: Datatoken! datatokenId: Datatoken!
balance: BigDecimal! balance: BigDecimal!
} }
type TokenTransaction @entity { type TokenTransaction @entity {
id: ID! # Log ID id: ID! # Log ID
event: String
datatokenAddress: Datatoken
userAddress: User
event: String block: Int!
datatokenAddress: Datatoken gasUsed: BigDecimal!
userAddress: User gasPrice: BigDecimal!
timestamp: Int!
block: Int! tx: Bytes!
gasUsed: BigDecimal!
gasPrice: BigDecimal!
timestamp: Int!
tx: Bytes!
} }
type User @entity { type User @entity {
id: ID! id: ID!
sharesOwned: [PoolShare!] @derivedFrom(field: "userAddress") sharesOwned: [PoolShare!] @derivedFrom(field: "userAddress")
tokenBalancesOwned: [TokenBalance!] @derivedFrom(field: "userAddress") tokenBalancesOwned: [TokenBalance!] @derivedFrom(field: "userAddress")
tokensOwned: [Datatoken!] @derivedFrom(field: "minter") tokensOwned: [Datatoken!] @derivedFrom(field: "minter")
poolTransactions: [PoolTransaction!] @derivedFrom(field: "userAddress") poolTransactions: [PoolTransaction!] @derivedFrom(field: "userAddress")
poolTransactionsTokenValues: [PoolTransactionTokenValues!] @derivedFrom(field: "userAddress") poolTransactionsTokenValues: [PoolTransactionTokenValues!]
tokenTransactions: [TokenTransaction!] @derivedFrom(field: "userAddress") @derivedFrom(field: "userAddress")
orders: [TokenOrder!] @derivedFrom(field: "payer") tokenTransactions: [TokenTransaction!] @derivedFrom(field: "userAddress")
freSwaps: [FixedRateExchangeSwap!] @derivedFrom(field: "by") orders: [TokenOrder!] @derivedFrom(field: "payer")
freSwaps: [FixedRateExchangeSwap!] @derivedFrom(field: "by")
} }
type FixedRateExchange @entity { type FixedRateExchange @entity {
id: ID! # fixed rate exchange id id: ID! # fixed rate exchange id
exchangeOwner: User! exchangeOwner: User!
datatoken: Datatoken! datatoken: Datatoken!
baseToken: String! baseToken: String!
rate: BigDecimal! rate: BigDecimal!
active: Boolean! active: Boolean!
updates: [FixedRateExchangeUpdate!] @derivedFrom(field: "exchangeId") updates: [FixedRateExchangeUpdate!] @derivedFrom(field: "exchangeId")
swaps: [FixedRateExchangeSwap!] @derivedFrom(field: "exchangeId") swaps: [FixedRateExchangeSwap!] @derivedFrom(field: "exchangeId")
} }
type FixedRateExchangeUpdate @entity { type FixedRateExchangeUpdate @entity {
id: ID! id: ID!
exchangeId: FixedRateExchange! exchangeId: FixedRateExchange!
oldRate: BigDecimal! oldRate: BigDecimal!
newRate: BigDecimal! newRate: BigDecimal!
oldActive: Boolean! oldActive: Boolean!
newActive: Boolean! newActive: Boolean!
block: Int! block: Int!
timestamp: Int! timestamp: Int!
tx: Bytes! tx: Bytes!
} }
type FixedRateExchangeSwap @entity { type FixedRateExchangeSwap @entity {
id: ID! id: ID!
exchangeId: FixedRateExchange! exchangeId: FixedRateExchange!
by: User! by: User!
baseTokenAmount: BigDecimal! baseTokenAmount: BigDecimal!
dataTokenAmount: BigDecimal! dataTokenAmount: BigDecimal!
block: Int! block: Int!
timestamp: Int! timestamp: Int!
tx: Bytes! tx: Bytes!
} }
type Dispenser @entity {
id: ID! # dispenser datatoken
active: Boolean!
owner: User!
minterApproved: Boolean!
isTrueMinter: Boolean!
maxTokens: BigDecimal!
maxBalance: BigDecimal!
balance: BigDecimal!
datatoken: Datatoken!
dispenses: [DispenserTransaction!] @derivedFrom(field: "dispenserId")
}
type DispenserTransaction @entity {
id: ID!
dispenserId: Dispenser!
datatoken: Datatoken!
user: User!
amount: BigDecimal!
block: Int!
timestamp: Int!
tx: Bytes!
type: String!
}

View File

@ -12,7 +12,9 @@ async function replaceContractAddresses() {
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
FixedRateExchange, FixedRateExchange,
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
Metadata Metadata,
// eslint-disable-next-line no-unused-vars
Dispenser
} = data.development } = data.development
let subgraph = fs.readFileSync('subgraph.yaml', 'utf8') let subgraph = fs.readFileSync('subgraph.yaml', 'utf8')
if (!data) { if (!data) {
@ -38,6 +40,11 @@ async function replaceContractAddresses() {
/0x608d05214E42722B94a54cF6114d4840FCfF84e1/g, /0x608d05214E42722B94a54cF6114d4840FCfF84e1/g,
FixedRateExchange FixedRateExchange
) )
// dispenser
subgraph = subgraph.replace(
/0xDEfD0018969cd2d4E648209F876ADe184815f038/g,
Dispenser
)
// network // network
subgraph = subgraph.replace(/network: mainnet/g, 'network: barge') subgraph = subgraph.replace(/network: mainnet/g, 'network: barge')

176
src/mappings/dispenser.ts Normal file
View File

@ -0,0 +1,176 @@
import { Address, BigInt, ethereum, log } from '@graphprotocol/graph-ts'
import {
Activated,
Deactivated,
AcceptedMinter,
RemovedMinter,
TokensDispensed,
OwnerWithdrawed,
Dispenser as DispenserEntity
} from '../@types/Dispenser/Dispenser'
import {
Dispenser,
DispenserTransaction,
User,
Datatoken
} from '../@types/schema'
import { tokenToDecimal, _debuglog } from '../helpers'
function _processDispenserUpdate(
event: ethereum.Event,
datatoken: string,
contractAddress: Address
): void {
_debuglog('Start processDispenserUpdate', null, [
datatoken,
contractAddress.toHexString()
])
const dt = Datatoken.load(datatoken)
if (!dt) {
return
}
let dispenser = Dispenser.load(datatoken)
if (!dispenser) {
_debuglog('Creating new dispenser', null, null)
dispenser = new Dispenser(datatoken)
}
const dispenserEntity = DispenserEntity.bind(contractAddress)
_debuglog('Bound dispenser entity', null, null)
const dispenserStatus = dispenserEntity.try_status(
Address.fromString(datatoken)
)
_debuglog('Got status', null, null)
if (dispenserStatus.reverted) return
dispenser.active = dispenserStatus.value.value0
let owner = User.load(dispenserStatus.value.value1.toHexString())
if (!owner) {
owner = new User(dispenserStatus.value.value1.toHexString())
owner.save()
}
dispenser.owner = owner.id
dispenser.minterApproved = dispenserStatus.value.value2
dispenser.isTrueMinter = dispenserStatus.value.value3
dispenser.maxTokens = tokenToDecimal(
dispenserStatus.value.value4.toBigDecimal(),
18
)
dispenser.maxBalance = tokenToDecimal(
dispenserStatus.value.value5.toBigDecimal(),
18
)
dispenser.balance = tokenToDecimal(
dispenserStatus.value.value6.toBigDecimal(),
18
)
dispenser.datatoken = dt.id
dispenser.save()
}
export function handleDispenserActivated(event: Activated): void {
_debuglog('Start handleDispenserActivated', event, [
event.params.datatokenAddress.toHexString()
])
_processDispenserUpdate(
event,
event.params.datatokenAddress.toHexString(),
event.address
)
}
export function handleDispenserDeactivated(event: Deactivated): void {
_processDispenserUpdate(
event,
event.params.datatokenAddress.toHexString(),
event.address
)
}
export function handleDispenserAcceptedMinter(event: AcceptedMinter): void {
_processDispenserUpdate(
event,
event.params.datatokenAddress.toHexString(),
event.address
)
}
export function handleDispenserRemovedMinter(event: RemovedMinter): void {
_processDispenserUpdate(
event,
event.params.datatokenAddress.toHexString(),
event.address
)
}
export function handleDispenserTokensDispensed(event: TokensDispensed): void {
_processDispenserUpdate(
event,
event.params.datatokenAddress.toHexString(),
event.address
)
const dt = Datatoken.load(event.params.datatokenAddress.toHexString())
if (!dt) {
log.warning('Tokens dispensed, but no datatoken ? ', [
event.params.datatokenAddress.toHexString()
])
return
}
const tx = event.transaction.hash
const id = tx
.toHexString()
.concat('-')
.concat(event.params.datatokenAddress.toHexString())
log.info('Created dispenser in handleDispenserTokensDispensed with id {}', [
id
])
const dispensers = new DispenserTransaction(id)
dispensers.dispenserId = event.params.datatokenAddress.toHexString()
dispensers.datatoken = event.params.datatokenAddress.toHexString()
dispensers.user = event.params.userAddress.toHexString()
dispensers.amount = tokenToDecimal(
event.params.amount.toBigDecimal(),
BigInt.fromI32(18).toI32()
)
dispensers.block = event.block.number.toI32()
dispensers.timestamp = event.block.timestamp.toI32()
dispensers.tx = tx
dispensers.type = 'dispense'
dispensers.save()
}
export function handleDispenserOwnerWithdrawed(event: OwnerWithdrawed): void {
_processDispenserUpdate(
event,
event.params.datatoken.toHexString(),
event.address
)
const dt = Datatoken.load(event.params.datatoken.toHexString())
if (!dt) {
log.warning('Tokens dispensed, but no datatoken ? ', [
event.params.datatoken.toHexString()
])
return
}
const tx = event.transaction.hash
const id = tx
.toHexString()
.concat('-')
.concat(event.params.datatoken.toHexString())
log.info('Created dispenser in handleDispenserOwnerWithdrawed with id {} ', [
id
])
const dispensers = new DispenserTransaction(id)
dispensers.dispenserId = event.params.datatoken.toHexString()
dispensers.datatoken = event.params.datatoken.toHexString()
dispensers.user = event.params.owner.toHexString()
dispensers.amount = tokenToDecimal(
event.params.amount.toBigDecimal(),
BigInt.fromI32(18).toI32()
)
dispensers.block = event.block.number.toI32()
dispensers.timestamp = event.block.timestamp.toI32()
dispensers.tx = tx
dispensers.type = 'withdraw'
dispensers.save()
}

View File

@ -94,6 +94,36 @@ dataSources:
handler: handleExchangeRateChanged handler: handleExchangeRateChanged
- event: Swapped(indexed bytes32,indexed address,uint256,uint256) - event: Swapped(indexed bytes32,indexed address,uint256,uint256)
handler: handleSwapped handler: handleSwapped
- kind: ethereum/contract
name: Dispenser
network: moonbeamalpha
source:
address: '0x042D709b72B437d7d387F2679bD4ac060e561c9f'
abi: Dispenser
startBlock: 260710
mapping:
kind: ethereum/events
apiVersion: 0.0.4
language: wasm/assemblyscript
file: ./src/mappings/dispenser.ts
entities:
- Dispenser
abis:
- name: Dispenser
file: ./abis/Dispenser.json
eventHandlers:
- event: Activated(indexed address)
handler: handleDispenserActivated
- event: Deactivated(indexed address)
handler: handleDispenserDeactivated
- event: AcceptedMinter(indexed address)
handler: handleDispenserAcceptedMinter
- event: RemovedMinter(indexed address)
handler: handleDispenserRemovedMinter
- event: TokensDispensed(indexed address,indexed address,uint256)
handler: handleDispenserTokensDispensed
- event: OwnerWithdrawed(indexed address,indexed address,uint256)
handler: handleDispenserOwnerWithdrawed
templates: templates:
- kind: ethereum/contract - kind: ethereum/contract
name: Pool name: Pool

View File

@ -94,6 +94,36 @@ dataSources:
handler: handleExchangeRateChanged handler: handleExchangeRateChanged
- event: Swapped(indexed bytes32,indexed address,uint256,uint256) - event: Swapped(indexed bytes32,indexed address,uint256,uint256)
handler: handleSwapped handler: handleSwapped
- kind: ethereum/contract
name: Dispenser
network: polygon
source:
address: '0x30E4CC2C7A9c6aA2b2Ce93586E3Df24a3A00bcDD'
abi: Dispenser
startBlock: 14275634
mapping:
kind: ethereum/events
apiVersion: 0.0.4
language: wasm/assemblyscript
file: ./src/mappings/dispenser.ts
entities:
- Dispenser
abis:
- name: Dispenser
file: ./abis/Dispenser.json
eventHandlers:
- event: Activated(indexed address)
handler: handleDispenserActivated
- event: Deactivated(indexed address)
handler: handleDispenserDeactivated
- event: AcceptedMinter(indexed address)
handler: handleDispenserAcceptedMinter
- event: RemovedMinter(indexed address)
handler: handleDispenserRemovedMinter
- event: TokensDispensed(indexed address,indexed address,uint256)
handler: handleDispenserTokensDispensed
- event: OwnerWithdrawed(indexed address,indexed address,uint256)
handler: handleDispenserOwnerWithdrawed
templates: templates:
- kind: ethereum/contract - kind: ethereum/contract
name: Pool name: Pool

View File

@ -94,6 +94,36 @@ dataSources:
handler: handleExchangeRateChanged handler: handleExchangeRateChanged
- event: Swapped(indexed bytes32,indexed address,uint256,uint256) - event: Swapped(indexed bytes32,indexed address,uint256,uint256)
handler: handleSwapped handler: handleSwapped
- kind: ethereum/contract
name: Dispenser
network: rinkeby
source:
address: '0x623744Cd25Ed553d3b4722667697F926cf99658B'
abi: Dispenser
startBlock: 8554110
mapping:
kind: ethereum/events
apiVersion: 0.0.4
language: wasm/assemblyscript
file: ./src/mappings/dispenser.ts
entities:
- Dispenser
abis:
- name: Dispenser
file: ./abis/Dispenser.json
eventHandlers:
- event: Activated(indexed address)
handler: handleDispenserActivated
- event: Deactivated(indexed address)
handler: handleDispenserDeactivated
- event: AcceptedMinter(indexed address)
handler: handleDispenserAcceptedMinter
- event: RemovedMinter(indexed address)
handler: handleDispenserRemovedMinter
- event: TokensDispensed(indexed address,indexed address,uint256)
handler: handleDispenserTokensDispensed
- event: OwnerWithdrawed(indexed address,indexed address,uint256)
handler: handleDispenserOwnerWithdrawed
templates: templates:
- kind: ethereum/contract - kind: ethereum/contract
name: Pool name: Pool

View File

@ -94,6 +94,36 @@ dataSources:
handler: handleExchangeRateChanged handler: handleExchangeRateChanged
- event: Swapped(indexed bytes32,indexed address,uint256,uint256) - event: Swapped(indexed bytes32,indexed address,uint256,uint256)
handler: handleSwapped handler: handleSwapped
- kind: ethereum/contract
name: Dispenser
network: ropsten
source:
address: '0xc37F8341Ac6e4a94538302bCd4d49Cf0852D30C0'
abi: Dispenser
startBlock: 10201082
mapping:
kind: ethereum/events
apiVersion: 0.0.4
language: wasm/assemblyscript
file: ./src/mappings/dispenser.ts
entities:
- Dispenser
abis:
- name: Dispenser
file: ./abis/Dispenser.json
eventHandlers:
- event: Activated(indexed address)
handler: handleDispenserActivated
- event: Deactivated(indexed address)
handler: handleDispenserDeactivated
- event: AcceptedMinter(indexed address)
handler: handleDispenserAcceptedMinter
- event: RemovedMinter(indexed address)
handler: handleDispenserRemovedMinter
- event: TokensDispensed(indexed address,indexed address,uint256)
handler: handleDispenserTokensDispensed
- event: OwnerWithdrawed(indexed address,indexed address,uint256)
handler: handleDispenserOwnerWithdrawed
templates: templates:
- kind: ethereum/contract - kind: ethereum/contract
name: Pool name: Pool

View File

@ -94,6 +94,36 @@ dataSources:
handler: handleExchangeRateChanged handler: handleExchangeRateChanged
- event: Swapped(indexed bytes32,indexed address,uint256,uint256) - event: Swapped(indexed bytes32,indexed address,uint256,uint256)
handler: handleSwapped handler: handleSwapped
- kind: ethereum/contract
name: Dispenser
network: mainnet
source:
address: '0xDEfD0018969cd2d4E648209F876ADe184815f038'
abi: Dispenser
startBlock: 12398958
mapping:
kind: ethereum/events
apiVersion: 0.0.4
language: wasm/assemblyscript
file: ./src/mappings/dispenser.ts
entities:
- Dispenser
abis:
- name: Dispenser
file: ./abis/Dispenser.json
eventHandlers:
- event: Activated(indexed address)
handler: handleDispenserActivated
- event: Deactivated(indexed address)
handler: handleDispenserDeactivated
- event: AcceptedMinter(indexed address)
handler: handleDispenserAcceptedMinter
- event: RemovedMinter(indexed address)
handler: handleDispenserRemovedMinter
- event: TokensDispensed(indexed address,indexed address,uint256)
handler: handleDispenserTokensDispensed
- event: OwnerWithdrawed(indexed address,indexed address,uint256)
handler: handleDispenserOwnerWithdrawed
templates: templates:
- kind: ethereum/contract - kind: ethereum/contract
name: Pool name: Pool

View File

@ -0,0 +1,235 @@
/* eslint-disable prefer-destructuring */
import { assert, use } from 'chai'
import spies from 'chai-spies'
import Web3 from 'web3'
import { Ocean, ConfigHelper, Account } from '@oceanprotocol/lib'
const fetch = require('cross-fetch')
const web3 = new Web3('http://127.0.0.1:8545')
const subgraphUrl =
'http://localhost:9000/subgraphs/name/oceanprotocol/ocean-subgraph'
function sleep(ms: number) {
return new Promise((resolve) => {
setTimeout(resolve, ms)
})
}
async function getDispenserStatusFromGraph(datatoken: string) {
const id = datatoken.toLowerCase()
const query = {
query: `query {
dispenser(id:"${id}"){active,owner{id},minterApproved,isTrueMinter,maxTokens,maxBalance,balance,datatoken{id}}}`
}
const response = await fetch(subgraphUrl, {
method: 'POST',
body: JSON.stringify(query)
})
const result = await response.json()
return result
}
use(spies)
describe('Dispenser test flow', () => {
let alice: Account
let bob: Account
let ocean: Ocean
let tokenAddress
let tokenAddress2
let tokenAddress3
const tokenAmount = '1000'
it('Initialize Ocean Library', async () => {
const config = new ConfigHelper().getConfig('development')
config.web3Provider = web3
ocean = await Ocean.getInstance(config)
alice = (await ocean.accounts.list())[0]
bob = (await ocean.accounts.list())[1]
})
it('should create some datatokens', async () => {
tokenAddress = await ocean.datatokens.create(
'',
alice.getId(),
'1000000000000000',
'AliceDT',
'DTA'
)
assert(tokenAddress !== null)
tokenAddress2 = await ocean.datatokens.create(
'',
alice.getId(),
'1000000000000000',
'AliceDT2',
'DTA2'
)
assert(tokenAddress2 !== null)
tokenAddress3 = await ocean.datatokens.create(
'',
alice.getId(),
'1000000000000000',
'AliceDT3',
'DTA3'
)
assert(tokenAddress3 !== null)
})
it('Alice mints 1000 tokens', async () => {
const txid = await ocean.datatokens.mint(
tokenAddress,
alice.getId(),
tokenAmount
)
assert(txid !== null)
})
it('Alice creates a dispenser', async () => {
const tx = await ocean.OceanDispenser.activate(
tokenAddress,
'1',
'1',
alice.getId()
)
assert(tx, 'Cannot activate dispenser')
await sleep(3000) // let graph ingest our transaction
const status = await getDispenserStatusFromGraph(tokenAddress)
assert(status.data.dispenser.datatoken.id === tokenAddress.toLowerCase())
assert(status.data.dispenser.owner.id === alice.getId().toLowerCase())
assert(status.data.dispenser.isTrueMinter === false)
assert(status.data.dispenser.minterApproved === false)
assert(status.data.dispenser.active === true)
assert(status.data.dispenser.balance === '0')
})
it('Alice should make the dispenser a minter', async () => {
const tx = await ocean.OceanDispenser.makeMinter(
tokenAddress,
alice.getId()
)
assert(tx, 'Cannot make dispenser a minter')
await sleep(3000) // let graph ingest our transaction
const status = await getDispenserStatusFromGraph(tokenAddress)
assert(status.data.dispenser.datatoken.id === tokenAddress.toLowerCase())
assert(status.data.dispenser.owner.id === alice.getId().toLowerCase())
assert(status.data.dispenser.isTrueMinter === true)
assert(status.data.dispenser.minterApproved === true)
})
it('Bob requests datatokens', async () => {
const tx = await ocean.OceanDispenser.dispense(
tokenAddress,
bob.getId(),
'1'
)
assert(tx, 'Bob failed to get 1DT')
await sleep(3000) // let graph ingest our transaction
const id = tx.transactionHash.toLowerCase()
const query = {
query: `query DispenserHistory {
dispenserTransactions(orderBy: timestamp, orderDirection: desc,
where: {tx: "${id}"}) {
datatoken{id},
user{id},
amount,
block,
timestamp,
tx,
type
}
}`
}
const response = await fetch(subgraphUrl, {
method: 'POST',
body: JSON.stringify(query)
})
const result = await response.json()
assert(result.data.dispenserTransactions[0].type === 'dispense')
})
it('Alice calls removeMinter role and checks if she is the new minter', async () => {
const tx = await ocean.OceanDispenser.cancelMinter(
tokenAddress,
alice.getId()
)
assert(tx, 'Cannot cancel minter role')
await sleep(3000) // let graph ingest our transaction
const status = await getDispenserStatusFromGraph(tokenAddress)
assert(status.data.dispenser.datatoken.id === tokenAddress.toLowerCase())
assert(status.data.dispenser.owner.id === alice.getId().toLowerCase())
assert(status.data.dispenser.isTrueMinter === false)
assert(status.data.dispenser.minterApproved === false)
assert(status.data.dispenser.active === true)
})
it('Alice deactivates the dispenser', async () => {
const tx = await ocean.OceanDispenser.deactivate(
tokenAddress,
alice.getId()
)
assert(tx, 'Cannot make dispenser a minter')
await sleep(3000) // let graph ingest our transaction
const status = await getDispenserStatusFromGraph(tokenAddress)
assert(status.data.dispenser.datatoken.id === tokenAddress.toLowerCase())
assert(status.data.dispenser.owner.id === alice.getId().toLowerCase())
assert(status.data.dispenser.active === false)
})
it('Alice creates a dispenser without minter role', async () => {
const tx = await ocean.OceanDispenser.activate(
tokenAddress2,
'1',
'1',
alice.getId()
)
assert(tx, 'Cannot activate dispenser')
await sleep(3000) // let graph ingest our transaction
const status = await getDispenserStatusFromGraph(tokenAddress2)
assert(status.data.dispenser.datatoken.id === tokenAddress2.toLowerCase())
assert(status.data.dispenser.owner.id === alice.getId().toLowerCase())
assert(status.data.dispenser.isTrueMinter === false)
assert(status.data.dispenser.minterApproved === false)
assert(status.data.dispenser.active === true)
})
it('Alice withdraws all datatokens', async () => {
const mintTx = await ocean.datatokens.mint(
tokenAddress2,
alice.getId(),
'10',
ocean.OceanDispenser.dispenserAddress
)
assert(mintTx, 'Alice cannot mint tokens')
const tx = await ocean.OceanDispenser.ownerWithdraw(
tokenAddress2,
alice.getId()
)
assert(tx, 'Alice failed to withdraw all her tokens')
await sleep(3000) // let graph ingest our transaction
const status = await getDispenserStatusFromGraph(tokenAddress2)
assert(status.data.dispenser.datatoken.id === tokenAddress2.toLowerCase())
assert(status.data.dispenser.owner.id === alice.getId().toLowerCase())
assert(status.data.dispenser.isTrueMinter === false)
assert(status.data.dispenser.minterApproved === false)
assert(status.data.dispenser.active === true)
assert(status.data.dispenser.balance === '0')
const id = tx.transactionHash.toLowerCase()
const query = {
query: `query DispenserHistory {
dispenserTransactions(orderBy: timestamp, orderDirection: desc,
where: {tx: "${id}"}) {
datatoken{id},
user{id},
amount,
block,
timestamp,
tx,
type
}
}`
}
// console.log(query)
const response = await fetch(subgraphUrl, {
method: 'POST',
body: JSON.stringify(query)
})
const result = await response.json()
assert(result.data.dispenserTransactions[0].type === 'withdraw')
})
})

View File

@ -7,11 +7,20 @@ const web3 = new Web3('http://127.0.0.1:8545')
const subgraphUrl = const subgraphUrl =
'http://localhost:9000/subgraphs/name/oceanprotocol/ocean-subgraph' 'http://localhost:9000/subgraphs/name/oceanprotocol/ocean-subgraph'
function sleep(ms: number) {
return new Promise((resolve) => {
setTimeout(resolve, ms)
})
}
use(spies) use(spies)
describe('Ending tests', () => { describe('Ending tests', () => {
let result: any let result: any
let lastblock
it('Get Graph status', async () => { it('Get Graph status', async () => {
await sleep(1000) // let graph ingest our last transactions
lastblock = await web3.eth.getBlockNumber()
const query = { const query = {
query: `query { query: `query {
_meta{hasIndexingErrors, _meta{hasIndexingErrors,
@ -31,7 +40,6 @@ describe('Ending tests', () => {
assert(result.data._meta.hasIndexingErrors == false) assert(result.data._meta.hasIndexingErrors == false)
}) })
it('Make sure that graph has synced to last block', async () => { it('Make sure that graph has synced to last block', async () => {
const lastblock = await web3.eth.getBlockNumber()
assert(result.data._meta.block.number === lastblock) assert(result.data._meta.block.number === lastblock)
}) })
}) })