Skip to content

Commit

Permalink
feat(subgraph): Receipts improved (#13824)
Browse files Browse the repository at this point in the history
* adding comments

* using the GNP_CHANGED event to identify values in subgraphs

* undue change

* extracting the unlock address

* fixed wasm

* fixing API

* fixing mocks

* adding more comments

* add test case for GNPChanged

* better mock

* unit test ok

* use data field in receipt instead of topics

* Update subgraph/src/receipt.ts

* Update subgraph/tests/mockTxReceipt.ts

---------

Co-authored-by: Clément Renaud <clement@unlock-protocol.com>
Co-authored-by: Clément Renaud <clement+git@clementrenaud.com>
  • Loading branch information
3 people committed May 14, 2024
1 parent 28194bc commit e3db005
Show file tree
Hide file tree
Showing 7 changed files with 297 additions and 150 deletions.
43 changes: 38 additions & 5 deletions subgraph/src/receipt.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { BigInt, log, Bytes, ethereum } from '@graphprotocol/graph-ts'
import { ERC20_TRANSFER_TOPIC0, nullAddress } from '../tests/constants'
import { BigInt, log, Bytes, ethereum, Address } from '@graphprotocol/graph-ts'
import {
GNP_CHANGED_TOPIC0,
ERC20_TRANSFER_TOPIC0,
nullAddress,
} from '../tests/constants'
import { PublicLockV11 as PublicLock } from '../generated/templates/PublicLock/PublicLockV11'

import { Lock, Receipt } from '../generated/schema'

Expand Down Expand Up @@ -32,13 +37,14 @@ export function createReceipt(event: ethereum.Event): void {
? lock.tokenAddress
: Bytes.fromHexString(nullAddress)

const txReceipt = event.receipt!
const logs: ethereum.Log[] = txReceipt.logs

if (tokenAddress != Bytes.fromHexString(nullAddress)) {
log.debug('Creating receipt for ERC20 lock {} {}', [
lockAddress,
tokenAddress.toHexString(),
])
const txReceipt = event.receipt!
const logs: ethereum.Log[] = txReceipt.logs

if (logs) {
// If it is an ERC20 lock, there should be multiple events
Expand Down Expand Up @@ -76,9 +82,36 @@ export function createReceipt(event: ethereum.Event): void {
// which means we don't need to create a receipt.
}
} else {
log.debug('Creating receipt for base currency lock {}', [lockAddress])
log.debug('Creating receipt for native currency lock {}', [lockAddress])
receipt.payer = event.transaction.from.toHexString()
receipt.amountTransferred = event.transaction.value
// We cannot trust `event.transaction.value` because the purchase function could in fact
// be happening inside of a larger transaction whose value is not the amount transfered,
// In that case, we need to look up the GNPChanged event
// This is a very fragile setup and we should consider moving to a more formal event triggered
// by the contract, like a `Receipt` event that would include everything we need.
if (logs) {
const lockContract = PublicLock.bind(Address.fromString(lockAddress))
const unlockAddress = lockContract.try_unlockProtocol()
let value = BigInt.zero()
for (let i = 0; i < logs.length; i++) {
const txLog = logs[i]
if (
txLog.address == unlockAddress.value &&
txLog.topics[0].toHexString() == GNP_CHANGED_TOPIC0
) {
const decoded = ethereum
.decode('(uint256,uint256,address,uint256,address)', txLog.data)!
.toTuple()

const keyValue = decoded[1].toBigInt()
value = value.plus(keyValue)
}
}
if (value > BigInt.zero()) {
receipt.amountTransferred = value
}
}
}

const totalGas = event.transaction.gasPrice.plus(event.transaction.gasLimit)
Expand Down
9 changes: 8 additions & 1 deletion subgraph/tests/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ export const lockManagers = [
'0x0000000000000000000000000000000000000125',
]

// protocol
export const unlockAddress = '0x0000000000000000000000000000000000000018'

// key
export const keyOwnerAddress = '0x0000000000000000000000000000000000000002'
export const tokenId = 1234
Expand All @@ -32,11 +35,15 @@ export const symbol = 'METAKEY'
export const baseTokenURI = 'https:/custom-lock.com/api/key/'

// default address used in newMockEvent() function
export const defaultMockAddress = '0xa16081f360e3847006db660bae1c6d1b2e17ec2a'
export const defaultMockAddress =
'0xA16081F360e3847006dB660bae1c6d1b2e17eC2A'.toLowerCase()
export const nullAddress = '0x0000000000000000000000000000000000000000'

// TODO: compile from contract ABI
// WARNING : For some tokens it may be different. In that case we would move to a list!
// TODO: for easier handling on future locks: trigger an "paid" event with the amount and data needed?
export const ERC20_TRANSFER_TOPIC0 =
'0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'

export const GNP_CHANGED_TOPIC0 =
'0x3b50eb9d9b4a8db204f2928c9e572c2865b0d02803493ccb6aa256848323ebb7'
131 changes: 0 additions & 131 deletions subgraph/tests/createCancelKeyEvent.ts

This file was deleted.

6 changes: 4 additions & 2 deletions subgraph/tests/keys-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
Transfer,
RenewKeyPurchase,
} from '../generated/templates/PublicLock/PublicLock'
import { GNPChanged } from '../generated/Unlock/Unlock'

import {
now,
Expand All @@ -31,8 +32,9 @@ import {
tokenId,
keyOwnerAddress,
lockOwner,
nullAddress,
} from './constants'
import { newTransactionReceipt } from './createCancelKeyEvent'
import { newCancelKeyTransactionReceipt } from './mockTxReceipt'

export function mockDataSourceV8(): void {
const v8context = new DataSourceContext()
Expand Down Expand Up @@ -204,7 +206,7 @@ export function createCancelKeyEvent(
): CancelKey {
const cancelKeyEvent = changetype<CancelKey>(newMockEvent())

cancelKeyEvent.receipt = newTransactionReceipt(tokenAddress, refund)
cancelKeyEvent.receipt = newCancelKeyTransactionReceipt(tokenAddress, refund)
cancelKeyEvent.address = dataSource.address()

cancelKeyEvent.parameters = []
Expand Down

0 comments on commit e3db005

Please sign in to comment.