Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
janjakubnanista committed Nov 21, 2023
1 parent 32a99f4 commit 1898c46
Show file tree
Hide file tree
Showing 14 changed files with 303 additions and 0 deletions.
2 changes: 2 additions & 0 deletions packages/utils-evm/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dist
node_modules
3 changes: 3 additions & 0 deletions packages/utils-evm/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
artifacts
cache
deployments
2 changes: 2 additions & 0 deletions packages/utils-evm/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dist/
node_modules/
7 changes: 7 additions & 0 deletions packages/utils-evm/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# @layerzerolabs/utils-evm-hardhat

## 0.0.2

### Patch Changes

- 6964deb: Memoize create\* calls
27 changes: 27 additions & 0 deletions packages/utils-evm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<p align="center">
<a href="https://layerzero.network">
<img alt="LayerZero" style="max-width: 500px" src="https://d3a2dpnnrypp5h.cloudfront.net/bridge-app/lz.png"/>
</a>
</p>

<h1 align="center">@layerzerolabs/utils-evm-hardhat</h1>

<!-- The badges section -->
<p align="center">
<!-- Shields.io NPM published package version -->
<a href="https://www.npmjs.com/package/@layerzerolabs/utils-evm-hardhat"><img alt="NPM Version" src="https://img.shields.io/npm/v/@layerzerolabs/utils-evm-hardhat"/></a>
<!-- Shields.io NPM downloads -->
<a href="https://www.npmjs.com/package/@layerzerolabs/utils-evm-hardhat"><img alt="Downloads" src="https://img.shields.io/npm/dm/@layerzerolabs/utils-evm-hardhat"/></a>
<!-- Shields.io license badge -->
<a href="https://www.npmjs.com/package/@layerzerolabs/utils-evm-hardhat"><img alt="NPM License" src="https://img.shields.io/npm/l/@layerzerolabs/utils-evm-hardhat"/></a>
</p>

## Installation

```bash
yarn add @layerzerolabs/utils-evm-hardhat

pnpm add @layerzerolabs/utils-evm-hardhat

npm install @layerzerolabs/utils-evm-hardhat
```
12 changes: 12 additions & 0 deletions packages/utils-evm/hardhat.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import "hardhat-deploy"
import { HardhatUserConfig } from "hardhat/types"

/**
* This is a dummy hardhat config that enables us to test
* hardhat functionality without mocking too much
*/
const config: HardhatUserConfig = {
networks: {},
}

export default config
50 changes: 50 additions & 0 deletions packages/utils-evm/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"name": "@layerzerolabs/utils-evm",
"description": "Utilities for LayerZero EVM projects",
"version": "0.0.2",
"license": "MIT",
"private": true,
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"module": "./dist/index.mjs",
"exports": {
"types": "./dist/index.d.ts",
"require": "./dist/index.js",
"import": "./dist/index.mjs"
},
"files": [
"./dist/index.*"
],
"scripts": {
"build": "npx tsup",
"clean": "rm -rf dist",
"dev": "npx tsup --watch",
"lint": "npx eslint '**/*.{js,ts,json}'",
"prebuild": "npx tsc --noEmit -p tsconfig.build.json",
"test": "npx hardhat test"
},
"repository": {
"type": "git",
"url": "git+https://github.com/LayerZero-Labs/lz-utils.git",
"directory": "packages/utils-evm"
},
"devDependencies": {
"@ethersproject/contracts": "5.7.0",
"@layerzerolabs/lz-definitions": "~1.5.58",
"@types/chai-as-promised": "^7.1.7",
"@types/mocha": "^10.0.1",
"@types/sinon": "^17.0.2",
"chai": "^4.3.10",
"chai-as-promised": "^7.1.1",
"hardhat": "^2.9.9",
"sinon": "^17.0.1",
"ts-node": "^10.9.1",
"tsup": "~7.2.0",
"typescript": "^5.2.2"
},
"peerDependencies": {
"@ethersproject/contracts": "5.7.0",
"@layerzerolabs/lz-definitions": "~1.5.58",
"hardhat": "^2.9.9"
}
}
77 changes: 77 additions & 0 deletions packages/utils-evm/src/configurable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import assert from "assert"

export type GetConfigurationValue<TContext extends unknown[], TValue = unknown> = (...context: TContext) => TValue | Promise<TValue>

export type SetConfigurationValue<TContext extends unknown[], TValue = unknown, TResult = unknown> = (
value: TValue,
...context: TContext
) => TResult | Promise<TResult>

/**
* Type encapsulating two states of a configurable property: Configured and Misconfigured
*
* Configurable property is understood as anything that has a getter and setter
* and its value needs to match a desired value (coming from some sort of a configuration).
*/
export type ConfigurationState<TValue = unknown, TResult = unknown> = Configured<TValue> | Misconfigured<TValue, TResult>

/**
* Interface for configured state of a configurable property.
*
* In configured state, the current value of the property matches its desired state
* and no action is necessary.
*/
export interface Configured<TValue = unknown> {
value: TValue
desiredValue?: never
configure?: never
}

/**
* Interface for misconfigured state of a configurable property.
*
* In misconfigured state, the current value of the property does not match its desired state
* and an action needs to be taken to synchronize these two.
*/
export interface Misconfigured<TValue = unknown, TResult = unknown> {
value: TValue
desiredValue: TValue
configure: () => TResult | Promise<TResult>
}

export const createConfigurable =
<TContext extends unknown[], TValue = unknown, TResult = unknown>(
getDesiredValue: GetConfigurationValue<TContext, TValue>,
getCurrentValue: GetConfigurationValue<TContext, TValue>,
setValue: SetConfigurationValue<TContext, TValue, TResult>
) =>
async (...context: TContext): Promise<ConfigurationState<TValue, TResult>> => {
const value = await getCurrentValue(...context)
const desiredValue = await getDesiredValue(...context)

try {
assert.deepStrictEqual(value, desiredValue)

return { value }
} catch {
return { value, desiredValue, configure: async () => setValue(desiredValue, ...context) }
}
}

/**
* Type assertion utility for narrowing the `ConfigurationState` type to `Misconfigured` type
*
* @param value `ConfigurationState<TValue, TResult>`
* @returns `value is Misconfigured<TValue, TResult>`
*/
export const isMisconfigured = <TValue = unknown, TResult = unknown>(
value: ConfigurationState<TValue, TResult>
): value is Misconfigured<TValue, TResult> => "configure" in value && "desiredValue" in value && typeof value.configure === "function"

/**
* Type assertion utility for narrowing the `ConfigurationState` type to `Configured` type
*
* @param value `ConfigurationState<TValue, TResult>`
* @returns `value is Configured<TValue, TResult>`
*/
export const isConfigured = <TValue = unknown>(value: ConfigurationState<TValue>): value is Configured<TValue> => !isMisconfigured(value)
14 changes: 14 additions & 0 deletions packages/utils-evm/src/oapp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Contract } from "@ethersproject/contracts"
import { GetConfigurationValue, createConfigurable } from "./configurable"
import { EndpointId } from "@layerzerolabs/lz-definitions"

type SetPeerConfigurableContext = [oapp: Contract, endpointId: EndpointId]

type SetPeerConfigurableValue = string

export const createSetPeerConfigurable = (getDesiredPeer: GetConfigurationValue<SetPeerConfigurableContext, SetPeerConfigurableValue>) =>
createConfigurable<SetPeerConfigurableContext, SetPeerConfigurableValue>(
getDesiredPeer,
(oapp, endpointId) => oapp.peers(endpointId),
(peer, oapp, endpointId) => oapp.setPeer(endpointId, peer)
)
72 changes: 72 additions & 0 deletions packages/utils-evm/test/configurable.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { expect } from "chai"
import { describe } from "mocha"
import sinon from "sinon"
import { createConfigurable, isConfigured, isMisconfigured } from "../src/configurable"

describe("configurable", () => {
describe("isMisconfigured", () => {
it("should return true if value is Misconfigured", () => {
expect(isMisconfigured({ value: false, desiredValue: true, configure: () => {} })).to.be.true
expect(isMisconfigured({ value: null, desiredValue: null, configure: () => {} })).to.be.true
expect(isMisconfigured({ value: 0, desiredValue: 0, configure: () => {} })).to.be.true
})

it("should return false if value is Configured", () => {
expect(isMisconfigured({ value: false })).to.be.false
expect(isMisconfigured({ value: true })).to.be.false
expect(isMisconfigured({ value: 1 })).to.be.false
})
})

describe("isConfigured", () => {
it("should return false if value is Configured", () => {
expect(isConfigured({ value: false, desiredValue: true, configure: () => {} })).to.be.false
expect(isConfigured({ value: null, desiredValue: null, configure: () => {} })).to.be.false
expect(isConfigured({ value: 0, desiredValue: 0, configure: () => {} })).to.be.false
})

it("should return true if value is Misconfigured", () => {
expect(isConfigured({ value: false })).to.be.true
expect(isConfigured({ value: true })).to.be.true
expect(isConfigured({ value: 1 })).to.be.true
})
})

describe("createConfigurable", () => {
it("should return Configured if the current and desired values match", async () => {
const configurable = createConfigurable(
() => [1, "two", { three: true }],
async () => [1, "two", { three: true }],
() => {}
)

expect(isMisconfigured(await configurable())).to.be.false
})

it("should return Misconfigured if the current and desired don't match", async () => {
const configurable = createConfigurable(
() => [1, "two", { three: true }],
async () => [1, "two", { three: false }],
() => {}
)

expect(isMisconfigured(await configurable())).to.be.true
})

it("should call the executable with desired value", async () => {
const setValue = sinon.spy()
const configurable = createConfigurable(
() => [1, "two", { three: true }],
async () => [1, "two", { three: false }],
setValue
)

const state = await configurable()
expect(isMisconfigured(state)).to.be.true

await state.configure?.()

expect(setValue.calledOnceWith([1, "two", { three: true }])).to.be.true
})
})
})
4 changes: 4 additions & 0 deletions packages/utils-evm/tsconfig.build.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "dist", "test"]
}
9 changes: 9 additions & 0 deletions packages/utils-evm/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.json",
"exclude": ["dist", "node_modules"],
"include": ["src", "test", "*.config.ts"],
"compilerOptions": {
"module": "commonjs",
"types": ["node", "mocha"]
}
}
12 changes: 12 additions & 0 deletions packages/utils-evm/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { defineConfig } from "tsup"

export default defineConfig({
entry: ["src/index.ts"],
outDir: "./dist",
clean: true,
dts: true,
sourcemap: true,
splitting: false,
treeshake: true,
format: ["esm", "cjs"],
})
12 changes: 12 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1622,6 +1622,18 @@
resolved "https://registry.npmjs.org/@types/semver/-/semver-7.5.5.tgz"
integrity sha512-+d+WYC1BxJ6yVOgUgzK8gWvp5qF8ssV5r4nsDcZWKRWcDQLQ619tvWAxJQYGgBrO1MnLJC7a5GtiYsAoQ47dJg==

"@types/sinon@^17.0.2":
version "17.0.2"
resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-17.0.2.tgz#9a769f67e62b45b7233f1fe01cb1f231d2393e1c"
integrity sha512-Zt6heIGsdqERkxctIpvN5Pv3edgBrhoeb3yHyxffd4InN0AX2SVNKSrhdDZKGQICVOxWP/q4DyhpfPNMSrpIiA==
dependencies:
"@types/sinonjs__fake-timers" "*"

"@types/sinonjs__fake-timers@*":
version "8.1.5"
resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.5.tgz#5fd3592ff10c1e9695d377020c033116cc2889f2"
integrity sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==

"@types/tinycolor2@*", "@types/tinycolor2@^1.4.0":
version "1.4.6"
resolved "https://registry.yarnpkg.com/@types/tinycolor2/-/tinycolor2-1.4.6.tgz#670cbc0caf4e58dd61d1e3a6f26386e473087f06"
Expand Down

0 comments on commit 1898c46

Please sign in to comment.