Graph Protocol子图开发报错:PlayerToLottery非空字段缺失值
子图开发多对多关系时Matchstick测试报错排查
我参照Graph Protocol官方多对多关系的高性能实现方式开发子图,使用matchstick-as ^0.5.0编译测试时出现以下错误:
Igniting tests nft-lottery -------------------------------------------------- Enter the NFT Lottery: thread 'main' panicked at '🆘 Unexpected error upon calling hook: Missing value for non-nullable field 'player' for an entity of type 'PlayerToLottery'. wasm backtrace: 0: 0x331a - <unknown>!src/nft-lottery/handleNftLotteryEnter; 1: 0x3807 - <unknown>!start:tests/nft-lottery.test~anonymous|0~anonymous|0
相关代码
./schema.graphql
type NftLottery @entity { id: ID! open: BigInt! close: BigInt! prize: BigInt! players: [PlayerToLottery!]! @derivedFrom(field: "lottery") requestId: BigInt updatedAtTimestamp: BigInt } type Player @entity { id: ID! # address lotteries: [PlayerToLottery!]! @derivedFrom(field: "player") } type PlayerToLottery @entity { id: ID! # Set playerAddress.toHexString() + lotteryId.toHexString() player: Player! lottery: NftLottery! } # events type NftLotteryEnter @entity { id: ID! # Set lotteryCounter + playerAddress lotteryCounter: BigInt! player: Bytes! numberOfEntrances: [BigInt!]! updatedAtTimestamp: BigInt }
./src/mapping.ts
function getIdFromEventAddressInt(par2: Address, par1: BigInt): string { return par1.toHexString() + par2.toHexString() } export function handleNftLotteryEnter(event: NftLotteryEnterEvent): void { /* if a PlayerToLottery does not exists, create it */ const playerLotteryId = getIdFromEventAddressInt(event.params.player, event.params.lotteryCounter) let playerToLottery = PlayerToLottery.load(playerLotteryId) if (!playerToLottery) { playerToLottery = new PlayerToLottery(playerLotteryId) playerToLottery.save() } /* if a player does not exist, create them */ const playerId = event.params.player.toHexString() let player = Player.load(playerId) if (!player) { player = new Player(playerId) player.save() } /* if a lottery does not exist, create it */ const itemId = event.params.lotteryCounter.toHexString() let nftLottery = NftLottery.load(itemId) if (!nftLottery) { nftLottery = new NftLottery(itemId) nftLottery.open = event.block.timestamp nftLottery.close = BigInt.fromString("000000000000") nftLottery.prize = BigInt.fromString(event.params.entranceFee.toString()) nftLottery.players = new Array<string>() } // update lottery data nftLottery.prize.plus(event.params.entranceFee) // update players let arrPlayers = nftLottery.players arrPlayers.push(event.params.player.toHexString()) nftLottery.players = arrPlayers nftLottery.updatedAtTimestamp = event.block.timestamp nftLottery.save() }
错误原因及修复方案
核心问题
报错明确指出PlayerToLottery实体缺少非空字段player。原因是创建PlayerToLottery实例后直接调用save(),但未给它的player和lottery字段赋值——这两个字段在schema中被标记为!(非空),必须赋值才能保存。
另外还有两个额外问题需要修正:
nftLottery.prize.plus(...)不会修改原BigInt值(BigInt是不可变类型),必须重新赋值给nftLottery.prizenftLottery.players是通过@derivedFrom从PlayerToLottery的lottery字段派生的关联,不需要手动维护数组,手动操作反而会和自动派生逻辑冲突
修复后的mapping.ts代码
function getIdFromEventAddressInt(par2: Address, par1: BigInt): string { return par1.toHexString() + par2.toHexString() } export function handleNftLotteryEnter(event: NftLotteryEnterEvent): void { /* 先处理Player和NftLottery实体,确保它们存在 */ const playerId = event.params.player.toHexString() let player = Player.load(playerId) if (!player) { player = new Player(playerId) player.save() } const itemId = event.params.lotteryCounter.toHexString() let nftLottery = NftLottery.load(itemId) if (!nftLottery) { nftLottery = new NftLottery(itemId) nftLottery.open = event.block.timestamp nftLottery.close = BigInt.fromString("000000000000") nftLottery.prize = BigInt.fromString(event.params.entranceFee.toString()) } else { // 修正BigInt赋值问题:重新赋值prize nftLottery.prize = nftLottery.prize.plus(event.params.entranceFee) } /* 处理PlayerToLottery关联实体 */ const playerLotteryId = getIdFromEventAddressInt(event.params.player, event.params.lotteryCounter) let playerToLottery = PlayerToLottery.load(playerLotteryId) if (!playerToLottery) { playerToLottery = new PlayerToLottery(playerLotteryId) // 必须赋值非空字段player和lottery playerToLottery.player = player playerToLottery.lottery = nftLottery playerToLottery.save() } nftLottery.updatedAtTimestamp = event.block.timestamp nftLottery.save() }
修复说明
- 调整顺序:先创建/加载
Player和NftLottery,确保后续创建PlayerToLottery时可以直接关联 - 给
PlayerToLottery实例赋值player和lottery字段后再调用save() - 修正
prize字段的更新逻辑:重新赋值nftLottery.prize - 删除手动维护
nftLottery.players数组的代码,让@derivedFrom自动处理关联关系
内容的提问来源于stack exchange,提问作者haraslub
相关产品推荐
相关产品推荐

