Skip to content
This repository has been archived by the owner on May 19, 2023. It is now read-only.

Commit

Permalink
feat: batch support for domain registration (#293)
Browse files Browse the repository at this point in the history
Co-authored-by: Adam Uhlíř <adam@uhlir.dev>
  • Loading branch information
julianmrodri and AuHau authored Sep 15, 2020
1 parent 678a764 commit 971fb7e
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 25 deletions.
1 change: 1 addition & 0 deletions config/ganache.json5
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
},
rns: {
enabled: true,
batchContractAddress: "0xc0b3B62DD0400E4baa721DdEc9B8A384147b23fF",
owner: {
contractAddress: "0x4bf749ec68270027C5910220CEAB30Cc284c7BA2",
eventsEmitter: {
Expand Down
1 change: 1 addition & 0 deletions config/rskmainnet.json5
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
enabled: false,
},
rns: {
batchContractAddress: "0x2beb2819e110b34c802c761e737470760af2057f",
owner: {
contractAddress: "0x45d3e4fb311982a06ba52359d44cb4f5980e0ef1",
eventsEmitter: {
Expand Down
1 change: 1 addition & 0 deletions config/rsktestnet.json5
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
enabled: false,
},
rns: {
batchContractAddress: "0x380a47e2a1cec8a21cf00374ea5d092166a702fc",
owner: {
contractAddress: "0xca0a477e19bac7e0e172ccfd2e3c28a7200bdb71",
eventsEmitter: {
Expand Down
1 change: 1 addition & 0 deletions config/test.json5
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
},
rns: {
enabled: true,
batchContractAddress: "0xc0b3b62dd0400e4baa721ddec9b8a384147b23ff", // encoded address used in tests
owner: {
contractAddress: "OWNER_ADDR"
},
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
"pg": "^8.2.1",
"pg-hstore": "^2.3.3",
"reflect-metadata": "^0.1.13",
"rlp": "^2.2.6",
"sequelize": "^5.21.10",
"sequelize-store": "^0.3.1",
"sequelize-typescript": "^1.1.0",
Expand Down
74 changes: 55 additions & 19 deletions src/services/rns/rns.processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ import DomainExpiration from './models/expiration.model'
import DomainOwner from './models/owner.model'
import Transfer from './models/transfer.model'
import SoldDomain from './models/sold-domain.model'
import RLP = require('rlp')

type RLPDecoded = Array<Array<number[]>>

/**
* Updates Domain Owner
Expand Down Expand Up @@ -64,13 +67,48 @@ async function registerTransfer (
return transferDomain.id
}

/**
* Decode domain name
* @param decodedData
* @param tokenId
* @param batchAddr
*/
function getDomainName (decodedData: DecodedData, tokenId: string, batchAddr: string): string | undefined {
if (decodedData) {
let name: string | undefined

if (decodedData.params[0].value === batchAddr) {
// Batch registration
const rlpDecoded: RLPDecoded = RLP.decode(decodedData.params[2].value) as unknown as RLPDecoded
const domainNames = rlpDecoded[1].map(
(domainData: number[]) =>
Utils.hexToAscii(
Utils.bytesToHex(
domainData.slice(88, domainData.length)
)
)
)
name = domainNames.find(
(domain: string) =>
Utils.numberToHex(Utils.sha3(domain) as string) === tokenId
)
} else {
// Single registration
const domainData: string = decodedData.params[2].value
name = Utils.hexToAscii('0x' + domainData.slice(218, domainData.length))
}

return name
}
}

async function transferHandler (logger: Logger, eventData: EventData, eth: Eth, services: RnsServices): Promise<void> {
const tokenId = Utils.numberToHex(eventData.returnValues.tokenId)
const ownerAddress = eventData.returnValues.to.toLowerCase()

const fiftsAddr = config.get('rns.fifsAddrRegistrar.contractAddress')
const registrar = config.get('rns.registrar.contractAddress')
const marketplace = config.get<string>('rns.placement.contractAddress')
const fifsAddr = config.get<string>('rns.fifsAddrRegistrar.contractAddress').toLowerCase()
const registrarAddr = config.get<string>('rns.registrar.contractAddress').toLowerCase()
const marketplaceAddr = config.get<string>('rns.placement.contractAddress').toLowerCase()
const batchAddr = config.get<string>('rns.batchContractAddress').toLowerCase()
const tld = config.get('rns.tld')

const transactionHash = eventData.transactionHash
Expand All @@ -83,33 +121,31 @@ async function transferHandler (logger: Logger, eventData: EventData, eth: Eth,
const transaction = await eth.getTransaction(transactionHash)
const decodedData: DecodedData = abiDecoder.decodeMethod(transaction.input)

if (decodedData) {
const name = Utils.hexToAscii('0x' + decodedData.params[2].value.slice(218, decodedData.params[2].value.length))
const name: string | undefined = getDomainName(decodedData, tokenId, batchAddr)

if (name) {
try {
await Domain.upsert({ tokenId, name: `${name}.${tld}` })
} catch (e) {
await Domain.upsert({ tokenId })
logger.warn(`Domain name ${name}.${tld} for token ${tokenId} could not be stored.`)
}
if (name) {
try {
await Domain.upsert({ tokenId, name: `${name}.${tld}` })
} catch (e) {
await Domain.upsert({ tokenId })
logger.warn(`Domain name ${name}.${tld} for token ${tokenId} could not be stored.`)
}

if (domainsService.emit) {
domainsService.emit('patched', { tokenId })
}
if (domainsService.emit) {
domainsService.emit('patched', { tokenId })
}
}
}

if (ownerAddress === (fiftsAddr as string).toLowerCase()) {
if (ownerAddress === fifsAddr) {
return
}

if (ownerAddress === (registrar as string).toLowerCase()) {
if (ownerAddress === registrarAddr) {
return
}

if (ownerAddress === marketplace.toLowerCase() || from === marketplace.toLowerCase()) {
if (ownerAddress === marketplaceAddr || from === marketplaceAddr) {
return // Marketplace transfers are handled in TokenSold
}

Expand Down
57 changes: 53 additions & 4 deletions test/services/rns/processor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import DomainExpiration from '../../../src/services/rns/models/expiration.model'
import DomainOffer from '../../../src/services/rns/models/domain-offer.model'
import SoldDomain from '../../../src/services/rns/models/sold-domain.model'

import { eventMock } from '../../utils'
import { eventMock, transactionMock } from '../../utils'

chai.use(sinonChai)
chai.use(chaiAsPromised)
Expand All @@ -39,10 +39,15 @@ describe('Domain events', () => {
const label = 'domain'
const name = 'domain.rsk'
const tokenId = Utils.sha3(label) as string
const labelOther = 'domainother'
const nameOther = 'domainother.rsk'
const tokenOtherId = Utils.sha3(labelOther) as string
const from = 'from_addr'
const to = 'to_addr'
const other = 'other_addr'
const expirationTime = (new Date()).getTime().toString()
const zeroAddress = '0x0000000000000000000000000000000000000000'
const transactionHash = 'TX_HASH'

before(() => {
sequelize = sequelizeFactory()
Expand Down Expand Up @@ -78,7 +83,7 @@ describe('Domain events', () => {
it('should update Domain Owner on Tranfer', async () => {
const event = eventMock({
event: 'Transfer',
transactionHash: 'TX_HASH',
transactionHash,
returnValues: { tokenId, from, to }
})

Expand All @@ -100,12 +105,12 @@ describe('Domain events', () => {
it('should handle multiple Transfers in the same transaction', async () => {
const event = eventMock({
event: 'Transfer',
transactionHash: 'TX_HASH',
transactionHash,
returnValues: { tokenId, from, to }
})
const newEvent = eventMock({
event: 'Transfer',
transactionHash: 'TX_HASH',
transactionHash,
returnValues: { tokenId, from: to, to: other }
})

Expand Down Expand Up @@ -237,6 +242,50 @@ describe('Domain events', () => {
expect(createdEvent).to.be.instanceOf(Domain)
expect(createdEvent?.name).to.be.eql(event.returnValues.name)
})

it('should Decode Name for new Domain - Batch', async () => {
// Encoded multiple registrations for `domain.rsk` and `domainother.rsk`
const txInput = '0x4000aea0000000000000000000000000c0b3b62dd0400e4baa721ddec9b8a384147b23ff' +
'0000000000000000000000000000000000000000000000003782dace9d90000000000000000000000000000000' +
'000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000' +
'0000000000d2f8d0881bc16d674ec80000f8c5b85ec2c414c890f8bf6a479f320ead074411a4b0e7944ea8c9c1' +
'6d97ebe8ec0de1264229e19b6681ec254dfdb4ee52ed336e6a58d6a635bba4dd00000000000000000000000000' +
'00000000000000000000000000000000000001646f6d61696eb863c2c414c890f8bf6a479f320ead074411a4b0' +
'e7944ea8c9c17a134ff0de2189b133a610ff8e4a0164c1fd91a7bb3c4052b1fb1bb6792a220800000000000000' +
'00000000000000000000000000000000000000000000000001646f6d61696e6f74686572000000000000000000' +
'0000000000'

const mockedTransaction = transactionMock(transactionHash, txInput)
eth.getTransaction(transactionHash).resolves(mockedTransaction)

const event = eventMock({
event: 'Transfer',
transactionHash,
returnValues: { tokenId, from: zeroAddress, to: to }
})

const newEvent = eventMock({
event: 'Transfer',
transactionHash,
returnValues: { tokenId: tokenOtherId, from: zeroAddress, to: to }
})

await processor(event)

const createdEvent = await Domain.findByPk(Utils.numberToHex(tokenId))

expect(createdEvent).to.be.instanceOf(Domain)
expect(createdEvent?.name).to.be.eql(name)
expect(domainServiceEmitSpy).to.have.been.calledWith('patched')

await processor(newEvent)

const newCreatedEvent = await Domain.findByPk(Utils.numberToHex(tokenOtherId))

expect(newCreatedEvent).to.be.instanceOf(Domain)
expect(newCreatedEvent?.name).to.be.eql(nameOther)
expect(domainServiceEmitSpy).to.have.been.calledWith('patched')
})
})

describe('Offer events', () => {
Expand Down
15 changes: 14 additions & 1 deletion test/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BlockHeader, BlockTransactionString, TransactionReceipt } from 'web3-eth'
import { BlockHeader, BlockTransactionString, TransactionReceipt, Transaction } from 'web3-eth'
import { Substitute } from '@fluffy-spoon/substitute'
import { EventData } from 'web3-eth-contract'

Expand Down Expand Up @@ -70,3 +70,16 @@ export function blockMock (blockNumber: number, blockHash = '0x123', options: Pa
block.hash.returns!(blockHash)
return block
}

export function transactionMock (hash: string, input: string, options: Partial<Transaction> = {}): Transaction {
const transaction = Substitute.for<Transaction>()

Object.entries(options).forEach(([key, value]) => {
// @ts-ignore
transaction[key].returns!(value)
})

transaction.hash.returns!(hash)
transaction.input.returns!(input)
return transaction
}

0 comments on commit 971fb7e

Please sign in to comment.