Skip to content

Commit

Permalink
@airswap/stores: index and store by chainId; flexible filters (#1288)
Browse files Browse the repository at this point in the history
* index and store by chainId; flexible filters

* clarity and existing order quickfix
  • Loading branch information
dmosites authored Feb 14, 2024
1 parent 2ab20e8 commit 3e55e78
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 41 deletions.
2 changes: 1 addition & 1 deletion tools/stores/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@airswap/stores",
"version": "4.2.0",
"version": "4.2.1",
"description": "AirSwap: Storage for Indexing",
"repository": {
"type": "git",
Expand Down
5 changes: 4 additions & 1 deletion tools/stores/redis/redis.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ require('dotenv').config({ path: '../../.env' })
const { SchemaFieldTypes } = require('redis')

export default async function reset(client) {
await client.flushAll()
await client.ft.create(
'index:ordersBySigner',
{
'$.chainId': {
type: SchemaFieldTypes.NUMERIC,
AS: 'chainId',
},
'$.nonce': {
type: SchemaFieldTypes.TEXT,
AS: 'nonce',
Expand Down
1 change: 1 addition & 0 deletions tools/stores/redis/redis.init.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ async function main() {
})
try {
await client.connect()
await client.flushAll()
await config['default'](client)
console.log('Flushed and created Redis indexes.')
process.exit(0)
Expand Down
79 changes: 54 additions & 25 deletions tools/stores/redis/redis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,21 @@ function tagsKey(token: string) {
return `tags:${token.toLowerCase()}`
}

function tokenKey(token: string, id: string) {
return `ordersByToken:${token.toLowerCase()}:${id}`
function tokenKey(chainId: number, signerToken: string, id: string) {
return `ordersByToken:${chainId}:${signerToken.toLowerCase()}:${id}`
}

function signerKey(signer: string, nonce: string) {
return `ordersBySigner:${signer.toLowerCase()}:${nonce}`
function signerKey(chainId: number, signerWallet: string, nonce: string) {
return `ordersBySigner:${chainId}:${signerWallet.toLowerCase()}:${nonce}`
}

function cleanTags(tags: string[]) {
return tags.map((tag) =>
tag.replace(/\:/g, '\\:').replace(/\=/g, '\\=').replace(/\|/g, '\\|')
tag
.replace(/\s/g, '\\ ')
.replace(/\:/g, '\\:')
.replace(/\=/g, '\\=')
.replace(/\|/g, '\\|')
)
}

Expand All @@ -49,41 +53,45 @@ export class Redis {
}

const existing = await this.client.json.get(
tokenKey(order.signer.token, order.signer.id)
tokenKey(order.chainId, order.signer.token, order.signer.id)
)

// Delete existing order if found.
if (existing) {
const existingOrder = await this.client.json.get(
signerKey(existing.signerWallet, existing.nonce)
signerKey(existing[0], existing[1], existing[2])
)
await this.client.json.del(
signerKey(existing.signerWallet, existing.nonce)
signerKey(existing[0], existing[1], existing[2])
)
await this.client.json.del(
tokenKey(existingOrder.signer.token, existingOrder.signer.id)
tokenKey(
existingOrder.chainId,
existingOrder.signer.token,
existingOrder.signer.id
)
)
}

// Set order by signer wallet and nonce.
await this.client.json.set(
signerKey(order.signer.wallet, order.nonce),
signerKey(order.chainId, order.signer.wallet, order.nonce),
'$',
{ ...order, tags: tags || [] }
)
await this.client.expire(
signerKey(order.signer.wallet, order.nonce),
signerKey(order.chainId, order.signer.wallet, order.nonce),
this.ttl
)

// Set unique by signer token and signer id.
await this.client.json.set(
tokenKey(order.signer.token, order.signer.id),
tokenKey(order.chainId, order.signer.token, order.signer.id),
'$',
{ nonce: order.nonce, signerWallet: order.signer.wallet.toLowerCase() }
[order.chainId, order.signer.wallet.toLowerCase(), order.nonce]
)
await this.client.expire(
tokenKey(order.signer.token, order.signer.id),
tokenKey(order.chainId, order.signer.token, order.signer.id),
this.ttl
)

Expand Down Expand Up @@ -113,12 +121,20 @@ export class Redis {
}

const args = []
if (filter.senderToken) args.push(`@senderToken:${filter.senderToken}`)
if (filter.signerToken) args.push(`@signerToken:${filter.signerToken}`)
if (filter.signerId) args.push(`@signerId:${filter.signerId}`)
if (filter.signerWallet) args.push(`@signerWallet:${filter.signerWallet}`)
if (filter.tags?.length)
args.push(`@tags:{${cleanTags(filter.tags).join('|')}}`)
for (const prop in filter) {
switch (prop) {
case 'chainId':
args.push(`@chainId:[${filter.chainId} ${filter.chainId}]`)
break
case 'tags':
if (filter.tags.length) {
args.push(`@tags:{${cleanTags(filter.tags).join('|')}}`)
}
break
default:
args.push(`@${prop}:(${filter[prop]})`)
}
}

const { total, documents } = await this.client.ft.search(
'index:ordersBySigner',
Expand All @@ -135,27 +151,40 @@ export class Redis {
}
}

public async delete(signerWallet: string, nonce: string) {
public async delete(chainId: number, signerWallet: string, nonce: string) {
if (!this.client.isOpen) {
await this.client.connect()
}

const order = await this.client.json.get(signerKey(signerWallet, nonce))
const order = await this.client.json.get(
signerKey(chainId, signerWallet, nonce)
)
if (order) {
await this.client.json.del(signerKey(order.signer.wallet, order.nonce))
await this.client.json.del(tokenKey(order.signer.token, order.signer.id))
await this.client.json.del(
signerKey(order.chainId, order.signer.wallet, order.nonce)
)
await this.client.json.del(
tokenKey(order.chainId, order.signer.token, order.signer.id)
)
return true
}
return false
}

public async reset() {
public async setup() {
if (!this.client.isOpen) {
await this.client.connect()
}
await reset(this.client)
}

public async flush() {
if (!this.client.isOpen) {
await this.client.connect()
}
await this.client.flushAll()
}

public async disconnect() {
if (this.client.isOpen) {
await this.client.disconnect()
Expand Down
54 changes: 40 additions & 14 deletions tools/stores/test/redis.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { expect } from 'chai'
import { Redis } from '../redis/redis'
import { ChainIds } from '@airswap/utils'

import {
createOrder,
Expand All @@ -10,10 +11,18 @@ import {

const store = new Redis(process.env.REDISCLOUD_URL)

const TOKEN_ADDRESS = '0x000000000000000000000000000000000000000A'
const signerOne = '0x0000000000000000000000000000000000000001'
const signerTwo = '0x0000000000000000000000000000000000000002'

let orderOne
let orderTwo

async function createSignedOrder(
id: string,
signerWallet = ADDRESS_ZERO,
signerToken = '0x000000000000000000000000000000000000000A'
signerToken = TOKEN_ADDRESS,
chainId = 1
): Promise<FullOrder> {
const unsignedOrder = createOrder({
signer: {
Expand All @@ -23,7 +32,7 @@ async function createSignedOrder(
},
})
return {
chainId: 1,
chainId,
swapContract: ADDRESS_ZERO,
...unsignedOrder,
...(await createOrderSignature(
Expand All @@ -34,15 +43,10 @@ async function createSignedOrder(
}
}

const signerOne = '0x0000000000000000000000000000000000000001'
const signerTwo = '0x0000000000000000000000000000000000000002'

let orderOne
let orderTwo

describe('Redis', async () => {
before(async () => {
await store.reset()
beforeEach(async () => {
await store.flush()
await store.setup()
orderOne = await createSignedOrder('1', signerOne)
orderTwo = await createSignedOrder('2', signerTwo)
})
Expand All @@ -54,15 +58,26 @@ describe('Redis', async () => {
it('Tags are stored', async () => {
await store.write(orderOne, ['grass:green', 'sky:blue'])
await store.write(orderTwo, ['grass:green', 'grass:green', 'sky:grey'])
const tokenTags = await store.tags(orderOne.signer.token)
const tokenTags = await store.tags(TOKEN_ADDRESS)
expect(tokenTags).to.deep.equal(['grass:green', 'sky:blue', 'sky:grey'])
})

it('Query by chain id', async () => {
await store.write(orderOne)
await store.write(
await createSignedOrder('2', signerTwo, TOKEN_ADDRESS, ChainIds.SEPOLIA)
)
const res = await store.read({
chainId: ChainIds.SEPOLIA,
})
expect(res.total).to.equal(1)
})

it('Query by signer token', async () => {
await store.write(orderOne)
await store.write(orderTwo)
const res = await store.read({
signerToken: orderOne.signer.token,
signerToken: TOKEN_ADDRESS,
})
expect(res.total).to.equal(2)
expect(res.orders[0].signer.id).to.equal('1')
Expand All @@ -89,13 +104,24 @@ describe('Redis', async () => {
'',
])
const res = await store.read({
signerToken: orderOne.signer.token,
signerToken: TOKEN_ADDRESS,
tags: ['Sun:Bright Yellow'],
})
expect(res.total).to.equal(1)
expect(res.orders[0].signer.id).to.equal('1')
})

it('Query with empty tags', async () => {
await store.write(orderOne)
await store.write(orderTwo)
const res = await store.read({
signerToken: TOKEN_ADDRESS,
tags: [],
})
expect(res.total).to.equal(2)
expect(res.orders[0].signer.id).to.equal('1')
})

it('Query with pagination', async () => {
const OFFSET = 5
const LIMIT = 30
Expand All @@ -106,7 +132,7 @@ describe('Redis', async () => {
}
const res = await store.read(
{
signerToken: orderOne.signer.token,
signerToken: TOKEN_ADDRESS,
},
OFFSET,
LIMIT
Expand Down

0 comments on commit 3e55e78

Please sign in to comment.