Skip to content

Commit

Permalink
feat: add RSKOwner contract to the subgraph
Browse files Browse the repository at this point in the history
  • Loading branch information
jonathansmirnoff committed Jan 28, 2025
1 parent c8d34ef commit de85f27
Show file tree
Hide file tree
Showing 9 changed files with 832 additions and 2 deletions.
462 changes: 462 additions & 0 deletions abis/RSKOwner.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions networks.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
},
"RNS": {
"address": "0xcb868aeabd31e2b66f74e9a55cf064abb31a4ad5"
},
"RSKOwner": {
"address": "0x45d3e4FB311982A06bA52359d44cb4f5980e0ef1"
}
}
}
77 changes: 76 additions & 1 deletion schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,13 @@ type Domain @entity {

"The account that owns the domain"
owner: Account!
"The account that owns the ERC721 NFT for the domain"
registrant: Account

"The expiry date for the domain, from either the registration, or the wrapped domain if PCC is burned"
expiryDate: BigInt
"The registration associated with the domain"
registration: Registration @derivedFrom(field: "domain")
}

type Account @entity {
Expand Down Expand Up @@ -141,4 +145,75 @@ type NewTTL implements DomainEvent @entity {
transactionID: Bytes!
"The new TTL value (in seconds) associated with the domain"
ttl: BigInt!
}
}

type ExpiryExtended implements DomainEvent @entity {
"The unique identifier of the event"
id: ID!
"The domain name associated with the event"
domain: Domain!
"The block number at which the event occurred"
blockNumber: Int!
"The transaction hash of the transaction that triggered the event"
transactionID: Bytes!
"The new expiry date associated with the domain after the extension event"
expiryDate: BigInt!
}

interface RegistrationEvent {
"The unique identifier of the registration event"
id: ID!
"The registration associated with the event"
registration: Registration!
"The block number of the event"
blockNumber: Int!
"The transaction ID associated with the event"
transactionID: Bytes!
}

type NameRegistered implements RegistrationEvent @entity {
"The unique identifier of the NameRegistered event"
id: ID!
"The registration associated with the event"
registration: Registration!
"The block number of the event"
blockNumber: Int!
"The transaction ID associated with the event"
transactionID: Bytes!
"The account that registered the name"
registrant: Account!
"The expiry date of the registration"
expiryDate: BigInt!
}

type Registration @entity {
"The unique identifier of the registration"
id: ID!
"The domain name associated with the registration"
domain: Domain!
"The registration date of the domain"
registrationDate: BigInt!
"The expiry date of the domain"
expiryDate: BigInt!
"The cost associated with the domain registration"
cost: BigInt
"The account that registered the domain"
registrant: Account!
"The human-readable label name associated with the domain registration"
labelName: String
"The events associated with the domain registration"
events: [RegistrationEvent!]! @derivedFrom(field: "registration")
}

type NameTransferred implements RegistrationEvent @entity {
"The ID of the event"
id: ID!
"The registration associated with the event"
registration: Registration!
"The block number of the event"
blockNumber: Int!
"The transaction ID of the event"
transactionID: Bytes!
"The new owner of the domain"
newOwner: Account!
}
78 changes: 78 additions & 0 deletions src/rsk-owner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { ByteArray, crypto, ens, log } from "@graphprotocol/graph-ts";
import {
Approval as ApprovalEvent,
ApprovalForAll as ApprovalForAllEvent,
ExpirationChanged as ExpirationChangedEvent,
OwnershipTransferred as OwnershipTransferredEvent,
Transfer as TransferEvent,
} from "../generated/RSKOwner/RSKOwner"
import {
Account,
Domain,
NameRegistered,
NameTransferred,
Registration,
Transfer,
} from "../generated/schema"
import { checkValidLabel, concat, createEventID, RSK_NODE, uint256ToByteArray } from "./utils";

var rootNode: ByteArray = ByteArray.fromHexString(RSK_NODE);

export function handleExpirationChanged(event: ExpirationChangedEvent): void {
let label = uint256ToByteArray(event.params.tokenId);
let domain = Domain.load(crypto.keccak256(concat(rootNode, label)).toHex());

// Workaround for the case when the domain is not found
if (domain == null) return ;

let registration = new Registration(label.toHex());

registration.domain = domain.id;
registration.registrationDate = event.block.timestamp;
registration.expiryDate = event.params.expirationTime;
registration.registrant = domain.owner;

domain.expiryDate = event.params.expirationTime;


let labelName = ens.nameByHash(label.toHexString());
if (checkValidLabel(labelName)) {
domain.labelName = labelName;
domain.name = labelName! + ".rsk";
registration.labelName = labelName;
}
domain.save();
registration.save();

let registrationEvent = new NameRegistered(createEventID(event));
registrationEvent.registration = registration.id;
registrationEvent.blockNumber = event.block.number.toI32();
registrationEvent.transactionID = event.transaction.hash;
registrationEvent.expiryDate = event.params.expirationTime;
registrationEvent.registrant = domain.owner;
registrationEvent.save();
}

export function handleTransfer(event: TransferEvent): void {
let account = new Account(event.params.to.toHex());
account.save();

let label = uint256ToByteArray(event.params.tokenId);
let registration = Registration.load(label.toHex());
if (registration == null) return;

let domain = Domain.load(crypto.keccak256(concat(rootNode, label)).toHex())!;

registration.registrant = account.id;
domain.registrant = account.id;

domain.save();
registration.save();

let transferEvent = new NameTransferred(createEventID(event));
transferEvent.registration = label.toHex();
transferEvent.blockNumber = event.block.number.toI32();
transferEvent.transactionID = event.transaction.hash;
transferEvent.newOwner = account.id;
transferEvent.save();
}
31 changes: 31 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { BigInt, ByteArray, ethereum, log } from "@graphprotocol/graph-ts";

export const RSK_NODE =
"0x0cd5c10192478cd220936e91293afc15e3f6de4d419de5de7506b679cbdd8ec4";
export const ROOT_NODE =
"0x0000000000000000000000000000000000000000000000000000000000000000";
export const EMPTY_ADDRESS = "0x0000000000000000000000000000000000000000";
Expand Down Expand Up @@ -45,4 +47,33 @@ export function checkValidLabel(name: string | null): boolean {
}

return true;
}

export function byteArrayFromHex(s: string): ByteArray {
if (s.length % 2 !== 0) {
throw new TypeError("Hex string must have an even number of characters");
}
let out = new Uint8Array(s.length / 2);
for (var i = 0; i < s.length; i += 2) {
out[i / 2] = parseInt(s.substring(i, i + 2), 16) as u32;
}
return changetype<ByteArray>(out);
}

export function uint256ToByteArray(i: BigInt): ByteArray {
let hex = i.toHex().slice(2).padStart(64, "0");
return byteArrayFromHex(hex);
}

// Helper for concatenating two byte arrays
export function concat(a: ByteArray, b: ByteArray): ByteArray {
let out = new Uint8Array(a.length + b.length);
for (let i = 0; i < a.length; i++) {
out[i] = a[i];
}
for (let j = 0; j < b.length; j++) {
out[a.length + j] = b[j];
}
// return out as ByteArray
return changetype<ByteArray>(out);
}
23 changes: 23 additions & 0 deletions subgraph.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,26 @@ dataSources:
- event: NewTTL(indexed bytes32,uint64)
handler: handleNewTTL
file: ./src/rns.ts
- kind: ethereum
name: RSKOwner
network: rootstock
source:
address: "0x45d3e4FB311982A06bA52359d44cb4f5980e0ef1"
abi: RSKOwner
startBlock: 1891388
mapping:
kind: ethereum/events
apiVersion: 0.0.7
language: wasm/assemblyscript
entities:
- OwnershipTransferred
- RSKOwnerTransfer
abis:
- name: RSKOwner
file: ./abis/RSKOwner.json
eventHandlers:
- event: ExpirationChanged(uint256,uint256)
handler: handleExpirationChanged
- event: Transfer(indexed address,indexed address,indexed uint256)
handler: handleTransfer
file: ./src/rsk-owner.ts
2 changes: 1 addition & 1 deletion tests/.latest.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"version": "0.6.0",
"timestamp": 1737655167212
"timestamp": 1738072715812
}
127 changes: 127 additions & 0 deletions tests/rsk-owner-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { newMockEvent } from "matchstick-as"
import { ethereum, Address, BigInt } from "@graphprotocol/graph-ts"
import {
Approval,
ApprovalForAll,
ExpirationChanged,
OwnershipTransferred,
Transfer
} from "../generated/RSKOwner/RSKOwner"

export function createApprovalEvent(
owner: Address,
approved: Address,
tokenId: BigInt
): Approval {
let approvalEvent = changetype<Approval>(newMockEvent())

approvalEvent.parameters = new Array()

approvalEvent.parameters.push(
new ethereum.EventParam("owner", ethereum.Value.fromAddress(owner))
)
approvalEvent.parameters.push(
new ethereum.EventParam("approved", ethereum.Value.fromAddress(approved))
)
approvalEvent.parameters.push(
new ethereum.EventParam(
"tokenId",
ethereum.Value.fromUnsignedBigInt(tokenId)
)
)

return approvalEvent
}

export function createApprovalForAllEvent(
owner: Address,
operator: Address,
approved: boolean
): ApprovalForAll {
let approvalForAllEvent = changetype<ApprovalForAll>(newMockEvent())

approvalForAllEvent.parameters = new Array()

approvalForAllEvent.parameters.push(
new ethereum.EventParam("owner", ethereum.Value.fromAddress(owner))
)
approvalForAllEvent.parameters.push(
new ethereum.EventParam("operator", ethereum.Value.fromAddress(operator))
)
approvalForAllEvent.parameters.push(
new ethereum.EventParam("approved", ethereum.Value.fromBoolean(approved))
)

return approvalForAllEvent
}

export function createExpirationChangedEvent(
tokenId: BigInt,
expirationTime: BigInt
): ExpirationChanged {
let expirationChangedEvent = changetype<ExpirationChanged>(newMockEvent())

expirationChangedEvent.parameters = new Array()

expirationChangedEvent.parameters.push(
new ethereum.EventParam(
"tokenId",
ethereum.Value.fromUnsignedBigInt(tokenId)
)
)
expirationChangedEvent.parameters.push(
new ethereum.EventParam(
"expirationTime",
ethereum.Value.fromUnsignedBigInt(expirationTime)
)
)

return expirationChangedEvent
}

export function createOwnershipTransferredEvent(
previousOwner: Address,
newOwner: Address
): OwnershipTransferred {
let ownershipTransferredEvent =
changetype<OwnershipTransferred>(newMockEvent())

ownershipTransferredEvent.parameters = new Array()

ownershipTransferredEvent.parameters.push(
new ethereum.EventParam(
"previousOwner",
ethereum.Value.fromAddress(previousOwner)
)
)
ownershipTransferredEvent.parameters.push(
new ethereum.EventParam("newOwner", ethereum.Value.fromAddress(newOwner))
)

return ownershipTransferredEvent
}

export function createTransferEvent(
from: Address,
to: Address,
tokenId: BigInt
): Transfer {
let transferEvent = changetype<Transfer>(newMockEvent())

transferEvent.parameters = new Array()

transferEvent.parameters.push(
new ethereum.EventParam("from", ethereum.Value.fromAddress(from))
)
transferEvent.parameters.push(
new ethereum.EventParam("to", ethereum.Value.fromAddress(to))
)
transferEvent.parameters.push(
new ethereum.EventParam(
"tokenId",
ethereum.Value.fromUnsignedBigInt(tokenId)
)
)

return transferEvent
}
Loading

0 comments on commit de85f27

Please sign in to comment.