From ea7d6a57e6dea10e776ce28949cf9b213cc2971e Mon Sep 17 00:00:00 2001 From: ChefJoJo <94336009+chef-jojo@users.noreply.github.com> Date: Wed, 7 Feb 2024 12:34:21 +0800 Subject: [PATCH] feat: Add price ref for stableswap bsc --- .../bsc/abis/PriceLens0.json | 121 ++++++++++++++++++ .../bsc/mappings/services/swap.ts | 65 +++++----- .../bsc/mappings/services/sync.ts | 13 +- .../bsc/mappings/utils/index.ts | 4 + .../bsc/mappings/utils/pricing.ts | 112 ++++++++-------- .../exchange-stableswap/bsc/subgraph.yaml | 13 +- 6 files changed, 237 insertions(+), 91 deletions(-) create mode 100644 subgraphs/exchange-stableswap/bsc/abis/PriceLens0.json diff --git a/subgraphs/exchange-stableswap/bsc/abis/PriceLens0.json b/subgraphs/exchange-stableswap/bsc/abis/PriceLens0.json new file mode 100644 index 00000000..a6cb7b27 --- /dev/null +++ b/subgraphs/exchange-stableswap/bsc/abis/PriceLens0.json @@ -0,0 +1,121 @@ +[ + { + "type": "constructor", + "inputs": [ + { "name": "nativePriceAggregator", "type": "address", "internalType": "address" }, + { "name": "wnative", "type": "address", "internalType": "address" } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "DECIMALS", + "inputs": [], + "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "_nativePriceAggregator", + "inputs": [], + "outputs": [{ "name": "", "type": "address", "internalType": "address" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "_wnative", + "inputs": [], + "outputs": [{ "name": "", "type": "address", "internalType": "address" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getNativePrice", + "inputs": [{ "name": "token", "type": "address", "internalType": "address" }], + "outputs": [{ "name": "nativePrice", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getUsd", + "inputs": [{ "name": "token", "type": "address", "internalType": "address" }], + "outputs": [{ "name": "price", "type": "uint256", "internalType": "uint256" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "owner", + "inputs": [], + "outputs": [{ "name": "", "type": "address", "internalType": "address" }], + "stateMutability": "view" + }, + { + "type": "function", + "name": "setNativePriceAggregator", + "inputs": [{ "name": "nativePriceAggregator", "type": "address", "internalType": "address" }], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setSource", + "inputs": [ + { "name": "token", "type": "address", "internalType": "address" }, + { "name": "sourceType", "type": "uint8", "internalType": "enum PriceLens0.SourceType" }, + { "name": "sourceAddress", "type": "address", "internalType": "address" }, + { "name": "baseAddress", "type": "address", "internalType": "address" } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setSourceList", + "inputs": [ + { + "name": "sourceList", + "type": "tuple[]", + "internalType": "struct PriceLens0.SourceList[]", + "components": [ + { "name": "token", "type": "address", "internalType": "address" }, + { "name": "sourceType", "type": "uint8", "internalType": "enum PriceLens0.SourceType" }, + { "name": "sourceAddress", "type": "address", "internalType": "address" }, + { "name": "baseAddress", "type": "address", "internalType": "address" } + ] + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "sources", + "inputs": [{ "name": "", "type": "address", "internalType": "address" }], + "outputs": [ + { "name": "sourceType", "type": "uint8", "internalType": "enum PriceLens0.SourceType" }, + { "name": "sourceAddress", "type": "address", "internalType": "address" }, + { "name": "baseAddress", "type": "address", "internalType": "address" } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "transferOwnership", + "inputs": [{ "name": "newOwner", "type": "address", "internalType": "address" }], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "OwnershipTransferred", + "inputs": [ + { "name": "user", "type": "address", "indexed": true, "internalType": "address" }, + { "name": "newOwner", "type": "address", "indexed": true, "internalType": "address" } + ], + "anonymous": false + }, + { "type": "error", "name": "IncorrectBaseAddress", "inputs": [] }, + { "type": "error", "name": "IncorrectChainlinkPrice", "inputs": [] }, + { "type": "error", "name": "IncorrectSourceAddress", "inputs": [] }, + { "type": "error", "name": "UnknowPrice", "inputs": [] } +] diff --git a/subgraphs/exchange-stableswap/bsc/mappings/services/swap.ts b/subgraphs/exchange-stableswap/bsc/mappings/services/swap.ts index 18fc2f84..f2fccd01 100644 --- a/subgraphs/exchange-stableswap/bsc/mappings/services/swap.ts +++ b/subgraphs/exchange-stableswap/bsc/mappings/services/swap.ts @@ -2,7 +2,6 @@ import { BigDecimal, BigInt, Bytes, ethereum } from "@graphprotocol/graph-ts"; import { Bundle, Pair, Swap, Token, Transaction } from "../../generated/schema"; import { BIG_DECIMAL_ZERO, BIG_INT_ONE, convertTokenToDecimal } from "../utils"; -import { getTrackedFeeVolumeUSD, getTrackedVolumeUSD } from "../utils/pricing"; import { getOrCreateFactory } from "../utils/data"; import { updatePairDayData, updatePairHourData, updatePancakeDayData, updateTokenDayData } from "./dayUpdates"; @@ -52,40 +51,40 @@ export function swap(event: ethereum.Event, params: SwapParams): void { } let derivedFeeAmountUSD = derivedFeeAmountBNB.times(bundle.bnbPrice); - // only accounts for volume through white listed tokens - let trackedAmountUSD = getTrackedVolumeUSD( - bundle as Bundle, - amount0Total, - token0 as Token, - amount1Total, - token1 as Token - ); - let trackedFeeAmountUSD = getTrackedFeeVolumeUSD( - bundle as Bundle, - amount0Total, - token0 as Token, - amount1Total, - token1 as Token - ); + // // only accounts for volume through white listed tokens + // let trackedAmountUSD = getTrackedVolumeUSD( + // bundle as Bundle, + // amount0Total, + // token0 as Token, + // amount1Total, + // token1 as Token + // ); + // let trackedFeeAmountUSD = getTrackedFeeVolumeUSD( + // bundle as Bundle, + // amount0Total, + // token0 as Token, + // amount1Total, + // token1 as Token + // ); let price0 = token0.derivedBNB.times(bundle.bnbPrice); let price1 = token1.derivedBNB.times(bundle.bnbPrice); - let trackedAmountBNB: BigDecimal; - if (bundle.bnbPrice.equals(BIG_DECIMAL_ZERO)) { - trackedAmountBNB = BIG_DECIMAL_ZERO; - } else { - trackedAmountBNB = trackedAmountUSD.div(bundle.bnbPrice); - } + // let trackedAmountBNB: BigDecimal; + // if (bundle.bnbPrice.equals(BIG_DECIMAL_ZERO)) { + // trackedAmountBNB = BIG_DECIMAL_ZERO; + // } else { + // trackedAmountBNB = trackedAmountUSD.div(bundle.bnbPrice); + // } // update token0 global volume and token liquidity stats token0.tradeVolume = token0.tradeVolume.plus(amount0In.plus(amount0Out)); - token0.tradeVolumeUSD = token0.tradeVolumeUSD.plus(trackedAmountUSD); + token0.tradeVolumeUSD = token0.tradeVolumeUSD.plus(derivedAmountUSD); token0.untrackedVolumeUSD = token0.untrackedVolumeUSD.plus(derivedAmountUSD); // update token1 global volume and token liquidity stats token1.tradeVolume = token1.tradeVolume.plus(amount1In.plus(amount1Out)); - token1.tradeVolumeUSD = token1.tradeVolumeUSD.plus(trackedAmountUSD); + token1.tradeVolumeUSD = token1.tradeVolumeUSD.plus(derivedAmountUSD); token1.untrackedVolumeUSD = token1.untrackedVolumeUSD.plus(derivedAmountUSD); // update txn counts @@ -93,7 +92,7 @@ export function swap(event: ethereum.Event, params: SwapParams): void { token1.totalTransactions = token1.totalTransactions.plus(BIG_INT_ONE); // update pair volume data, use tracked amount if we have it as its probably more accurate - pair.volumeUSD = pair.volumeUSD.plus(trackedAmountUSD); + pair.volumeUSD = pair.volumeUSD.plus(derivedAmountUSD); pair.volumeOutUSD = pair.volumeOutUSD.plus(amount0Out.times(price0)).plus(amount1Out.times(price1)); pair.volumeToken0 = pair.volumeToken0.plus(amount0Total); pair.volumeToken1 = pair.volumeToken1.plus(amount1Total); @@ -103,8 +102,8 @@ export function swap(event: ethereum.Event, params: SwapParams): void { // update global values, only used tracked amounts for volume let factory = getOrCreateFactory(pair.factory); - factory.totalVolumeUSD = factory.totalVolumeUSD.plus(trackedAmountUSD); - factory.totalVolumeBNB = factory.totalVolumeBNB.plus(trackedAmountBNB); + factory.totalVolumeUSD = factory.totalVolumeUSD.plus(derivedAmountUSD); + factory.totalVolumeBNB = factory.totalVolumeBNB.plus(derivedAmountBNB); factory.untrackedVolumeUSD = factory.untrackedVolumeUSD.plus(derivedAmountUSD); factory.totalTransactions = factory.totalTransactions.plus(BIG_INT_ONE); @@ -140,8 +139,8 @@ export function swap(event: ethereum.Event, params: SwapParams): void { swap.from = event.transaction.from; swap.logIndex = event.logIndex; // use the tracked amount if we have it - swap.amountUSD = trackedAmountUSD === BIG_DECIMAL_ZERO ? derivedAmountUSD : trackedAmountUSD; - swap.amountFeeUSD = trackedFeeAmountUSD === BIG_DECIMAL_ZERO ? derivedFeeAmountUSD : trackedFeeAmountUSD; + swap.amountUSD = derivedAmountUSD; + swap.amountFeeUSD = derivedFeeAmountUSD; swap.save(); // update the transaction @@ -160,21 +159,21 @@ export function swap(event: ethereum.Event, params: SwapParams): void { let token1DayData = updateTokenDayData(token1 as Token, event); // swap specific updating - pancakeDayData.dailyVolumeUSD = pancakeDayData.dailyVolumeUSD.plus(trackedAmountUSD); - pancakeDayData.dailyVolumeBNB = pancakeDayData.dailyVolumeBNB.plus(trackedAmountBNB); + pancakeDayData.dailyVolumeUSD = pancakeDayData.dailyVolumeUSD.plus(derivedAmountUSD); + pancakeDayData.dailyVolumeBNB = pancakeDayData.dailyVolumeBNB.plus(derivedAmountBNB); pancakeDayData.dailyVolumeUntracked = pancakeDayData.dailyVolumeUntracked.plus(derivedAmountUSD); pancakeDayData.save(); // swap specific updating for pair pairDayData.dailyVolumeToken0 = pairDayData.dailyVolumeToken0.plus(amount0Total); pairDayData.dailyVolumeToken1 = pairDayData.dailyVolumeToken1.plus(amount1Total); - pairDayData.dailyVolumeUSD = pairDayData.dailyVolumeUSD.plus(trackedAmountUSD); + pairDayData.dailyVolumeUSD = pairDayData.dailyVolumeUSD.plus(derivedAmountUSD); pairDayData.save(); // update hourly pair data pairHourData.hourlyVolumeToken0 = pairHourData.hourlyVolumeToken0.plus(amount0Total); pairHourData.hourlyVolumeToken1 = pairHourData.hourlyVolumeToken1.plus(amount1Total); - pairHourData.hourlyVolumeUSD = pairHourData.hourlyVolumeUSD.plus(trackedAmountUSD); + pairHourData.hourlyVolumeUSD = pairHourData.hourlyVolumeUSD.plus(derivedAmountUSD); pairHourData.save(); // swap specific updating for token0 diff --git a/subgraphs/exchange-stableswap/bsc/mappings/services/sync.ts b/subgraphs/exchange-stableswap/bsc/mappings/services/sync.ts index 6139f3b8..0b275953 100644 --- a/subgraphs/exchange-stableswap/bsc/mappings/services/sync.ts +++ b/subgraphs/exchange-stableswap/bsc/mappings/services/sync.ts @@ -39,11 +39,22 @@ export function sync(pairAddress: Address): void { bundle.save(); let t0DerivedBNB = findBnbPerToken(token0 as Token); + let t1DerivedBNB = findBnbPerToken(token1 as Token); + + // if t0 bnb is not found + if (t0DerivedBNB.equals(BIG_DECIMAL_ZERO) && t1DerivedBNB.gt(BIG_DECIMAL_ZERO)) { + t0DerivedBNB = t1DerivedBNB.times(pair.token1Price); + } + + // if t1 bnb is not found + if (t1DerivedBNB.equals(BIG_DECIMAL_ZERO) && t0DerivedBNB.gt(BIG_DECIMAL_ZERO)) { + t1DerivedBNB = t0DerivedBNB.times(pair.token0Price); + } + token0.derivedBNB = t0DerivedBNB; token0.derivedUSD = t0DerivedBNB.times(bundle.bnbPrice); token0.save(); - let t1DerivedBNB = findBnbPerToken(token1 as Token); token1.derivedBNB = t1DerivedBNB; token1.derivedUSD = t1DerivedBNB.times(bundle.bnbPrice); token1.save(); diff --git a/subgraphs/exchange-stableswap/bsc/mappings/utils/index.ts b/subgraphs/exchange-stableswap/bsc/mappings/utils/index.ts index 504d163b..7dd77049 100644 --- a/subgraphs/exchange-stableswap/bsc/mappings/utils/index.ts +++ b/subgraphs/exchange-stableswap/bsc/mappings/utils/index.ts @@ -8,6 +8,7 @@ import { Factory } from "../../generated/StableSwapFactory/Factory"; import { ERC20 } from "../../generated/StableSwapFactory/ERC20"; import { ERC20SymbolBytes } from "../../generated/StableSwapFactory/ERC20SymbolBytes"; import { ERC20NameBytes } from "../../generated/StableSwapFactory/ERC20NameBytes"; +import { PriceLens0 } from "../../generated/StableSwapFactory/PriceLens0"; export let ADDRESS_ZERO = "0x0000000000000000000000000000000000000000"; export let STABLESWAP_FACTORY_ADDRESS = "0x36bbb126e75351c0dfb651e39b38fe0bc436ffd2"; @@ -20,6 +21,8 @@ export let CAKE_ADDRESS = "0x0e09fabb73bd3ade0a17ecc321fd13a19e81ce82"; //When new factory was deployed, and SC address into list export let FACTORIES: string[] = [STABLESWAP_FACTORY_ADDRESS, STABLESWAP_FACTORY_ADDRESS_2]; +export let priceLensContract = PriceLens0.bind(Address.fromString("0x6234d989583520565d00131948c1fb7c4ebb691d")); + export let BUSD_ADDR = Address.fromString(BUSD_ADDRESS); export let WBNB_ADDR = Address.fromString(WBNB_ADDRESS); export let CAKE_ADDR = Address.fromString(CAKE_ADDRESS); @@ -31,6 +34,7 @@ export let BIG_DECIMAL_ONE = BigDecimal.fromString("1"); export let BIG_DECIMAL_1E18 = BigDecimal.fromString("1e18"); export let BIG_INT_18 = BigInt.fromI32(18); +export let BIG_INT_6 = BigInt.fromI32(6); export let stableSwapFactoryContract = StableSwapFactory.bind(Address.fromString(STABLESWAP_FACTORY_ADDRESS_2)); export let pcsFactoryContract = Factory.bind(Address.fromString(PCS_FACTORY_ADDRESS)); diff --git a/subgraphs/exchange-stableswap/bsc/mappings/utils/pricing.ts b/subgraphs/exchange-stableswap/bsc/mappings/utils/pricing.ts index 90436bd6..f0dc96ff 100644 --- a/subgraphs/exchange-stableswap/bsc/mappings/utils/pricing.ts +++ b/subgraphs/exchange-stableswap/bsc/mappings/utils/pricing.ts @@ -13,6 +13,8 @@ import { powBigDecimal, stableSwapFactoryContract, WBNB_ADDR, + priceLensContract, + BIG_INT_6, } from "./index"; import { Pair as PairContract } from "../../generated/StableSwapFactory/Pair"; import { PairV3 as PairV3Contract } from "../../generated/StableSwapFactory/PairV3"; @@ -167,6 +169,11 @@ export function findBnbPerToken(token: Token): BigDecimal { } } } + + let tokenBNBPrice = priceLensContract.try_getNativePrice(Address.fromString(token.id)); + if (!tokenBNBPrice.reverted) { + return convertTokenToDecimal(tokenBNBPrice.value, BIG_INT_6); + } return BIG_DECIMAL_ZERO; // nothing was found return 0 } @@ -176,64 +183,64 @@ export function findBnbPerToken(token: Token): BigDecimal { * If both are, return average of two amounts * If neither is, return 0 */ -export function getTrackedVolumeUSD( - bundle: Bundle, - tokenAmount0: BigDecimal, - token0: Token, - tokenAmount1: BigDecimal, - token1: Token -): BigDecimal { - let price0 = token0.derivedBNB.times(bundle.bnbPrice); - let price1 = token1.derivedBNB.times(bundle.bnbPrice); +// export function getTrackedVolumeUSD( +// bundle: Bundle, +// tokenAmount0: BigDecimal, +// token0: Token, +// tokenAmount1: BigDecimal, +// token1: Token +// ): BigDecimal { +// let price0 = token0.derivedBNB.times(bundle.bnbPrice); +// let price1 = token1.derivedBNB.times(bundle.bnbPrice); - // both are whitelist tokens, take average of both amounts - if (WHITELIST.includes(token0.id) && WHITELIST.includes(token1.id)) { - return tokenAmount0.times(price0).plus(tokenAmount1.times(price1)).div(BigDecimal.fromString("2")); - } +// // both are whitelist tokens, take average of both amounts +// if (WHITELIST.includes(token0.id) && WHITELIST.includes(token1.id)) { +// return tokenAmount0.times(price0).plus(tokenAmount1.times(price1)).div(BigDecimal.fromString("2")); +// } - // take full value of the whitelisted token amount - if (WHITELIST.includes(token0.id) && !WHITELIST.includes(token1.id)) { - return tokenAmount0.times(price0); - } +// // take full value of the whitelisted token amount +// if (WHITELIST.includes(token0.id) && !WHITELIST.includes(token1.id)) { +// return tokenAmount0.times(price0); +// } - // take full value of the whitelisted token amount - if (!WHITELIST.includes(token0.id) && WHITELIST.includes(token1.id)) { - return tokenAmount1.times(price1); - } +// // take full value of the whitelisted token amount +// if (!WHITELIST.includes(token0.id) && WHITELIST.includes(token1.id)) { +// return tokenAmount1.times(price1); +// } - // neither token is on white list, tracked volume is 0 - return BIG_DECIMAL_ZERO; -} +// // neither token is on white list, tracked volume is 0 +// return BIG_DECIMAL_ZERO; +// } /** * Accepts tokens and amounts, return tracked fee amount based on token whitelist * If both are, return the difference between the token amounts * If not, return 0 */ -export function getTrackedFeeVolumeUSD( - bundle: Bundle, - tokenAmount0: BigDecimal, - token0: Token, - tokenAmount1: BigDecimal, - token1: Token -): BigDecimal { - let price0 = token0.derivedBNB.times(bundle.bnbPrice); - let price1 = token1.derivedBNB.times(bundle.bnbPrice); +// export function getTrackedFeeVolumeUSD( +// bundle: Bundle, +// tokenAmount0: BigDecimal, +// token0: Token, +// tokenAmount1: BigDecimal, +// token1: Token +// ): BigDecimal { +// let price0 = token0.derivedBNB.times(bundle.bnbPrice); +// let price1 = token1.derivedBNB.times(bundle.bnbPrice); - // both are whitelist tokens, take average of both amounts - if (WHITELIST.includes(token0.id) && WHITELIST.includes(token1.id)) { - let tokenAmount0USD = tokenAmount0.times(price0); - let tokenAmount1USD = tokenAmount1.times(price1); - if (tokenAmount0USD.ge(tokenAmount1USD)) { - return tokenAmount0USD.minus(tokenAmount1USD); - } else { - return tokenAmount1USD.minus(tokenAmount0USD); - } - } +// // both are whitelist tokens, take average of both amounts +// if (WHITELIST.includes(token0.id) && WHITELIST.includes(token1.id)) { +// let tokenAmount0USD = tokenAmount0.times(price0); +// let tokenAmount1USD = tokenAmount1.times(price1); +// if (tokenAmount0USD.ge(tokenAmount1USD)) { +// return tokenAmount0USD.minus(tokenAmount1USD); +// } else { +// return tokenAmount1USD.minus(tokenAmount0USD); +// } +// } - // neither token is on white list, tracked volume is 0 - return BIG_DECIMAL_ZERO; -} +// // neither token is on white list, tracked volume is 0 +// return BIG_DECIMAL_ZERO; +// } /** * Accepts tokens and amounts, return tracked amount based on token whitelist @@ -251,11 +258,6 @@ export function getTrackedLiquidityUSD( let price0 = token0.derivedBNB.times(bundle.bnbPrice); let price1 = token1.derivedBNB.times(bundle.bnbPrice); - // both are whitelist tokens, take average of both amounts - if (WHITELIST.includes(token0.id) && WHITELIST.includes(token1.id)) { - return tokenAmount0.times(price0).plus(tokenAmount1.times(price1)); - } - // take double value of the whitelisted token amount if (WHITELIST.includes(token0.id) && !WHITELIST.includes(token1.id)) { return tokenAmount0.times(price0).times(BigDecimal.fromString("2")); @@ -266,6 +268,10 @@ export function getTrackedLiquidityUSD( return tokenAmount1.times(price1).times(BigDecimal.fromString("2")); } - // neither token is on white list, tracked volume is 0 - return BIG_DECIMAL_ZERO; + // // both are whitelist tokens, take average of both amounts + // if (WHITELIST.includes(token0.id) && WHITELIST.includes(token1.id)) { + // return tokenAmount0.times(price0).plus(tokenAmount1.times(price1)); + // } + + return tokenAmount0.times(price0).plus(tokenAmount1.times(price1)); } diff --git a/subgraphs/exchange-stableswap/bsc/subgraph.yaml b/subgraphs/exchange-stableswap/bsc/subgraph.yaml index 6b6a9f6b..0dd48332 100644 --- a/subgraphs/exchange-stableswap/bsc/subgraph.yaml +++ b/subgraphs/exchange-stableswap/bsc/subgraph.yaml @@ -3,15 +3,18 @@ description: PancakeSwap is a decentralized protocol for automated token exchang repository: https://github.com/pancakeswap schema: file: ./schema.graphql +# graft: +# base: Qme4U6by9yqGwTPKRaPvbgo7Ar9CwYFaTjmy9z6uVSjxvL +# block: 33890015 # for previous exiting factory graft: - base: Qme4U6by9yqGwTPKRaPvbgo7Ar9CwYFaTjmy9z6uVSjxvL - block: 33890015 # for previous exiting factory + base: QmXYctSeyF2dMi6Cmkb4t83BMn1RJcENkVSkAgsTTXFAC4 + block: 35661200 # for previous exiting factory dataSources: - kind: ethereum/contract name: StableSwapFactory network: bsc source: - address: '0x25a55f9f2279A54951133D503490342b50E5cd15' + address: "0x25a55f9f2279A54951133D503490342b50E5cd15" abi: StableSwapFactory startBlock: 25535458 mapping: @@ -38,6 +41,8 @@ dataSources: file: ./abis/ERC20NameBytes.json - name: ERC20SymbolBytes file: ./abis/ERC20SymbolBytes.json + - name: PriceLens0 + file: ./abis/PriceLens0.json eventHandlers: - event: NewStableSwapPair(indexed address,address,address,address,address) handler: handlePairCreated @@ -167,4 +172,4 @@ templates: file: ./abis/ERC20.json eventHandlers: - event: Transfer(indexed address,indexed address,uint256) - handler: handleTransfer \ No newline at end of file + handler: handleTransfer