Skip to content

Commit

Permalink
chore: Coordinates
Browse files Browse the repository at this point in the history
  • Loading branch information
janjakubnanista committed Nov 23, 2023
1 parent 2b6e0e7 commit 7cb3a95
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 42 deletions.
6 changes: 6 additions & 0 deletions packages/ua-utils/src/coordinates.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { OmnichainGraphCoordinate } from "./types"

export const areSameCoordinates = (a: OmnichainGraphCoordinate, b: OmnichainGraphCoordinate): boolean =>
a.address === b.address && a.eid === b.eid

export const serializeCoordinate = ({ address, eid }: OmnichainGraphCoordinate): string => `${eid}|${address}`
1 change: 1 addition & 0 deletions packages/ua-utils/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./coordinates"
export * from "./schema"
export * from "./types"
16 changes: 10 additions & 6 deletions packages/ua-utils/src/schema.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import { EndpointId } from "@layerzerolabs/lz-definitions"
import { z } from "zod"
import type { ContractSpec } from "./types"
import type { OmnichainGraphCoordinate, OmnichainGraphNode } from "./types"

export const AddressSchema = z.string()

export const EndpointIdSchema = z.nativeEnum(EndpointId).pipe(z.number())

export const createContractSpecSchema = <TConfig = unknown>(
export const OmnichainGraphCoordinateSchema: z.ZodSchema<OmnichainGraphCoordinate, z.ZodTypeDef, unknown> = z.object({
address: AddressSchema,
eid: EndpointIdSchema,
})

export const createOmnichainGraphNodeSchema = <TConfig = unknown>(
configSchema: z.ZodSchema<TConfig, z.ZodTypeDef, unknown>
): z.ZodSchema<ContractSpec<TConfig>, z.ZodTypeDef, unknown> =>
): z.ZodSchema<OmnichainGraphNode<TConfig>, z.ZodTypeDef, unknown> =>
z.object({
address: AddressSchema,
endpointId: EndpointIdSchema,
coordinate: OmnichainGraphCoordinateSchema,
config: configSchema,
}) as z.ZodSchema<ContractSpec<TConfig>, z.ZodTypeDef, unknown>
}) as z.ZodSchema<OmnichainGraphNode<TConfig>, z.ZodTypeDef, unknown>
45 changes: 37 additions & 8 deletions packages/ua-utils/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,48 @@ import type { EndpointId } from "@layerzerolabs/lz-definitions"

export type Address = string

export interface ContractSpec<TConfig = unknown> {
endpointId: EndpointId
/**
* OmnichainGraphCoordinate identifies a point in omniverse, an omnichain universe.
*
* In layman terms this is a contract deployed on a particular network (represented by an endpoint).
*/
export interface OmnichainGraphCoordinate {
eid: EndpointId
address: Address
}

/**
* OmnichainGraphNode represents a point in omniverse
* with an additional piece of information attached
*/
export interface OmnichainGraphNode<TConfig = unknown> {
coordinate: OmnichainGraphCoordinate
config: TConfig
}

export interface ContractConnectionSpec<TConfig = unknown> {
from: EndpointId
to: EndpointId
/**
* OmnichainGraphEdge represents a connection between two points in omniverse
* with an additional piece of information attached
*
* TODO There is a certain asymetry now between OmnichainGraphNode and OmnichainGraphEdge -
* OmnichainGraphNode contains a coordinate and config whereas OmnichainGraphEdge has no wrapper
* for the coordinate part - the coordinate (which in this case is a tuple [from, to]) is spread directly
* on its type. This asymetry looks fine - the sacrifice of verbosity is made in the name of smaller boilerplate
*/
export interface OmnichainGraphEdge<TConfig = unknown> {
from: OmnichainGraphCoordinate
to: OmnichainGraphCoordinate
config: TConfig
}

export interface OmnichainSpec<TContractConfig = unknown, TConnectionConfig = unknown> {
contracts: ContractSpec<TContractConfig>[]
connections: ContractConnectionSpec<TConnectionConfig>[]
/**
* OmnichainGraph is a collection of nodes and edges of omniverse
* that together represent an omnichain app a.k.a. OApp.
*
* For purposes of readability and to avoid overabstraction on the user end,
* the names are set to be `contracts` rather than `nodes` and `connections` rather than `edges`
*/
export interface OmnichainGraph<TNodeConfig = unknown, TEdgeConfig = unknown> {
contracts: OmnichainGraphNode<TNodeConfig>[]
connections: OmnichainGraphEdge<TEdgeConfig>[]
}
45 changes: 45 additions & 0 deletions packages/ua-utils/test/coordinates.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { areSameCoordinates } from "@/coordinates"
import { OmnichainGraphCoordinate } from "@/types"
import { EndpointId } from "@layerzerolabs/lz-definitions"

describe("coordinates", () => {
describe("areSameCoordinates", () => {
type TestCase = [OmnichainGraphCoordinate, OmnichainGraphCoordinate]

const GOOD: TestCase[] = [
[
{ eid: EndpointId.BSC_TESTNET, address: "0x0" },
{ eid: EndpointId.BSC_TESTNET, address: "0x0" },
],
[
{ eid: EndpointId.SOLANA_MAINNET, address: "mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So" },
{ eid: EndpointId.SOLANA_MAINNET, address: "mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So" },
],
]

it.each(GOOD)(`should be true for %j and %j`, (a, b) => {
expect(areSameCoordinates(a, b)).toBeTruthy()
expect(areSameCoordinates(a, a)).toBeTruthy()
expect(areSameCoordinates(b, b)).toBeTruthy()
})

const BAD: TestCase[] = [
[
{ eid: EndpointId.ETHEREUM_MAINNET, address: "0x0" },
{ eid: EndpointId.BSC_TESTNET, address: "0x0" },
],
[
{ eid: EndpointId.ETHEREUM_MAINNET, address: "0x0" },
{ eid: EndpointId.ETHEREUM_MAINNET, address: "0x1" },
],
[
{ eid: EndpointId.ETHEREUM_MAINNET, address: "0xa" },
{ eid: EndpointId.ETHEREUM_MAINNET, address: "0xA" },
],
]

it.each(BAD)(`should be false for %j and %j`, (a, b) => {
expect(areSameCoordinates(a, b)).toBeFalsy()
})
})
})
50 changes: 22 additions & 28 deletions packages/ua-utils/test/schema.test.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,50 @@
import { createContractSpecSchema } from "@/schema"
import { createOmnichainGraphNodeSchema } from "@/schema"
import { OmnichainGraphCoordinate } from "@/types"
import { EndpointId } from "@layerzerolabs/lz-definitions"
import { expect } from "chai"
import { z } from "zod"

describe("schema", () => {
describe("createContractSpecSchema", () => {
describe("createOmnichainGraphNodeSchema", () => {
interface TestCase {
schema: z.ZodSchema
configSchema: z.ZodSchema
good: unknown[]
bad: unknown[]
}

const TEST_CASES: TestCase[] = [
{
schema: z.string(),
configSchema: z.string(),
good: ["config", ""],
bad: [false, null, undefined, 1, {}],
},
{
schema: z.object({ a: z.number().nonnegative() }),
configSchema: z.object({ a: z.number().nonnegative() }),
good: [{ a: 0 }, { a: 1 }],
bad: [{ a: -1 }, false, "", []],
},
]

TEST_CASES.forEach(({ schema, good, bad }, index) => {
const contractSpecSchema = createContractSpecSchema(schema)
describe.each(TEST_CASES)(`schema`, ({ configSchema, good, bad }) => {
const schema = createOmnichainGraphNodeSchema(configSchema)
const coordinate: OmnichainGraphCoordinate = { eid: EndpointId.APTOS_MAINNET, address: "0x0" }

describe(`case ${index}`, () => {
good.forEach((config) => {
it(`should work for ${JSON.stringify(config)}`, () => {
const spec = {
endpointId: EndpointId.APTOS_MAINNET,
address: "0x0",
config,
}
it.each(good)(`should pass with %j`, (config) => {
const node = {
coordinate,
config,
}

expect(contractSpecSchema.parse(spec)).to.eql(spec)
})
})
expect(schema.parse(node)).to.eql(node)
})

bad.forEach((config) => {
it(`should not work for ${JSON.stringify(config)}`, () => {
const spec = {
endpointId: EndpointId.APTOS_MAINNET,
address: "0x0",
config,
}
it.each(bad)(`should fail with %j`, (config) => {
const node = {
coordinate,
config,
}

expect(() => contractSpecSchema.parse(spec)).to.throw()
})
})
expect(() => schema.parse(node)).to.throw()
})
})
})
Expand Down

0 comments on commit 7cb3a95

Please sign in to comment.