diff --git a/.changeset/brave-clouds-swim.md b/.changeset/brave-clouds-swim.md
new file mode 100644
index 0000000000..4485c0bc24
--- /dev/null
+++ b/.changeset/brave-clouds-swim.md
@@ -0,0 +1,7 @@
+---
+"@layerzerolabs/devtools": patch
+"@layerzerolabs/metadata-tools": patch
+"@layerzerolabs/ua-devtools": patch
+---
+
+Add support for Sui and Starknet chain types in peer address handling
diff --git a/packages/devtools-starknet/.eslintignore b/packages/devtools-starknet/.eslintignore
new file mode 100644
index 0000000000..de4d1f007d
--- /dev/null
+++ b/packages/devtools-starknet/.eslintignore
@@ -0,0 +1,2 @@
+dist
+node_modules
diff --git a/packages/devtools-starknet/.eslintrc.json b/packages/devtools-starknet/.eslintrc.json
new file mode 100644
index 0000000000..be97c53fbb
--- /dev/null
+++ b/packages/devtools-starknet/.eslintrc.json
@@ -0,0 +1,3 @@
+{
+ "extends": "../../.eslintrc.json"
+}
diff --git a/packages/devtools-starknet/.prettierignore b/packages/devtools-starknet/.prettierignore
new file mode 100644
index 0000000000..1eae0cf670
--- /dev/null
+++ b/packages/devtools-starknet/.prettierignore
@@ -0,0 +1,2 @@
+dist/
+node_modules/
diff --git a/packages/devtools-starknet/README.md b/packages/devtools-starknet/README.md
new file mode 100644
index 0000000000..b05cf14341
--- /dev/null
+++ b/packages/devtools-starknet/README.md
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+@layerzerolabs/devtools-starknet
+
+Utilities for working with LayerZero Starknet contracts.
diff --git a/packages/devtools-starknet/package.json b/packages/devtools-starknet/package.json
new file mode 100644
index 0000000000..6d6290130a
--- /dev/null
+++ b/packages/devtools-starknet/package.json
@@ -0,0 +1,66 @@
+{
+ "name": "@layerzerolabs/devtools-starknet",
+ "version": "0.1.0",
+ "description": "Developer utilities for working with LayerZero Starknet contracts",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/LayerZero-Labs/devtools.git",
+ "directory": "packages/devtools-starknet"
+ },
+ "license": "MIT",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "require": "./dist/index.js",
+ "import": "./dist/index.mjs"
+ },
+ "./*": {
+ "types": "./dist/*.d.ts",
+ "require": "./dist/*.js",
+ "import": "./dist/*.mjs"
+ }
+ },
+ "main": "./dist/index.js",
+ "module": "./dist/index.mjs",
+ "types": "./dist/index.d.ts",
+ "files": [
+ "./dist/index.*"
+ ],
+ "scripts": {
+ "prebuild": "tsc -noEmit",
+ "build": "$npm_execpath tsup --clean",
+ "clean": "rm -rf dist",
+ "dev": "$npm_execpath tsup --watch",
+ "lint": "$npm_execpath eslint '**/*.{js,ts,json}'",
+ "lint:fix": "eslint --fix '**/*.{js,ts,json}'",
+ "test": "jest --ci --passWithNoTests"
+ },
+ "dependencies": {
+ "p-memoize": "~4.0.4"
+ },
+ "devDependencies": {
+ "@layerzerolabs/devtools": "~2.0.4",
+ "@layerzerolabs/io-devtools": "~0.3.2",
+ "@layerzerolabs/lz-definitions": "^3.0.148",
+ "@layerzerolabs/lz-v2-utilities": "^3.0.148",
+ "@layerzerolabs/protocol-starknet-v2": "^0.2.19",
+ "@swc/core": "^1.4.0",
+ "@swc/jest": "^0.2.36",
+ "@types/jest": "^29.5.12",
+ "jest": "^29.7.0",
+ "starknet": "^8.9.0",
+ "ts-node": "^10.9.2",
+ "tslib": "~2.6.2",
+ "tsup": "~8.0.1",
+ "typescript": "^5.4.4"
+ },
+ "peerDependencies": {
+ "@layerzerolabs/devtools": "~2.0.4",
+ "@layerzerolabs/io-devtools": "~0.3.2",
+ "@layerzerolabs/lz-definitions": "^3.0.148",
+ "starknet": "^8.9.0"
+ },
+ "publishConfig": {
+ "access": "public"
+ }
+}
diff --git a/packages/devtools-starknet/src/common/addresses.ts b/packages/devtools-starknet/src/common/addresses.ts
new file mode 100644
index 0000000000..2beeee2845
--- /dev/null
+++ b/packages/devtools-starknet/src/common/addresses.ts
@@ -0,0 +1,9 @@
+const STARKNET_HEX_REGEX = /^0x[0-9a-fA-F]{1,64}$/
+
+export const isStarknetAddress = (value: string): boolean => STARKNET_HEX_REGEX.test(value)
+
+export const assertStarknetAddress = (value: string, label = 'address'): void => {
+ if (!isStarknetAddress(value)) {
+ throw new Error(`Invalid Starknet ${label}: ${value}`)
+ }
+}
diff --git a/packages/devtools-starknet/src/common/index.ts b/packages/devtools-starknet/src/common/index.ts
new file mode 100644
index 0000000000..897b610db2
--- /dev/null
+++ b/packages/devtools-starknet/src/common/index.ts
@@ -0,0 +1 @@
+export * from './addresses'
diff --git a/packages/devtools-starknet/src/connection/factory.ts b/packages/devtools-starknet/src/connection/factory.ts
new file mode 100644
index 0000000000..f99988bdac
--- /dev/null
+++ b/packages/devtools-starknet/src/connection/factory.ts
@@ -0,0 +1,22 @@
+import pMemoize from 'p-memoize'
+import { type RpcUrlFactory } from '@layerzerolabs/devtools'
+import type { EndpointId } from '@layerzerolabs/lz-definitions'
+import { RpcProvider } from 'starknet'
+import type { ConnectionFactory } from './types'
+
+export const defaultRpcUrlFactory: RpcUrlFactory = (eid) => {
+ throw new Error(
+ `No default Starknet RPC URL configured for eid ${eid}. Provide an override via createRpcUrlFactory().`
+ )
+}
+
+export const createRpcUrlFactory =
+ (overrides: Partial> = {}): RpcUrlFactory =>
+ (eid) =>
+ overrides[eid] ??
+ process.env.RPC_URL_STARKNET ??
+ process.env.RPC_URL_STARKNET_TESTNET ??
+ defaultRpcUrlFactory(eid)
+
+export const createConnectionFactory = (urlFactory: RpcUrlFactory = defaultRpcUrlFactory): ConnectionFactory =>
+ pMemoize(async (eid) => new RpcProvider({ nodeUrl: await urlFactory(eid) }))
diff --git a/packages/devtools-starknet/src/connection/index.ts b/packages/devtools-starknet/src/connection/index.ts
new file mode 100644
index 0000000000..97a7b59914
--- /dev/null
+++ b/packages/devtools-starknet/src/connection/index.ts
@@ -0,0 +1,2 @@
+export * from './factory'
+export * from './types'
diff --git a/packages/devtools-starknet/src/connection/types.ts b/packages/devtools-starknet/src/connection/types.ts
new file mode 100644
index 0000000000..4e19316a6c
--- /dev/null
+++ b/packages/devtools-starknet/src/connection/types.ts
@@ -0,0 +1,4 @@
+import type { RpcProvider } from 'starknet'
+import type { EndpointId } from '@layerzerolabs/lz-definitions'
+
+export type ConnectionFactory = (eid: EndpointId) => Promise
diff --git a/packages/devtools-starknet/src/index.ts b/packages/devtools-starknet/src/index.ts
new file mode 100644
index 0000000000..ded5780815
--- /dev/null
+++ b/packages/devtools-starknet/src/index.ts
@@ -0,0 +1,4 @@
+export * from './common'
+export * from './connection'
+export * from './omnigraph'
+export * from './transactions'
diff --git a/packages/devtools-starknet/src/omnigraph/coordinates.ts b/packages/devtools-starknet/src/omnigraph/coordinates.ts
new file mode 100644
index 0000000000..0742758b51
--- /dev/null
+++ b/packages/devtools-starknet/src/omnigraph/coordinates.ts
@@ -0,0 +1,10 @@
+import type { OmniPoint } from '@layerzerolabs/devtools'
+import { ChainType, endpointIdToChainType, type EndpointId } from '@layerzerolabs/lz-definitions'
+import { assertStarknetAddress } from '../common'
+
+export const createStarknetPoint = (eid: EndpointId, address: string): OmniPoint => {
+ assertStarknetAddress(address)
+ return { eid, address }
+}
+
+export const isOmniPointOnStarknet = ({ eid }: OmniPoint): boolean => endpointIdToChainType(eid) === ChainType.STARKNET
diff --git a/packages/devtools-starknet/src/omnigraph/index.ts b/packages/devtools-starknet/src/omnigraph/index.ts
new file mode 100644
index 0000000000..02b48ea20f
--- /dev/null
+++ b/packages/devtools-starknet/src/omnigraph/index.ts
@@ -0,0 +1,3 @@
+export * from './coordinates'
+export * from './sdk'
+export * from './types'
diff --git a/packages/devtools-starknet/src/omnigraph/sdk.ts b/packages/devtools-starknet/src/omnigraph/sdk.ts
new file mode 100644
index 0000000000..29ba4795a2
--- /dev/null
+++ b/packages/devtools-starknet/src/omnigraph/sdk.ts
@@ -0,0 +1,24 @@
+import type { Call, RpcProvider } from 'starknet'
+import { formatOmniPoint, OmniPoint, OmniTransaction } from '@layerzerolabs/devtools'
+import { createModuleLogger, type Logger } from '@layerzerolabs/io-devtools'
+import type { IOmniSDK } from './types'
+import { serializeStarknetCalls } from '../transactions'
+
+export class OmniSDK implements IOmniSDK {
+ constructor(
+ public readonly provider: RpcProvider,
+ public readonly point: OmniPoint,
+ protected readonly logger: Logger = createModuleLogger(`Starknet SDK @ ${formatOmniPoint(point)}`)
+ ) {}
+
+ get label(): string {
+ return `Starknet contract @ ${formatOmniPoint(this.point)}`
+ }
+
+ protected createTransaction(calls: Call[]): OmniTransaction {
+ return {
+ point: this.point,
+ data: serializeStarknetCalls(calls),
+ }
+ }
+}
diff --git a/packages/devtools-starknet/src/omnigraph/types.ts b/packages/devtools-starknet/src/omnigraph/types.ts
new file mode 100644
index 0000000000..4f75ffaa06
--- /dev/null
+++ b/packages/devtools-starknet/src/omnigraph/types.ts
@@ -0,0 +1,3 @@
+import type { IOmniSDK as IOmniSDKAbstract } from '@layerzerolabs/devtools'
+
+export interface IOmniSDK extends IOmniSDKAbstract {}
diff --git a/packages/devtools-starknet/src/transactions/index.ts b/packages/devtools-starknet/src/transactions/index.ts
new file mode 100644
index 0000000000..18aee9d5ff
--- /dev/null
+++ b/packages/devtools-starknet/src/transactions/index.ts
@@ -0,0 +1,2 @@
+export * from './serde'
+export * from './signer'
diff --git a/packages/devtools-starknet/src/transactions/serde.ts b/packages/devtools-starknet/src/transactions/serde.ts
new file mode 100644
index 0000000000..43933f5cc2
--- /dev/null
+++ b/packages/devtools-starknet/src/transactions/serde.ts
@@ -0,0 +1,6 @@
+import type { Call } from 'starknet'
+
+export const serializeStarknetCalls = (calls: Call[]): string =>
+ JSON.stringify(calls, (_key, value) => (typeof value === 'bigint' ? value.toString() : value))
+
+export const deserializeStarknetCalls = (data: string): Call[] => JSON.parse(data) as Call[]
diff --git a/packages/devtools-starknet/src/transactions/signer.ts b/packages/devtools-starknet/src/transactions/signer.ts
new file mode 100644
index 0000000000..cad0e74189
--- /dev/null
+++ b/packages/devtools-starknet/src/transactions/signer.ts
@@ -0,0 +1,59 @@
+import type { Account, Call, RpcProvider } from 'starknet'
+import {
+ OmniSigner,
+ OmniSignerBase,
+ type OmniSignerFactory,
+ type OmniTransaction,
+ type OmniTransactionReceipt,
+ type OmniTransactionResponse,
+ OmniPoint,
+} from '@layerzerolabs/devtools'
+import type { EndpointId } from '@layerzerolabs/lz-definitions'
+import { deserializeStarknetCalls } from './serde'
+import { createModuleLogger, type Logger } from '@layerzerolabs/io-devtools'
+
+export class OmniSignerStarknet extends OmniSignerBase implements OmniSigner {
+ constructor(
+ eid: EndpointId,
+ public readonly provider: RpcProvider,
+ public readonly account: Account,
+ protected readonly logger: Logger = createModuleLogger('OmniSignerStarknet')
+ ) {
+ super(eid)
+ }
+
+ getPoint(): OmniPoint {
+ return { eid: this.eid, address: this.account.address }
+ }
+
+ async sign(): Promise {
+ throw new Error('Starknet OmniSigner does not support offline signing. Use signAndSend instead.')
+ }
+
+ async signAndSend(transaction: OmniTransaction): Promise> {
+ this.assertTransaction(transaction)
+
+ const calls = deserializeStarknetCalls(transaction.data) as Call[]
+ const response = await this.account.execute(calls)
+ const transactionHash = response.transaction_hash
+
+ return {
+ transactionHash,
+ wait: async () => {
+ await this.provider.waitForTransaction(transactionHash)
+ return { transactionHash }
+ },
+ }
+ }
+}
+
+export type StarknetAccountFactory = (eid: EndpointId) => Promise
+export type StarknetProviderFactory = (eid: EndpointId) => Promise
+
+export const createStarknetSignerFactory =
+ (
+ accountFactory: StarknetAccountFactory,
+ providerFactory: StarknetProviderFactory
+ ): OmniSignerFactory>> =>
+ async (eid: EndpointId) =>
+ new OmniSignerStarknet(eid, await providerFactory(eid), await accountFactory(eid))
diff --git a/packages/devtools-starknet/tsconfig.json b/packages/devtools-starknet/tsconfig.json
new file mode 100644
index 0000000000..75eba9f992
--- /dev/null
+++ b/packages/devtools-starknet/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "rootDir": "src",
+ "outDir": "dist"
+ },
+ "include": ["src"]
+}
diff --git a/packages/devtools-starknet/tsup.config.ts b/packages/devtools-starknet/tsup.config.ts
new file mode 100644
index 0000000000..cbe50c7c92
--- /dev/null
+++ b/packages/devtools-starknet/tsup.config.ts
@@ -0,0 +1,9 @@
+import { defineConfig } from 'tsup'
+
+export default defineConfig({
+ entry: ['src/index.ts'],
+ format: ['cjs', 'esm'],
+ dts: true,
+ sourcemap: true,
+ clean: true,
+})
diff --git a/packages/devtools-sui/.eslintignore b/packages/devtools-sui/.eslintignore
new file mode 100644
index 0000000000..de4d1f007d
--- /dev/null
+++ b/packages/devtools-sui/.eslintignore
@@ -0,0 +1,2 @@
+dist
+node_modules
diff --git a/packages/devtools-sui/.eslintrc.json b/packages/devtools-sui/.eslintrc.json
new file mode 100644
index 0000000000..be97c53fbb
--- /dev/null
+++ b/packages/devtools-sui/.eslintrc.json
@@ -0,0 +1,3 @@
+{
+ "extends": "../../.eslintrc.json"
+}
diff --git a/packages/devtools-sui/.prettierignore b/packages/devtools-sui/.prettierignore
new file mode 100644
index 0000000000..1eae0cf670
--- /dev/null
+++ b/packages/devtools-sui/.prettierignore
@@ -0,0 +1,2 @@
+dist/
+node_modules/
diff --git a/packages/devtools-sui/README.md b/packages/devtools-sui/README.md
new file mode 100644
index 0000000000..ee7f79e6ed
--- /dev/null
+++ b/packages/devtools-sui/README.md
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+@layerzerolabs/devtools-sui
+
+Utilities for working with LayerZero Sui contracts.
diff --git a/packages/devtools-sui/package.json b/packages/devtools-sui/package.json
new file mode 100644
index 0000000000..b71a9b4e49
--- /dev/null
+++ b/packages/devtools-sui/package.json
@@ -0,0 +1,68 @@
+{
+ "name": "@layerzerolabs/devtools-sui",
+ "version": "0.1.0",
+ "description": "Developer utilities for working with LayerZero Sui contracts",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/LayerZero-Labs/devtools.git",
+ "directory": "packages/devtools-sui"
+ },
+ "license": "MIT",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "require": "./dist/index.js",
+ "import": "./dist/index.mjs"
+ },
+ "./*": {
+ "types": "./dist/*.d.ts",
+ "require": "./dist/*.js",
+ "import": "./dist/*.mjs"
+ }
+ },
+ "main": "./dist/index.js",
+ "module": "./dist/index.mjs",
+ "types": "./dist/index.d.ts",
+ "files": [
+ "./dist/index.*"
+ ],
+ "scripts": {
+ "prebuild": "tsc -noEmit",
+ "build": "$npm_execpath tsup --clean",
+ "clean": "rm -rf dist",
+ "dev": "$npm_execpath tsup --watch",
+ "lint": "$npm_execpath eslint '**/*.{js,ts,json}'",
+ "lint:fix": "eslint --fix '**/*.{js,ts,json}'",
+ "test": "jest --ci --passWithNoTests"
+ },
+ "dependencies": {
+ "p-memoize": "~4.0.4"
+ },
+ "devDependencies": {
+ "@layerzerolabs/devtools": "~2.0.4",
+ "@layerzerolabs/io-devtools": "~0.3.2",
+ "@layerzerolabs/lz-definitions": "^3.0.148",
+ "@layerzerolabs/lz-sui-oft-sdk-v2": "^3.0.156",
+ "@layerzerolabs/lz-sui-sdk-v2": "^3.0.156",
+ "@mysten/bcs": "^1.9.2",
+ "@mysten/sui": "^1.45.2",
+ "@swc/core": "^1.4.0",
+ "@swc/jest": "^0.2.36",
+ "@types/jest": "^29.5.12",
+ "jest": "^29.7.0",
+ "ts-node": "^10.9.2",
+ "tslib": "~2.6.2",
+ "tsup": "~8.0.1",
+ "typescript": "^5.4.4"
+ },
+ "peerDependencies": {
+ "@layerzerolabs/devtools": "~2.0.4",
+ "@layerzerolabs/io-devtools": "~0.3.2",
+ "@layerzerolabs/lz-definitions": "^3.0.148",
+ "@mysten/bcs": "^1.9.2",
+ "@mysten/sui": "^1.45.2"
+ },
+ "publishConfig": {
+ "access": "public"
+ }
+}
diff --git a/packages/devtools-sui/src/common/addresses.ts b/packages/devtools-sui/src/common/addresses.ts
new file mode 100644
index 0000000000..32a5bf72b4
--- /dev/null
+++ b/packages/devtools-sui/src/common/addresses.ts
@@ -0,0 +1,9 @@
+const SUI_HEX_REGEX = /^0x[0-9a-fA-F]{1,64}$/
+
+export const isSuiAddress = (value: string): boolean => SUI_HEX_REGEX.test(value)
+
+export const assertSuiAddress = (value: string, label = 'address'): void => {
+ if (!isSuiAddress(value)) {
+ throw new Error(`Invalid Sui ${label}: ${value}`)
+ }
+}
diff --git a/packages/devtools-sui/src/common/index.ts b/packages/devtools-sui/src/common/index.ts
new file mode 100644
index 0000000000..897b610db2
--- /dev/null
+++ b/packages/devtools-sui/src/common/index.ts
@@ -0,0 +1 @@
+export * from './addresses'
diff --git a/packages/devtools-sui/src/connection/factory.ts b/packages/devtools-sui/src/connection/factory.ts
new file mode 100644
index 0000000000..070aaa0bea
--- /dev/null
+++ b/packages/devtools-sui/src/connection/factory.ts
@@ -0,0 +1,17 @@
+import pMemoize from 'p-memoize'
+import { type RpcUrlFactory } from '@layerzerolabs/devtools'
+import type { EndpointId } from '@layerzerolabs/lz-definitions'
+import { SuiClient } from '@mysten/sui/client'
+import type { ConnectionFactory } from './types'
+
+export const defaultRpcUrlFactory: RpcUrlFactory = (eid) => {
+ throw new Error(`No default Sui RPC URL configured for eid ${eid}. Provide an override via createRpcUrlFactory().`)
+}
+
+export const createRpcUrlFactory =
+ (overrides: Partial> = {}): RpcUrlFactory =>
+ (eid) =>
+ overrides[eid] ?? process.env.RPC_URL_SUI ?? process.env.RPC_URL_SUI_TESTNET ?? defaultRpcUrlFactory(eid)
+
+export const createConnectionFactory = (urlFactory: RpcUrlFactory = defaultRpcUrlFactory): ConnectionFactory =>
+ pMemoize(async (eid) => new SuiClient({ url: await urlFactory(eid) }))
diff --git a/packages/devtools-sui/src/connection/index.ts b/packages/devtools-sui/src/connection/index.ts
new file mode 100644
index 0000000000..97a7b59914
--- /dev/null
+++ b/packages/devtools-sui/src/connection/index.ts
@@ -0,0 +1,2 @@
+export * from './factory'
+export * from './types'
diff --git a/packages/devtools-sui/src/connection/types.ts b/packages/devtools-sui/src/connection/types.ts
new file mode 100644
index 0000000000..fabebbfe3b
--- /dev/null
+++ b/packages/devtools-sui/src/connection/types.ts
@@ -0,0 +1,4 @@
+import type { SuiClient } from '@mysten/sui/client'
+import type { EndpointId } from '@layerzerolabs/lz-definitions'
+
+export type ConnectionFactory = (eid: EndpointId) => Promise
diff --git a/packages/devtools-sui/src/index.ts b/packages/devtools-sui/src/index.ts
new file mode 100644
index 0000000000..ded5780815
--- /dev/null
+++ b/packages/devtools-sui/src/index.ts
@@ -0,0 +1,4 @@
+export * from './common'
+export * from './connection'
+export * from './omnigraph'
+export * from './transactions'
diff --git a/packages/devtools-sui/src/omnigraph/coordinates.ts b/packages/devtools-sui/src/omnigraph/coordinates.ts
new file mode 100644
index 0000000000..cb72ea241c
--- /dev/null
+++ b/packages/devtools-sui/src/omnigraph/coordinates.ts
@@ -0,0 +1,10 @@
+import type { OmniPoint } from '@layerzerolabs/devtools'
+import { ChainType, endpointIdToChainType, type EndpointId } from '@layerzerolabs/lz-definitions'
+import { assertSuiAddress } from '../common'
+
+export const createSuiPoint = (eid: EndpointId, address: string): OmniPoint => {
+ assertSuiAddress(address)
+ return { eid, address }
+}
+
+export const isOmniPointOnSui = ({ eid }: OmniPoint): boolean => endpointIdToChainType(eid) === ChainType.SUI
diff --git a/packages/devtools-sui/src/omnigraph/index.ts b/packages/devtools-sui/src/omnigraph/index.ts
new file mode 100644
index 0000000000..02b48ea20f
--- /dev/null
+++ b/packages/devtools-sui/src/omnigraph/index.ts
@@ -0,0 +1,3 @@
+export * from './coordinates'
+export * from './sdk'
+export * from './types'
diff --git a/packages/devtools-sui/src/omnigraph/sdk.ts b/packages/devtools-sui/src/omnigraph/sdk.ts
new file mode 100644
index 0000000000..0ef6e9b82f
--- /dev/null
+++ b/packages/devtools-sui/src/omnigraph/sdk.ts
@@ -0,0 +1,29 @@
+import { SuiClient } from '@mysten/sui/client'
+import { Transaction } from '@mysten/sui/transactions'
+import { formatOmniPoint, OmniPoint, OmniTransaction } from '@layerzerolabs/devtools'
+import { createModuleLogger, type Logger } from '@layerzerolabs/io-devtools'
+import type { IOmniSDK } from './types'
+
+export class OmniSDK implements IOmniSDK {
+ constructor(
+ public readonly client: SuiClient,
+ public readonly point: OmniPoint,
+ protected readonly logger: Logger = createModuleLogger(`Sui SDK @ ${formatOmniPoint(point)}`)
+ ) {}
+
+ get label(): string {
+ return `Sui package @ ${formatOmniPoint(this.point)}`
+ }
+
+ protected async createTransaction(transaction: Transaction): Promise {
+ // Serialize the transaction using its JSON representation
+ // This preserves all transaction data without requiring a sender at build time
+ // The sender will be set during signing
+ const serialized = transaction.serialize()
+
+ return {
+ point: this.point,
+ data: serialized,
+ }
+ }
+}
diff --git a/packages/devtools-sui/src/omnigraph/types.ts b/packages/devtools-sui/src/omnigraph/types.ts
new file mode 100644
index 0000000000..4f75ffaa06
--- /dev/null
+++ b/packages/devtools-sui/src/omnigraph/types.ts
@@ -0,0 +1,3 @@
+import type { IOmniSDK as IOmniSDKAbstract } from '@layerzerolabs/devtools'
+
+export interface IOmniSDK extends IOmniSDKAbstract {}
diff --git a/packages/devtools-sui/src/transactions/index.ts b/packages/devtools-sui/src/transactions/index.ts
new file mode 100644
index 0000000000..18aee9d5ff
--- /dev/null
+++ b/packages/devtools-sui/src/transactions/index.ts
@@ -0,0 +1,2 @@
+export * from './serde'
+export * from './signer'
diff --git a/packages/devtools-sui/src/transactions/serde.ts b/packages/devtools-sui/src/transactions/serde.ts
new file mode 100644
index 0000000000..d42b0f6658
--- /dev/null
+++ b/packages/devtools-sui/src/transactions/serde.ts
@@ -0,0 +1,5 @@
+import { fromBase64, toBase64 } from '@mysten/bcs'
+
+export const serializeTransactionBytes = (bytes: Uint8Array): string => toBase64(bytes)
+
+export const deserializeTransactionBytes = (data: string): Uint8Array => fromBase64(data)
diff --git a/packages/devtools-sui/src/transactions/signer.ts b/packages/devtools-sui/src/transactions/signer.ts
new file mode 100644
index 0000000000..ec084360ad
--- /dev/null
+++ b/packages/devtools-sui/src/transactions/signer.ts
@@ -0,0 +1,70 @@
+import { Transaction } from '@mysten/sui/transactions'
+import { type Signer } from '@mysten/sui/cryptography'
+import type { SuiClient } from '@mysten/sui/client'
+import {
+ OmniSigner,
+ OmniSignerBase,
+ type OmniSignerFactory,
+ type OmniTransaction,
+ type OmniTransactionReceipt,
+ type OmniTransactionResponse,
+ OmniPoint,
+} from '@layerzerolabs/devtools'
+import type { EndpointId } from '@layerzerolabs/lz-definitions'
+import type { ConnectionFactory } from '../connection'
+import { createModuleLogger, type Logger } from '@layerzerolabs/io-devtools'
+
+export class OmniSignerSui extends OmniSignerBase implements OmniSigner {
+ constructor(
+ eid: EndpointId,
+ public readonly client: SuiClient,
+ public readonly signer: Signer,
+ protected readonly logger: Logger = createModuleLogger('OmniSignerSui')
+ ) {
+ super(eid)
+ }
+
+ getPoint(): OmniPoint {
+ return { eid: this.eid, address: this.signer.toSuiAddress() }
+ }
+
+ async sign(transaction: OmniTransaction): Promise {
+ this.assertTransaction(transaction)
+
+ const suiTransaction = Transaction.from(transaction.data)
+ suiTransaction.setSender(this.signer.toSuiAddress())
+ const bytes = await suiTransaction.build({ client: this.client })
+ const signature = await this.signer.signTransaction(bytes)
+
+ return signature.signature
+ }
+
+ async signAndSend(transaction: OmniTransaction): Promise> {
+ this.assertTransaction(transaction)
+
+ const suiTransaction = Transaction.from(transaction.data)
+ suiTransaction.setSender(this.signer.toSuiAddress())
+ const response = await this.signer.signAndExecuteTransaction({
+ transaction: suiTransaction,
+ client: this.client,
+ })
+
+ const digest = response.digest
+
+ return {
+ transactionHash: digest,
+ wait: async () => {
+ await this.client.waitForTransaction({ digest })
+ return { transactionHash: digest }
+ },
+ }
+ }
+}
+
+export const createSuiSignerFactory =
+ (
+ signer: Signer,
+ connectionFactory: ConnectionFactory
+ ): OmniSignerFactory>> =>
+ async (eid: EndpointId) =>
+ new OmniSignerSui(eid, await connectionFactory(eid), signer)
diff --git a/packages/devtools-sui/tsconfig.json b/packages/devtools-sui/tsconfig.json
new file mode 100644
index 0000000000..75eba9f992
--- /dev/null
+++ b/packages/devtools-sui/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "rootDir": "src",
+ "outDir": "dist"
+ },
+ "include": ["src"]
+}
diff --git a/packages/devtools-sui/tsup.config.ts b/packages/devtools-sui/tsup.config.ts
new file mode 100644
index 0000000000..cbe50c7c92
--- /dev/null
+++ b/packages/devtools-sui/tsup.config.ts
@@ -0,0 +1,9 @@
+import { defineConfig } from 'tsup'
+
+export default defineConfig({
+ entry: ['src/index.ts'],
+ format: ['cjs', 'esm'],
+ dts: true,
+ sourcemap: true,
+ clean: true,
+})
diff --git a/packages/devtools/src/common/bytes.ts b/packages/devtools/src/common/bytes.ts
index d890dd6c36..28646f22c7 100644
--- a/packages/devtools/src/common/bytes.ts
+++ b/packages/devtools/src/common/bytes.ts
@@ -100,6 +100,7 @@ export const normalizePeer = (address: OmniAddress | null | undefined, eid: Endp
return bs58.decode(address)
case ChainType.APTOS:
+ case ChainType.SUI:
case ChainType.EVM:
case ChainType.STARKNET:
return toBytes32(fromHex(address))
@@ -131,6 +132,7 @@ export const denormalizePeer = (bytes: Uint8Array | null | undefined, eid: Endpo
return bs58.encode(toBytes32(bytes))
case ChainType.APTOS:
+ case ChainType.SUI:
case ChainType.STARKNET:
return toHex(toBytes32(bytes))
@@ -194,10 +196,18 @@ export const toHex = (bytes: Uint8Array): string => `0x${Buffer.from(bytes).toSt
/**
* Helper utility to convert a hex string (with or without leading `0x`) to `UInt8Array`
*
+ * Note: Buffer.from(str, 'hex') silently truncates the last character if the string
+ * has odd length. We pad with a leading '0' to prevent data loss.
+ *
* @param {string} hex
* @returns {Uint8Array}
*/
-export const fromHex = (hex: string): Uint8Array => Uint8Array.from(Buffer.from(hex.replace(/^0x/, ''), 'hex'))
+export const fromHex = (hex: string): Uint8Array => {
+ const stripped = hex.replace(/^0x/, '')
+ // Pad with leading '0' if odd length to prevent Buffer.from truncation
+ const padded = stripped.length % 2 === 1 ? '0' + stripped : stripped
+ return Uint8Array.from(Buffer.from(padded, 'hex'))
+}
/**
* Helper utility that returns the leftmost bytes after removing the rightmost `length` bytes from a UInt8Array.
diff --git a/packages/metadata-tools/src/config-metadata.ts b/packages/metadata-tools/src/config-metadata.ts
index 6ad2550694..45a03787be 100644
--- a/packages/metadata-tools/src/config-metadata.ts
+++ b/packages/metadata-tools/src/config-metadata.ts
@@ -112,6 +112,23 @@ function isSolanaDeployment(deployment: { chainKey: string; executor?: { pda?: s
return deployment.chainKey.startsWith('solana')
}
+function isNonEvmDeployment(deployment: { chainKey: string }) {
+ return ['solana', 'sui', 'starknet', 'aptos', 'ton'].some((prefix) => deployment.chainKey.startsWith(prefix))
+}
+
+const maybeChecksumAddress = (address: string) => {
+ if (!address) {
+ return address
+ }
+ if (!address.startsWith('0x')) {
+ return address
+ }
+ if (address.length !== 42) {
+ return address
+ }
+ return getAddress(address)
+}
+
function resolveExecutorForDeployment(
customExecutor: string | undefined,
deployment: { chainKey: string; executor?: { pda?: string; address?: string } },
@@ -167,8 +184,13 @@ function isBlocked(blockConfirmationsDefinition: BlockConfirmationsDefinition |
)
}
-const getLibraryAddress = (deployment: any, metadataKey: string) =>
- isSolanaDeployment(deployment) ? deployment[metadataKey].address : getAddress(deployment[metadataKey].address)
+const getLibraryAddress = (deployment: any, metadataKey: string) => {
+ const address = deployment[metadataKey].address
+ if (isNonEvmDeployment(deployment)) {
+ return address
+ }
+ return maybeChecksumAddress(address)
+}
export async function translatePathwayToConfig(
pathway: TwoWayConfig,
diff --git a/packages/protocol-devtools-starknet/.eslintignore b/packages/protocol-devtools-starknet/.eslintignore
new file mode 100644
index 0000000000..de4d1f007d
--- /dev/null
+++ b/packages/protocol-devtools-starknet/.eslintignore
@@ -0,0 +1,2 @@
+dist
+node_modules
diff --git a/packages/protocol-devtools-starknet/.eslintrc.json b/packages/protocol-devtools-starknet/.eslintrc.json
new file mode 100644
index 0000000000..be97c53fbb
--- /dev/null
+++ b/packages/protocol-devtools-starknet/.eslintrc.json
@@ -0,0 +1,3 @@
+{
+ "extends": "../../.eslintrc.json"
+}
diff --git a/packages/protocol-devtools-starknet/.prettierignore b/packages/protocol-devtools-starknet/.prettierignore
new file mode 100644
index 0000000000..1eae0cf670
--- /dev/null
+++ b/packages/protocol-devtools-starknet/.prettierignore
@@ -0,0 +1,2 @@
+dist/
+node_modules/
diff --git a/packages/protocol-devtools-starknet/README.md b/packages/protocol-devtools-starknet/README.md
new file mode 100644
index 0000000000..6474cd79a7
--- /dev/null
+++ b/packages/protocol-devtools-starknet/README.md
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+@layerzerolabs/protocol-devtools-starknet
+
+
+
+
+
+
+
+
+
+
+
+Utilities for working with LayerZero Starknet protocol contracts.
diff --git a/packages/protocol-devtools-starknet/bin/test b/packages/protocol-devtools-starknet/bin/test
new file mode 100755
index 0000000000..cb0085aeda
--- /dev/null
+++ b/packages/protocol-devtools-starknet/bin/test
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+
+if [ -z "${LZ_DEVTOOLS_ENABLE_STARKNET_TESTS}" ]; then
+ echo 'Starknet tests can be enabled by setting LZ_DEVTOOLS_ENABLE_STARKNET_TESTS environment variable to a non-empty value'
+else
+ jest --ci "$@"
+fi
diff --git a/packages/protocol-devtools-starknet/jest.config.js b/packages/protocol-devtools-starknet/jest.config.js
new file mode 100644
index 0000000000..04c8477342
--- /dev/null
+++ b/packages/protocol-devtools-starknet/jest.config.js
@@ -0,0 +1,14 @@
+/** @type {import('ts-jest').JestConfigWithTsJest} */
+module.exports = {
+ cache: false,
+ reporters: [['github-actions', { silent: false }], 'default'],
+ testEnvironment: 'node',
+ testTimeout: 60_000,
+ setupFilesAfterEnv: ['/jest.setup.js'],
+ moduleNameMapper: {
+ '^@/(.*)$': '/src/$1',
+ },
+ transform: {
+ '^.+\\.(t|j)sx?$': '@swc/jest',
+ },
+};
diff --git a/packages/protocol-devtools-starknet/jest.setup.js b/packages/protocol-devtools-starknet/jest.setup.js
new file mode 100644
index 0000000000..956faa4d5c
--- /dev/null
+++ b/packages/protocol-devtools-starknet/jest.setup.js
@@ -0,0 +1,4 @@
+import * as jestExtended from 'jest-extended';
+
+// add all jest-extended matchers
+expect.extend(jestExtended);
diff --git a/packages/protocol-devtools-starknet/package.json b/packages/protocol-devtools-starknet/package.json
new file mode 100644
index 0000000000..f3ca137da7
--- /dev/null
+++ b/packages/protocol-devtools-starknet/package.json
@@ -0,0 +1,79 @@
+{
+ "name": "@layerzerolabs/protocol-devtools-starknet",
+ "version": "0.1.0",
+ "description": "Utilities for LayerZero Starknet protocol contracts",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/LayerZero-Labs/devtools.git",
+ "directory": "packages/protocol-devtools-starknet"
+ },
+ "license": "MIT",
+ "sideEffects": false,
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "require": "./dist/index.js",
+ "import": "./dist/index.mjs"
+ },
+ "./*": {
+ "types": "./dist/*.d.ts",
+ "require": "./dist/*.js",
+ "import": "./dist/*.mjs"
+ }
+ },
+ "main": "./dist/index.js",
+ "module": "./dist/index.mjs",
+ "types": "./dist/index.d.ts",
+ "files": [
+ "./dist/index.*"
+ ],
+ "scripts": {
+ "prebuild": "$npm_execpath tsc --noEmit",
+ "build": "$npm_execpath tsup",
+ "clean": "rm -rf dist",
+ "dev": "$npm_execpath tsup --watch",
+ "lint": "$npm_execpath eslint '**/*.{js,ts,json}'",
+ "lint:fix": "eslint --fix '**/*.{js,ts,json}'",
+ "test": "./bin/test"
+ },
+ "dependencies": {
+ "p-memoize": "~4.0.4"
+ },
+ "devDependencies": {
+ "@layerzerolabs/devtools": "~2.0.4",
+ "@layerzerolabs/devtools-starknet": "~0.1.0",
+ "@layerzerolabs/io-devtools": "~0.3.2",
+ "@layerzerolabs/lz-definitions": "^3.0.148",
+ "@layerzerolabs/lz-v2-utilities": "^3.0.148",
+ "@layerzerolabs/protocol-devtools": "~3.0.2",
+ "@layerzerolabs/protocol-starknet-v2": "^0.2.19",
+ "@layerzerolabs/test-devtools": "~0.4.7",
+ "@layerzerolabs/test-devtools-starknet": "~0.0.1",
+ "@swc/core": "^1.4.0",
+ "@swc/jest": "^0.2.36",
+ "@types/jest": "^29.5.12",
+ "fast-check": "^3.15.1",
+ "jest": "^29.7.0",
+ "jest-extended": "^4.0.2",
+ "starknet": "^8.9.0",
+ "ts-node": "^10.9.2",
+ "tslib": "~2.6.2",
+ "tsup": "~8.0.1",
+ "typescript": "^5.4.4",
+ "zod": "^3.22.4"
+ },
+ "peerDependencies": {
+ "@layerzerolabs/devtools": "~2.0.4",
+ "@layerzerolabs/devtools-starknet": "~0.1.0",
+ "@layerzerolabs/io-devtools": "~0.3.2",
+ "@layerzerolabs/lz-definitions": "^3.0.148",
+ "@layerzerolabs/lz-v2-utilities": "^3.0.148",
+ "@layerzerolabs/protocol-devtools": "~3.0.2",
+ "@layerzerolabs/protocol-starknet-v2": "^0.2.19",
+ "starknet": "^8.9.0",
+ "zod": "^3.22.4"
+ },
+ "publishConfig": {
+ "access": "public"
+ }
+}
diff --git a/packages/protocol-devtools-starknet/src/addresses.ts b/packages/protocol-devtools-starknet/src/addresses.ts
new file mode 100644
index 0000000000..1887a15db4
--- /dev/null
+++ b/packages/protocol-devtools-starknet/src/addresses.ts
@@ -0,0 +1,16 @@
+import { EndpointId } from '@layerzerolabs/lz-definitions'
+
+export const STARKNET_ENDPOINT_V2_ADDRESSES: Partial> = {
+ [EndpointId.STARKNET_V2_MAINNET]: '0x0524e065abff21d225fb7b28f26ec2f48314ace6094bc085f0a7cf1dc2660f68',
+ [EndpointId.STARKNET_V2_TESTNET]: '0x0316d70a6e0445a58c486215fac8ead48d3db985acde27efca9130da4c675878',
+}
+
+export const STARKNET_ULN_302_ADDRESSES: Partial> = {
+ [EndpointId.STARKNET_V2_MAINNET]: '0x0727f40349719ac76861a51a0b3d3e07be1577fff137bb81a5dc32e5a5c61d38',
+ [EndpointId.STARKNET_V2_TESTNET]: '0x0706572d6f7b938c813a20dc1b0328b83de939066e25bd0fbe14c270077f769d',
+}
+
+export const STARKNET_EXECUTOR_ADDRESSES: Partial> = {
+ [EndpointId.STARKNET_V2_MAINNET]: '0x03887bd8da2999d39e2e88fe55733c4cac8e20a6d51bfe162176c9f2eb134c65',
+ [EndpointId.STARKNET_V2_TESTNET]: '0x068ffdaca6533001344f377beaf1137360168604b227df3e8cf735fe06da47a9',
+}
diff --git a/packages/protocol-devtools-starknet/src/endpointv2/index.ts b/packages/protocol-devtools-starknet/src/endpointv2/index.ts
new file mode 100644
index 0000000000..7db67b18a1
--- /dev/null
+++ b/packages/protocol-devtools-starknet/src/endpointv2/index.ts
@@ -0,0 +1 @@
+export * from './sdk'
diff --git a/packages/protocol-devtools-starknet/src/endpointv2/sdk.ts b/packages/protocol-devtools-starknet/src/endpointv2/sdk.ts
new file mode 100644
index 0000000000..01744035ab
--- /dev/null
+++ b/packages/protocol-devtools-starknet/src/endpointv2/sdk.ts
@@ -0,0 +1,415 @@
+import type {
+ IEndpointV2,
+ IUlnRead,
+ MessageParams,
+ MessagingFee,
+ SetConfigParam,
+ Timeout,
+ Uln302ConfigType,
+ Uln302ExecutorConfig,
+ Uln302SetExecutorConfig,
+ Uln302SetUlnConfig,
+ Uln302UlnConfig,
+ Uln302UlnUserConfig,
+ UlnReadSetUlnConfig,
+ UlnReadUlnConfig,
+ UlnReadUlnUserConfig,
+} from '@layerzerolabs/protocol-devtools'
+import { type EndpointId } from '@layerzerolabs/lz-definitions'
+import {
+ formatEid,
+ type Bytes32,
+ type OmniAddress,
+ type OmniTransaction,
+ type PossiblyBytes,
+} from '@layerzerolabs/devtools'
+import { OmniSDK } from '@layerzerolabs/devtools-starknet'
+import type { Call, Contract } from 'starknet'
+import type { Uln302 } from '../uln302'
+import { STARKNET_ENDPOINT_V2_ADDRESSES } from '../addresses'
+import {
+ encodeExecutorConfig,
+ encodeUlnConfig,
+ getEndpointV2Contract,
+ getOAppContract,
+ MessageLibConfigType,
+} from '../protocol'
+
+const RECEIVE_ULN_CONFIG_TYPE = 3
+
+export class EndpointV2 extends OmniSDK implements IEndpointV2 {
+ private endpoint?: Contract
+
+ async getUln302SDK(address: OmniAddress): Promise {
+ this.logger.debug(`Getting Uln302 SDK for address ${address}`)
+ const { Uln302 } = await import('../uln302')
+ return new Uln302(this.provider, { eid: this.point.eid, address })
+ }
+
+ async getUlnReadSDK(_address: OmniAddress): Promise {
+ throw new Error('ULN Read functionality is not supported for Starknet.')
+ }
+
+ async getDelegate(_oapp: OmniAddress): Promise {
+ const oapp = await this.getOApp(_oapp)
+ if (!('get_delegate' in oapp)) {
+ return this.notImplemented('getDelegate')
+ }
+ const result = await (oapp as any).get_delegate()
+ return this.parseFelt(result)
+ }
+
+ async isDelegate(_oapp: OmniAddress, _delegate: OmniAddress): Promise {
+ const delegate = await this.getDelegate(_oapp)
+ return delegate === _delegate
+ }
+
+ async getDefaultReceiveLibrary(_eid: EndpointId): Promise {
+ const endpoint = await this.getEndpoint()
+ const result = await (endpoint as any).get_default_receive_library(_eid)
+ return this.parseFelt(result)
+ }
+
+ async setDefaultReceiveLibrary(
+ _eid: EndpointId,
+ _uln: OmniAddress | null | undefined,
+ _gracePeriod: bigint = 0n
+ ): Promise {
+ return this.notImplemented('setDefaultReceiveLibrary')
+ }
+
+ async getDefaultSendLibrary(_eid: EndpointId): Promise {
+ const endpoint = await this.getEndpoint()
+ const result = await (endpoint as any).get_default_send_library(_eid)
+ return this.parseFelt(result)
+ }
+
+ async setDefaultSendLibrary(_eid: EndpointId, _uln: OmniAddress | null | undefined): Promise {
+ return this.notImplemented('setDefaultSendLibrary')
+ }
+
+ async isRegisteredLibrary(_uln: OmniAddress): Promise {
+ return this.notImplemented('isRegisteredLibrary')
+ }
+
+ async registerLibrary(_uln: OmniAddress): Promise {
+ return this.notImplemented('registerLibrary')
+ }
+
+ async isBlockedLibrary(_uln: OmniAddress): Promise {
+ return this.notImplemented('isBlockedLibrary')
+ }
+
+ async getSendLibrary(_sender: OmniAddress, _dstEid: EndpointId): Promise {
+ const endpoint = await this.getEndpoint()
+ const result = await (endpoint as any).get_send_library(_sender, _dstEid)
+ return this.parseFelt(result?.lib ?? result)
+ }
+
+ async getReceiveLibrary(
+ _receiver: OmniAddress,
+ _srcEid: EndpointId
+ ): Promise<[address: Bytes32 | undefined, isDefault: boolean]> {
+ const endpoint = await this.getEndpoint()
+ const result = await (endpoint as any).get_receive_library(_receiver, _srcEid)
+ const address = this.parseFelt(result?.lib ?? result)
+ const isDefault = Boolean(result?.is_default)
+ return [address, isDefault]
+ }
+
+ async getDefaultReceiveLibraryTimeout(_eid: EndpointId): Promise {
+ return this.notImplemented('getDefaultReceiveLibraryTimeout')
+ }
+
+ async getReceiveLibraryTimeout(_receiver: OmniAddress, _srcEid: EndpointId): Promise {
+ return this.notImplemented('getReceiveLibraryTimeout')
+ }
+
+ async setSendLibrary(_oapp: OmniAddress, _eid: EndpointId, _uln: OmniAddress): Promise {
+ const endpoint = await this.getEndpoint()
+ const call = (endpoint as any).populateTransaction.set_send_library(_oapp, _eid, _uln)
+ return this.createTransactionWithDescription([call], `Setting send library for ${formatEid(_eid)} to ${_uln}`)
+ }
+
+ async isDefaultSendLibrary(_sender: PossiblyBytes, _dstEid: EndpointId): Promise {
+ // Get the send library for this sender and destination
+ const sendLib = await this.getSendLibrary(String(_sender), _dstEid)
+ // Get the default send library for this destination
+ const defaultLib = await this.getDefaultSendLibrary(_dstEid)
+ // If the send library matches the default, or if no specific send library is set, it's using the default
+ return sendLib === defaultLib || sendLib == null
+ }
+
+ async setReceiveLibrary(
+ _oapp: OmniAddress,
+ _eid: EndpointId,
+ _uln: OmniAddress,
+ _gracePeriod: bigint
+ ): Promise {
+ const endpoint = await this.getEndpoint()
+ const call = (endpoint as any).populateTransaction.set_receive_library(_oapp, _eid, _uln, _gracePeriod)
+ return this.createTransactionWithDescription(
+ [call],
+ `Setting receive library for ${formatEid(_eid)} to ${_uln}`
+ )
+ }
+
+ async setReceiveLibraryTimeout(
+ _oapp: OmniAddress,
+ _eid: EndpointId,
+ _uln: OmniAddress,
+ _expiry: bigint
+ ): Promise {
+ return this.notImplemented('setReceiveLibraryTimeout')
+ }
+
+ async getExecutorConfig(_oapp: PossiblyBytes, _uln: OmniAddress, _eid: EndpointId): Promise {
+ const ulnSdk = await this.getUln302SDK(_uln)
+ return ulnSdk.getExecutorConfig(_eid, String(_oapp))
+ }
+
+ async getAppExecutorConfig(
+ _oapp: PossiblyBytes,
+ _uln: OmniAddress,
+ _eid: EndpointId
+ ): Promise {
+ const ulnSdk = await this.getUln302SDK(_uln)
+ return ulnSdk.getAppExecutorConfig(_eid, String(_oapp))
+ }
+
+ async hasAppExecutorConfig(
+ _oapp: OmniAddress,
+ _uln: OmniAddress,
+ _eid: EndpointId,
+ _config: Uln302ExecutorConfig
+ ): Promise {
+ const ulnSdk = await this.getUln302SDK(_uln)
+ return ulnSdk.hasAppExecutorConfig(_eid, _oapp, _config)
+ }
+
+ async setExecutorConfig(
+ _oapp: PossiblyBytes,
+ _uln: PossiblyBytes,
+ _setExecutorConfig: Uln302SetExecutorConfig[]
+ ): Promise {
+ const endpoint = await this.getEndpoint()
+ return _setExecutorConfig.map(({ eid, executorConfig }) => {
+ const encoded = encodeExecutorConfig({
+ max_message_size: executorConfig.maxMessageSize,
+ executor: executorConfig.executor,
+ })
+ const call = (endpoint as any).populateTransaction.set_send_configs(String(_oapp), String(_uln), [
+ {
+ eid,
+ config_type: MessageLibConfigType.EXECUTOR,
+ config: encoded,
+ },
+ ])
+ return this.createTransactionWithDescription([call], `Setting executor config for ${formatEid(eid)}`)
+ })
+ }
+
+ async getUlnConfig(
+ _oapp: OmniAddress,
+ _uln: OmniAddress,
+ _eid: EndpointId,
+ _type: Uln302ConfigType
+ ): Promise {
+ const ulnSdk = await this.getUln302SDK(_uln)
+ return ulnSdk.getUlnConfig(_eid, _oapp, _type)
+ }
+
+ async getAppUlnConfig(
+ _oapp: OmniAddress,
+ _uln: OmniAddress,
+ _eid: EndpointId,
+ _type: Uln302ConfigType
+ ): Promise {
+ const ulnSdk = await this.getUln302SDK(_uln)
+ return ulnSdk.getAppUlnConfig(_eid, _oapp, _type)
+ }
+
+ async getAppUlnReadConfig(_oapp: OmniAddress, _uln: OmniAddress, _channelId: number): Promise {
+ throw new Error('ULN Read functionality is not supported for Starknet.')
+ }
+
+ async hasAppUlnConfig(
+ _oapp: OmniAddress,
+ _uln: OmniAddress,
+ _eid: EndpointId,
+ _config: Uln302UlnUserConfig,
+ _type: Uln302ConfigType
+ ): Promise {
+ const ulnSdk = await this.getUln302SDK(_uln)
+ return ulnSdk.hasAppUlnConfig(_eid, _oapp, _config, _type)
+ }
+
+ async hasAppUlnReadConfig(
+ _oapp: OmniAddress,
+ _uln: OmniAddress,
+ _channelId: number,
+ _config: UlnReadUlnUserConfig
+ ): Promise {
+ throw new Error('ULN Read functionality is not supported for Starknet.')
+ }
+
+ async setUlnConfig(
+ _oapp: OmniAddress,
+ _uln: OmniAddress,
+ _setUlnConfig: Uln302SetUlnConfig[]
+ ): Promise {
+ const endpoint = await this.getEndpoint()
+ return _setUlnConfig.map(({ eid, ulnConfig, type }) => {
+ const encoded = encodeUlnConfig({
+ confirmations: ulnConfig.confirmations ?? 0n,
+ required_dvns: ulnConfig.requiredDVNs,
+ optional_dvns: ulnConfig.optionalDVNs ?? [],
+ optional_dvn_threshold: ulnConfig.optionalDVNThreshold ?? 0,
+ })
+ const call =
+ type === 'send'
+ ? (endpoint as any).populateTransaction.set_send_configs(_oapp, _uln, [
+ {
+ eid,
+ config_type: MessageLibConfigType.ULN,
+ config: encoded,
+ },
+ ])
+ : (endpoint as any).populateTransaction.set_receive_configs(_oapp, _uln, [
+ {
+ eid,
+ config_type: MessageLibConfigType.ULN,
+ config: encoded,
+ },
+ ])
+ return this.createTransactionWithDescription([call], `Setting ${type} ULN config for ${formatEid(eid)}`)
+ })
+ }
+
+ async setUlnReadConfig(
+ _oapp: OmniAddress,
+ _uln: OmniAddress,
+ _setUlnConfig: UlnReadSetUlnConfig[]
+ ): Promise {
+ throw new Error('ULN Read functionality is not supported for Starknet.')
+ }
+
+ async getUlnConfigParams(_uln: OmniAddress, _setUlnConfig: Uln302SetUlnConfig[]): Promise {
+ return _setUlnConfig.map(({ eid, ulnConfig, type }) => {
+ return {
+ eid,
+ configType: type === 'send' ? MessageLibConfigType.ULN : RECEIVE_ULN_CONFIG_TYPE,
+ config: encodeUlnConfig({
+ confirmations: ulnConfig.confirmations ?? 0n,
+ required_dvns: ulnConfig.requiredDVNs,
+ optional_dvns: ulnConfig.optionalDVNs ?? [],
+ optional_dvn_threshold: ulnConfig.optionalDVNThreshold ?? 0,
+ }),
+ }
+ })
+ }
+
+ async getUlnReadConfigParams(_uln: OmniAddress, _setUlnConfig: UlnReadSetUlnConfig[]): Promise {
+ throw new Error('ULN Read functionality is not supported for Starknet.')
+ }
+
+ async getExecutorConfigParams(
+ _uln: OmniAddress,
+ _setExecutorConfig: Uln302SetExecutorConfig[]
+ ): Promise {
+ return _setExecutorConfig.map(({ eid, executorConfig }) => ({
+ eid,
+ configType: MessageLibConfigType.EXECUTOR,
+ config: encodeExecutorConfig({
+ max_message_size: executorConfig.maxMessageSize,
+ executor: executorConfig.executor,
+ }),
+ }))
+ }
+
+ async setConfig(
+ _oapp: OmniAddress,
+ _uln: OmniAddress,
+ _setConfigParam: SetConfigParam[]
+ ): Promise {
+ const endpoint = await this.getEndpoint()
+ return _setConfigParam.map(({ eid, configType, config }) => {
+ if (configType === RECEIVE_ULN_CONFIG_TYPE) {
+ const call = (endpoint as any).populateTransaction.set_receive_configs(_oapp, _uln, [
+ {
+ eid,
+ config_type: MessageLibConfigType.ULN,
+ config,
+ },
+ ])
+ return this.createTransactionWithDescription([call], `Setting receive ULN config for ${formatEid(eid)}`)
+ }
+
+ const call = (endpoint as any).populateTransaction.set_send_configs(_oapp, _uln, [
+ {
+ eid,
+ config_type: configType,
+ config,
+ },
+ ])
+ return this.createTransactionWithDescription(
+ [call],
+ `Setting send config ${configType} for ${formatEid(eid)}`
+ )
+ })
+ }
+
+ async quote(_params: MessageParams, _sender: OmniAddress): Promise {
+ return this.notImplemented('quote')
+ }
+
+ private notImplemented(method: string): never {
+ throw new TypeError(`${method}() not implemented on Starknet Endpoint SDK`)
+ }
+
+ private async getEndpoint(): Promise {
+ if (!this.endpoint) {
+ const endpointAddress = STARKNET_ENDPOINT_V2_ADDRESSES[this.point.eid]
+ if (!endpointAddress) {
+ throw new Error(`Missing Starknet EndpointV2 address for eid ${this.point.eid}`)
+ }
+ this.endpoint = getEndpointV2Contract(endpointAddress, this.provider)
+ }
+ return this.endpoint!
+ }
+
+ private async getOApp(address: OmniAddress): Promise {
+ return getOAppContract(address, this.provider)
+ }
+
+ protected override createTransaction(calls: Call[]): OmniTransaction {
+ return super.createTransaction(calls)
+ }
+
+ private createTransactionWithDescription(calls: Call[], description: string): OmniTransaction {
+ return { ...super.createTransaction(calls), description }
+ }
+
+ private parseFelt(value: unknown): string | undefined {
+ if (value == null) {
+ return undefined
+ }
+ let hexValue: string | undefined
+ if (typeof value === 'string') {
+ hexValue = value
+ } else if (typeof value === 'bigint') {
+ hexValue = `0x${value.toString(16)}`
+ } else if (typeof value === 'object' && value !== null && 'value' in value) {
+ const feltValue = (value as { value: bigint | string }).value
+ hexValue = typeof feltValue === 'bigint' ? `0x${feltValue.toString(16)}` : String(feltValue)
+ }
+ // Normalize Starknet felt252 addresses by removing leading zeros after 0x
+ // This ensures consistent comparison regardless of how addresses are formatted
+ // e.g., 0x0727f... and 0x727f... should be treated as the same address
+ if (hexValue) {
+ const normalized = hexValue.toLowerCase().replace(/^0x0*/, '0x')
+ return normalized === '0x' ? '0x0' : normalized
+ }
+ return undefined
+ }
+}
diff --git a/packages/protocol-devtools-starknet/src/index.ts b/packages/protocol-devtools-starknet/src/index.ts
new file mode 100644
index 0000000000..8571ea73f9
--- /dev/null
+++ b/packages/protocol-devtools-starknet/src/index.ts
@@ -0,0 +1,4 @@
+export * from './addresses'
+export * from './protocol'
+export * from './endpointv2'
+export * from './uln302'
diff --git a/packages/protocol-devtools-starknet/src/protocol.ts b/packages/protocol-devtools-starknet/src/protocol.ts
new file mode 100644
index 0000000000..9a088a2f3f
--- /dev/null
+++ b/packages/protocol-devtools-starknet/src/protocol.ts
@@ -0,0 +1,66 @@
+import { Contract } from 'starknet'
+import { abi } from '@layerzerolabs/protocol-starknet-v2'
+
+type StarknetProvider = unknown
+
+export enum MessageLibConfigType {
+ EXECUTOR = 1,
+ ULN = 2,
+}
+
+type ExecutorConfigInput = {
+ max_message_size?: number
+ maxMessageSize?: number
+ executor: string
+}
+
+type UlnConfigInput = {
+ confirmations?: bigint | number
+ required_dvns?: string[]
+ requiredDVNs?: string[]
+ optional_dvns?: string[]
+ optionalDVNs?: string[]
+ optional_dvn_threshold?: number
+ optionalDVNThreshold?: number
+}
+
+const toBoolFelt = (value: boolean) => (value ? 1 : 0)
+
+export const encodeExecutorConfig = (config: ExecutorConfigInput): (string | number | bigint)[] => {
+ const maxMessageSize = config.max_message_size ?? config.maxMessageSize ?? 0
+ return [maxMessageSize, config.executor]
+}
+
+export const encodeUlnConfig = (config: UlnConfigInput): (string | number | bigint)[] => {
+ const confirmations = BigInt(config.confirmations ?? 0)
+ const requiredDvns = config.required_dvns ?? config.requiredDVNs ?? []
+ const optionalDvns = config.optional_dvns ?? config.optionalDVNs ?? []
+ const optionalThreshold = config.optional_dvn_threshold ?? config.optionalDVNThreshold ?? 0
+ const hasConfirmations = confirmations !== 0n
+ const hasRequiredDvns = requiredDvns.length > 0
+ const hasOptionalDvns = optionalDvns.length > 0
+
+ return [
+ confirmations,
+ toBoolFelt(hasConfirmations),
+ requiredDvns.length,
+ ...requiredDvns,
+ toBoolFelt(hasRequiredDvns),
+ optionalDvns.length,
+ ...optionalDvns,
+ optionalThreshold,
+ toBoolFelt(hasOptionalDvns),
+ ]
+}
+
+const createContract = (contractAbi: unknown, address: string, provider: StarknetProvider) =>
+ new (Contract as any)({ abi: contractAbi, address, providerOrAccount: provider })
+
+export const getEndpointV2Contract = (address: string, provider: StarknetProvider) =>
+ createContract(abi.endpointV2, address, provider)
+
+export const getOAppContract = (address: string, provider: StarknetProvider) =>
+ createContract(abi.oApp, address, provider)
+
+export const getUltraLightNodeContractWithAddress = (address: string, provider: StarknetProvider) =>
+ createContract(abi.ultraLightNode302, address, provider)
diff --git a/packages/protocol-devtools-starknet/src/uln302/index.ts b/packages/protocol-devtools-starknet/src/uln302/index.ts
new file mode 100644
index 0000000000..7db67b18a1
--- /dev/null
+++ b/packages/protocol-devtools-starknet/src/uln302/index.ts
@@ -0,0 +1 @@
+export * from './sdk'
diff --git a/packages/protocol-devtools-starknet/src/uln302/sdk.ts b/packages/protocol-devtools-starknet/src/uln302/sdk.ts
new file mode 100644
index 0000000000..9fed972057
--- /dev/null
+++ b/packages/protocol-devtools-starknet/src/uln302/sdk.ts
@@ -0,0 +1,144 @@
+import type {
+ IUln302,
+ Uln302ConfigType,
+ Uln302ExecutorConfig,
+ Uln302UlnConfig,
+ Uln302UlnUserConfig,
+} from '@layerzerolabs/protocol-devtools'
+import type { EndpointId } from '@layerzerolabs/lz-definitions'
+import type { OmniAddress, OmniTransaction } from '@layerzerolabs/devtools'
+import { areBytes32Equal } from '@layerzerolabs/devtools'
+import { OmniSDK } from '@layerzerolabs/devtools-starknet'
+import type { Contract } from 'starknet'
+import { getUltraLightNodeContractWithAddress } from '../protocol'
+
+export class Uln302 extends OmniSDK implements IUln302 {
+ private uln?: Contract
+
+ async getUlnConfig(
+ _eid: EndpointId,
+ _address: OmniAddress | null | undefined,
+ _type: Uln302ConfigType
+ ): Promise {
+ if (!_address) {
+ return this.emptyUlnConfig()
+ }
+ return this.getAppUlnConfig(_eid, _address, _type)
+ }
+
+ async getAppUlnConfig(_eid: EndpointId, _address: OmniAddress, _type: Uln302ConfigType): Promise {
+ const uln = await this.getUln()
+ const config =
+ _type === 'send'
+ ? await (uln as any).get_raw_oapp_uln_send_config(_address, _eid)
+ : await (uln as any).get_raw_oapp_uln_receive_config(_address, _eid)
+ return {
+ confirmations: config.has_confirmations ? BigInt(config.confirmations) : 0n,
+ requiredDVNs: config.has_required_dvns ? this.normalizeAddressArray(config.required_dvns) : [],
+ requiredDVNCount: config.has_required_dvns ? config.required_dvns.length : 0,
+ optionalDVNs: config.has_optional_dvns ? this.normalizeAddressArray(config.optional_dvns) : [],
+ optionalDVNThreshold: config.has_optional_dvns ? Number(config.optional_dvn_threshold) : 0,
+ }
+ }
+
+ async hasAppUlnConfig(
+ _eid: EndpointId,
+ _oapp: OmniAddress,
+ _config: Uln302UlnUserConfig,
+ _type: Uln302ConfigType
+ ): Promise {
+ const current = await this.getAppUlnConfig(_eid, _oapp, _type)
+
+ const confirmationsMatch = current.confirmations === (_config.confirmations ?? current.confirmations)
+ const requiredDvnsMatch = this.equalAddressArrays(current.requiredDVNs, _config.requiredDVNs)
+ const optionalDvnsMatch = this.equalAddressArrays(current.optionalDVNs, _config.optionalDVNs ?? [])
+ const thresholdMatch = current.optionalDVNThreshold === (_config.optionalDVNThreshold ?? 0)
+
+ return confirmationsMatch && requiredDvnsMatch && optionalDvnsMatch && thresholdMatch
+ }
+
+ async setDefaultUlnConfig(_eid: EndpointId, _config: Uln302UlnUserConfig): Promise {
+ return this.notImplemented('setDefaultUlnConfig')
+ }
+
+ async getExecutorConfig(
+ _eid: EndpointId,
+ _address?: OmniAddress | null | undefined
+ ): Promise {
+ if (!_address) {
+ return { maxMessageSize: 0, executor: '0x0' }
+ }
+ return this.getAppExecutorConfig(_eid, _address)
+ }
+
+ async getAppExecutorConfig(_eid: EndpointId, _address: OmniAddress): Promise {
+ const uln = await this.getUln()
+ const config = await (uln as any).get_raw_oapp_executor_config(_address, _eid)
+ return {
+ maxMessageSize: Number(config.max_message_size ?? 0),
+ executor: this.normalizeAddress(config.executor) ?? '0x0',
+ }
+ }
+
+ async hasAppExecutorConfig(_eid: EndpointId, _oapp: OmniAddress, _config: Uln302ExecutorConfig): Promise {
+ const current = await this.getAppExecutorConfig(_eid, _oapp)
+ return current.maxMessageSize === _config.maxMessageSize && areBytes32Equal(current.executor, _config.executor)
+ }
+
+ async setDefaultExecutorConfig(_eid: EndpointId, _config: Uln302ExecutorConfig): Promise {
+ return this.notImplemented('setDefaultExecutorConfig')
+ }
+
+ private notImplemented(method: string): never {
+ throw new TypeError(`${method}() not implemented on Starknet ULN302 SDK`)
+ }
+
+ private async getUln(): Promise {
+ if (!this.uln) {
+ this.uln = await getUltraLightNodeContractWithAddress(this.point.address, this.provider)
+ }
+ return this.uln!
+ }
+
+ private emptyUlnConfig(): Uln302UlnConfig {
+ return {
+ confirmations: 0n,
+ requiredDVNs: [],
+ requiredDVNCount: 0,
+ optionalDVNs: [],
+ optionalDVNThreshold: 0,
+ }
+ }
+
+ private equalAddressArrays(left: string[], right: string[]): boolean {
+ if (left.length !== right.length) {
+ return false
+ }
+ // Use areBytes32Equal for address comparison to handle leading zero differences
+ const normalizedLeft = [...left].sort()
+ const normalizedRight = [...right].sort()
+ return normalizedLeft.every((value, index) => areBytes32Equal(value, normalizedRight[index]))
+ }
+
+ private normalizeAddress(value: unknown): string | undefined {
+ if (value == null) {
+ return undefined
+ }
+ let hexValue: string | undefined
+ if (typeof value === 'string') {
+ hexValue = value
+ } else if (typeof value === 'bigint') {
+ hexValue = `0x${value.toString(16)}`
+ }
+ // Normalize Starknet felt252 addresses by removing leading zeros after 0x
+ if (hexValue) {
+ const normalized = hexValue.toLowerCase().replace(/^0x0*/, '0x')
+ return normalized === '0x' ? '0x0' : normalized
+ }
+ return undefined
+ }
+
+ private normalizeAddressArray(addresses: unknown[]): string[] {
+ return addresses.map((addr) => this.normalizeAddress(addr) ?? '0x0')
+ }
+}
diff --git a/packages/protocol-devtools-starknet/tsconfig.json b/packages/protocol-devtools-starknet/tsconfig.json
new file mode 100644
index 0000000000..12774c873d
--- /dev/null
+++ b/packages/protocol-devtools-starknet/tsconfig.json
@@ -0,0 +1,13 @@
+{
+ "extends": "../../tsconfig.json",
+ "exclude": ["dist", "node_modules"],
+ "include": ["src", "test", "*.config.ts"],
+ "compilerOptions": {
+ "target": "es2020",
+ "experimentalDecorators": true,
+ "types": ["node", "jest"],
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ }
+}
diff --git a/packages/protocol-devtools-starknet/tsup.config.ts b/packages/protocol-devtools-starknet/tsup.config.ts
new file mode 100644
index 0000000000..7ef46a5ad1
--- /dev/null
+++ b/packages/protocol-devtools-starknet/tsup.config.ts
@@ -0,0 +1,14 @@
+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'],
+ },
+])
diff --git a/packages/protocol-devtools-sui/.eslintignore b/packages/protocol-devtools-sui/.eslintignore
new file mode 100644
index 0000000000..de4d1f007d
--- /dev/null
+++ b/packages/protocol-devtools-sui/.eslintignore
@@ -0,0 +1,2 @@
+dist
+node_modules
diff --git a/packages/protocol-devtools-sui/.eslintrc.json b/packages/protocol-devtools-sui/.eslintrc.json
new file mode 100644
index 0000000000..be97c53fbb
--- /dev/null
+++ b/packages/protocol-devtools-sui/.eslintrc.json
@@ -0,0 +1,3 @@
+{
+ "extends": "../../.eslintrc.json"
+}
diff --git a/packages/protocol-devtools-sui/.prettierignore b/packages/protocol-devtools-sui/.prettierignore
new file mode 100644
index 0000000000..1eae0cf670
--- /dev/null
+++ b/packages/protocol-devtools-sui/.prettierignore
@@ -0,0 +1,2 @@
+dist/
+node_modules/
diff --git a/packages/protocol-devtools-sui/README.md b/packages/protocol-devtools-sui/README.md
new file mode 100644
index 0000000000..f533d06957
--- /dev/null
+++ b/packages/protocol-devtools-sui/README.md
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+@layerzerolabs/protocol-devtools-sui
+
+
+
+
+
+
+
+
+
+
+
+Utilities for working with LayerZero Sui protocol packages.
diff --git a/packages/protocol-devtools-sui/bin/test b/packages/protocol-devtools-sui/bin/test
new file mode 100755
index 0000000000..2f9caec4e7
--- /dev/null
+++ b/packages/protocol-devtools-sui/bin/test
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+
+if [ -z "${LZ_DEVTOOLS_ENABLE_SUI_TESTS}" ]; then
+ echo 'Sui tests can be enabled by setting LZ_DEVTOOLS_ENABLE_SUI_TESTS environment variable to a non-empty value'
+else
+ jest --ci "$@"
+fi
diff --git a/packages/protocol-devtools-sui/jest.config.js b/packages/protocol-devtools-sui/jest.config.js
new file mode 100644
index 0000000000..04c8477342
--- /dev/null
+++ b/packages/protocol-devtools-sui/jest.config.js
@@ -0,0 +1,14 @@
+/** @type {import('ts-jest').JestConfigWithTsJest} */
+module.exports = {
+ cache: false,
+ reporters: [['github-actions', { silent: false }], 'default'],
+ testEnvironment: 'node',
+ testTimeout: 60_000,
+ setupFilesAfterEnv: ['/jest.setup.js'],
+ moduleNameMapper: {
+ '^@/(.*)$': '/src/$1',
+ },
+ transform: {
+ '^.+\\.(t|j)sx?$': '@swc/jest',
+ },
+};
diff --git a/packages/protocol-devtools-sui/jest.setup.js b/packages/protocol-devtools-sui/jest.setup.js
new file mode 100644
index 0000000000..956faa4d5c
--- /dev/null
+++ b/packages/protocol-devtools-sui/jest.setup.js
@@ -0,0 +1,4 @@
+import * as jestExtended from 'jest-extended';
+
+// add all jest-extended matchers
+expect.extend(jestExtended);
diff --git a/packages/protocol-devtools-sui/package.json b/packages/protocol-devtools-sui/package.json
new file mode 100644
index 0000000000..abc9f28fc3
--- /dev/null
+++ b/packages/protocol-devtools-sui/package.json
@@ -0,0 +1,79 @@
+{
+ "name": "@layerzerolabs/protocol-devtools-sui",
+ "version": "0.1.0",
+ "description": "Utilities for LayerZero Sui protocol packages",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/LayerZero-Labs/devtools.git",
+ "directory": "packages/protocol-devtools-sui"
+ },
+ "license": "MIT",
+ "sideEffects": false,
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "require": "./dist/index.js",
+ "import": "./dist/index.mjs"
+ },
+ "./*": {
+ "types": "./dist/*.d.ts",
+ "require": "./dist/*.js",
+ "import": "./dist/*.mjs"
+ }
+ },
+ "main": "./dist/index.js",
+ "module": "./dist/index.mjs",
+ "types": "./dist/index.d.ts",
+ "files": [
+ "./dist/index.*"
+ ],
+ "scripts": {
+ "prebuild": "$npm_execpath tsc --noEmit",
+ "build": "$npm_execpath tsup",
+ "clean": "rm -rf dist",
+ "dev": "$npm_execpath tsup --watch",
+ "lint": "$npm_execpath eslint '**/*.{js,ts,json}'",
+ "lint:fix": "eslint --fix '**/*.{js,ts,json}'",
+ "test": "./bin/test"
+ },
+ "dependencies": {
+ "p-memoize": "~4.0.4"
+ },
+ "devDependencies": {
+ "@layerzerolabs/devtools": "~2.0.4",
+ "@layerzerolabs/devtools-sui": "~0.1.0",
+ "@layerzerolabs/io-devtools": "~0.3.2",
+ "@layerzerolabs/lz-definitions": "^3.0.148",
+ "@layerzerolabs/lz-sui-sdk-v2": "^3.0.156",
+ "@layerzerolabs/lz-v2-utilities": "^3.0.148",
+ "@layerzerolabs/protocol-devtools": "~3.0.2",
+ "@layerzerolabs/test-devtools": "~0.4.7",
+ "@layerzerolabs/test-devtools-sui": "~0.0.1",
+ "@mysten/sui": "^1.45.2",
+ "@swc/core": "^1.4.0",
+ "@swc/jest": "^0.2.36",
+ "@types/jest": "^29.5.12",
+ "fast-check": "^3.15.1",
+ "jest": "^29.7.0",
+ "jest-extended": "^4.0.2",
+ "ts-node": "^10.9.2",
+ "tslib": "~2.6.2",
+ "tsup": "~8.0.1",
+ "typescript": "^5.4.4",
+ "zod": "^3.22.4"
+ },
+ "peerDependencies": {
+ "@layerzerolabs/devtools": "~2.0.4",
+ "@layerzerolabs/devtools-sui": "~0.1.0",
+ "@layerzerolabs/io-devtools": "~0.3.2",
+ "@layerzerolabs/lz-definitions": "^3.0.148",
+ "@layerzerolabs/lz-sui-sdk-v2": "^3.0.156",
+ "@layerzerolabs/lz-v2-utilities": "^3.0.148",
+ "@layerzerolabs/protocol-devtools": "~3.0.2",
+ "@mysten/sui": "^1.45.2",
+ "zod": "^3.22.4"
+ },
+ "publishConfig": {
+ "access": "public"
+ }
+}
diff --git a/packages/protocol-devtools-sui/src/addresses.ts b/packages/protocol-devtools-sui/src/addresses.ts
new file mode 100644
index 0000000000..0b4322ebf0
--- /dev/null
+++ b/packages/protocol-devtools-sui/src/addresses.ts
@@ -0,0 +1,16 @@
+import { EndpointId } from '@layerzerolabs/lz-definitions'
+
+export const SUI_ENDPOINT_V2_ADDRESSES: Partial> = {
+ [EndpointId.SUI_V2_MAINNET]: '0x31beaef889b08b9c3b37d19280fc1f8b75bae5b2de2410fc3120f403e9a36dac',
+ [EndpointId.SUI_V2_TESTNET]: '0xabf9629418d997fcc742a5ca22820241b72fb53691f010bc964eb49b4bd2263a',
+}
+
+export const SUI_ULN_302_ADDRESSES: Partial> = {
+ [EndpointId.SUI_V2_MAINNET]: '0x3ce7457bed48ad23ee5d611dd3172ae4fbd0a22ea0e846782a7af224d905dbb0',
+ [EndpointId.SUI_V2_TESTNET]: '0xf5d69c7b0922ce0ab4540525fbc66ca25ce9f092c64b032b91e4c5625ea0fb24',
+}
+
+export const SUI_EXECUTOR_ADDRESSES: Partial> = {
+ [EndpointId.SUI_V2_MAINNET]: '0xde7fe1a6648d587fcc991f124f3aa5b6389340610804108094d5c5fbf61d1989',
+ [EndpointId.SUI_V2_TESTNET]: '0xb9fdc6748fb939095e249b22717d564edf890681e387131d6c525d867d30f834',
+}
diff --git a/packages/protocol-devtools-sui/src/endpointv2/index.ts b/packages/protocol-devtools-sui/src/endpointv2/index.ts
new file mode 100644
index 0000000000..7db67b18a1
--- /dev/null
+++ b/packages/protocol-devtools-sui/src/endpointv2/index.ts
@@ -0,0 +1 @@
+export * from './sdk'
diff --git a/packages/protocol-devtools-sui/src/endpointv2/sdk.ts b/packages/protocol-devtools-sui/src/endpointv2/sdk.ts
new file mode 100644
index 0000000000..7212abbdb3
--- /dev/null
+++ b/packages/protocol-devtools-sui/src/endpointv2/sdk.ts
@@ -0,0 +1,383 @@
+import { Transaction } from '@mysten/sui/transactions'
+import type {
+ IEndpointV2,
+ IUlnRead,
+ MessageParams,
+ MessagingFee,
+ SetConfigParam,
+ Timeout,
+ Uln302ConfigType,
+ Uln302ExecutorConfig,
+ Uln302SetExecutorConfig,
+ Uln302SetUlnConfig,
+ Uln302UlnConfig,
+ Uln302UlnUserConfig,
+ UlnReadSetUlnConfig,
+ UlnReadUlnConfig,
+ UlnReadUlnUserConfig,
+} from '@layerzerolabs/protocol-devtools'
+import { endpointIdToStage, Stage, type EndpointId } from '@layerzerolabs/lz-definitions'
+import { type Bytes32, type OmniAddress, type OmniTransaction, type PossiblyBytes } from '@layerzerolabs/devtools'
+import { OmniSDK } from '@layerzerolabs/devtools-sui'
+import { CONFIG_TYPE, ExecutorConfigBcs, OAppUlnConfigBcs, SDK } from '@layerzerolabs/lz-sui-sdk-v2'
+import type { Endpoint, OApp, ExecutorConfig, OAppUlnConfig } from '@layerzerolabs/lz-sui-sdk-v2'
+import type { Uln302 } from '../uln302'
+
+export class EndpointV2 extends OmniSDK implements IEndpointV2 {
+ private sdk?: SDK
+ private oapp?: OApp
+ private endpoint?: Endpoint
+
+ async getUln302SDK(address: OmniAddress): Promise {
+ this.logger.debug(`Getting Uln302 SDK for address ${address}`)
+ const { Uln302 } = await import('../uln302')
+ return new Uln302(this.client, { eid: this.point.eid, address })
+ }
+
+ async getUlnReadSDK(_address: OmniAddress): Promise {
+ throw new Error('ULN Read functionality is not supported for Sui.')
+ }
+
+ async getDelegate(_oapp: OmniAddress): Promise {
+ return this.getEndpoint().getDelegate(_oapp)
+ }
+
+ async isDelegate(_oapp: OmniAddress, _delegate: OmniAddress): Promise {
+ const delegate = await this.getDelegate(_oapp)
+ return delegate === _delegate
+ }
+
+ async getDefaultReceiveLibrary(_eid: EndpointId): Promise {
+ return this.getEndpoint().getDefaultReceiveLibrary(_eid)
+ }
+
+ async setDefaultReceiveLibrary(
+ _eid: EndpointId,
+ _uln: OmniAddress | null | undefined,
+ _gracePeriod: bigint = 0n
+ ): Promise {
+ const tx = new Transaction()
+ this.getEndpoint().setDefaultReceiveLibraryMoveCall(tx, _eid, _uln ?? '0x0', _gracePeriod)
+ return this.createTransaction(tx)
+ }
+
+ async getDefaultSendLibrary(_eid: EndpointId): Promise {
+ return this.getEndpoint().getDefaultSendLibrary(_eid)
+ }
+
+ async setDefaultSendLibrary(_eid: EndpointId, _uln: OmniAddress | null | undefined): Promise {
+ const tx = new Transaction()
+ this.getEndpoint().setDefaultSendLibraryMoveCall(tx, _eid, _uln ?? '0x0')
+ return this.createTransaction(tx)
+ }
+
+ async isRegisteredLibrary(_uln: OmniAddress): Promise {
+ return this.notImplemented('isRegisteredLibrary')
+ }
+
+ async registerLibrary(_uln: OmniAddress): Promise {
+ return this.notImplemented('registerLibrary')
+ }
+
+ async isBlockedLibrary(_uln: OmniAddress): Promise {
+ return this.notImplemented('isBlockedLibrary')
+ }
+
+ async getSendLibrary(_sender: OmniAddress, _dstEid: EndpointId): Promise {
+ const [library] = await this.getEndpoint().getSendLibrary(_sender, _dstEid)
+ return library
+ }
+
+ async getReceiveLibrary(
+ _receiver: OmniAddress,
+ _srcEid: EndpointId
+ ): Promise<[address: Bytes32 | undefined, isDefault: boolean]> {
+ const [library, isDefault] = await this.getEndpoint().getReceiveLibrary(_receiver, _srcEid)
+ return [library, isDefault]
+ }
+
+ async getDefaultReceiveLibraryTimeout(_eid: EndpointId): Promise {
+ const timeout = await this.getEndpoint().getDefaultReceiveLibraryTimeout(_eid)
+ if (!timeout) {
+ return { lib: '0x0', expiry: 0n }
+ }
+ return { lib: timeout.fallbackLib, expiry: timeout.expiry }
+ }
+
+ async getReceiveLibraryTimeout(_receiver: OmniAddress, _srcEid: EndpointId): Promise {
+ const timeout = await this.getEndpoint().getReceiveLibraryTimeout(_receiver, _srcEid)
+ if (!timeout) {
+ return { lib: '0x0', expiry: 0n }
+ }
+ return { lib: timeout.fallbackLib, expiry: timeout.expiry }
+ }
+
+ async setSendLibrary(_oapp: OmniAddress, _eid: EndpointId, _uln: OmniAddress): Promise {
+ const tx = new Transaction()
+ await this.getOApp(_oapp).setSendLibraryMoveCall(tx, _eid, _uln)
+ return this.createTransaction(tx)
+ }
+
+ async isDefaultSendLibrary(_sender: PossiblyBytes, _dstEid: EndpointId): Promise {
+ const [_, isDefault] = await this.getEndpoint().getSendLibrary(String(_sender), _dstEid)
+ return isDefault
+ }
+
+ async setReceiveLibrary(
+ _oapp: OmniAddress,
+ _eid: EndpointId,
+ _uln: OmniAddress,
+ _gracePeriod: bigint
+ ): Promise {
+ const tx = new Transaction()
+ await this.getOApp(_oapp).setReceiveLibraryMoveCall(tx, _eid, _uln, _gracePeriod)
+ return this.createTransaction(tx)
+ }
+
+ async setReceiveLibraryTimeout(
+ _oapp: OmniAddress,
+ _eid: EndpointId,
+ _uln: OmniAddress,
+ _expiry: bigint
+ ): Promise {
+ const tx = new Transaction()
+ await this.getOApp(_oapp).setReceiveLibraryTimeoutMoveCall(tx, _eid, _uln, _expiry)
+ return this.createTransaction(tx)
+ }
+
+ async getExecutorConfig(_oapp: PossiblyBytes, _uln: OmniAddress, _eid: EndpointId): Promise {
+ const ulnSdk = await this.getUln302SDK(_uln)
+ return ulnSdk.getExecutorConfig(_eid, String(_oapp))
+ }
+
+ async getAppExecutorConfig(
+ _oapp: PossiblyBytes,
+ _uln: OmniAddress,
+ _eid: EndpointId
+ ): Promise {
+ const ulnSdk = await this.getUln302SDK(_uln)
+ return ulnSdk.getAppExecutorConfig(_eid, String(_oapp))
+ }
+
+ async hasAppExecutorConfig(
+ _oapp: OmniAddress,
+ _uln: OmniAddress,
+ _eid: EndpointId,
+ _config: Uln302ExecutorConfig
+ ): Promise {
+ const ulnSdk = await this.getUln302SDK(_uln)
+ return ulnSdk.hasAppExecutorConfig(_eid, _oapp, _config)
+ }
+
+ async setExecutorConfig(
+ _oapp: PossiblyBytes,
+ _uln: PossiblyBytes,
+ _setExecutorConfig: Uln302SetExecutorConfig[]
+ ): Promise {
+ return this.createConfigTransactions(String(_oapp), String(_uln), _setExecutorConfig, CONFIG_TYPE.EXECUTOR)
+ }
+
+ async getUlnConfig(
+ _oapp: OmniAddress,
+ _uln: OmniAddress,
+ _eid: EndpointId,
+ _type: Uln302ConfigType
+ ): Promise {
+ const ulnSdk = await this.getUln302SDK(_uln)
+ return ulnSdk.getUlnConfig(_eid, _oapp, _type)
+ }
+
+ async getAppUlnConfig(
+ _oapp: OmniAddress,
+ _uln: OmniAddress,
+ _eid: EndpointId,
+ _type: Uln302ConfigType
+ ): Promise {
+ const ulnSdk = await this.getUln302SDK(_uln)
+ return ulnSdk.getAppUlnConfig(_eid, _oapp, _type)
+ }
+
+ async getAppUlnReadConfig(_oapp: OmniAddress, _uln: OmniAddress, _channelId: number): Promise {
+ throw new Error('ULN Read functionality is not supported for Sui.')
+ }
+
+ async hasAppUlnConfig(
+ _oapp: OmniAddress,
+ _uln: OmniAddress,
+ _eid: EndpointId,
+ _config: Uln302UlnUserConfig,
+ _type: Uln302ConfigType
+ ): Promise {
+ const ulnSdk = await this.getUln302SDK(_uln)
+ return ulnSdk.hasAppUlnConfig(_eid, _oapp, _config, _type)
+ }
+
+ async hasAppUlnReadConfig(
+ _oapp: OmniAddress,
+ _uln: OmniAddress,
+ _channelId: number,
+ _config: UlnReadUlnUserConfig
+ ): Promise {
+ throw new Error('ULN Read functionality is not supported for Sui.')
+ }
+
+ async setUlnConfig(
+ _oapp: OmniAddress,
+ _uln: OmniAddress,
+ _setUlnConfig: Uln302SetUlnConfig[]
+ ): Promise {
+ return this.createConfigTransactions(_oapp, _uln, _setUlnConfig, CONFIG_TYPE.SEND_ULN)
+ }
+
+ async setUlnReadConfig(
+ _oapp: OmniAddress,
+ _uln: OmniAddress,
+ _setUlnConfig: UlnReadSetUlnConfig[]
+ ): Promise {
+ throw new Error('ULN Read functionality is not supported for Sui.')
+ }
+
+ async getUlnConfigParams(_uln: OmniAddress, _setUlnConfig: Uln302SetUlnConfig[]): Promise {
+ return _setUlnConfig.map(({ eid, ulnConfig, type }) => ({
+ eid,
+ configType: type === 'send' ? CONFIG_TYPE.SEND_ULN : CONFIG_TYPE.RECEIVE_ULN,
+ config: this.serializeUlnConfig(ulnConfig),
+ }))
+ }
+
+ async getUlnReadConfigParams(_uln: OmniAddress, _setUlnConfig: UlnReadSetUlnConfig[]): Promise {
+ throw new Error('ULN Read functionality is not supported for Sui.')
+ }
+
+ async getExecutorConfigParams(
+ _uln: OmniAddress,
+ _setExecutorConfig: Uln302SetExecutorConfig[]
+ ): Promise {
+ return _setExecutorConfig.map(({ eid, executorConfig }) => ({
+ eid,
+ configType: CONFIG_TYPE.EXECUTOR,
+ config: this.serializeExecutorConfig(executorConfig),
+ }))
+ }
+
+ async setConfig(
+ _oapp: OmniAddress,
+ _uln: OmniAddress,
+ _setConfigParam: SetConfigParam[]
+ ): Promise {
+ const txs: OmniTransaction[] = []
+ for (const param of _setConfigParam) {
+ const tx = new Transaction()
+ const setConfigCall = await this.getOApp(_oapp).setConfigMoveCall(
+ tx,
+ _uln,
+ param.eid,
+ param.configType,
+ param.config as Uint8Array
+ )
+ // The setConfigMoveCall returns a Call that must be
+ // populated using the endpoint's populateSetConfigTransaction to build the complete transaction
+ await this.getEndpoint().populateSetConfigTransaction(tx, setConfigCall)
+ txs.push(await this.createTransaction(tx))
+ }
+ return txs
+ }
+
+ async quote(_params: MessageParams, _sender: OmniAddress): Promise {
+ return this.notImplemented('quote')
+ }
+
+ private notImplemented(method: string): never {
+ throw new TypeError(`${method}() not implemented on Sui Endpoint SDK`)
+ }
+
+ private getSdk(): SDK {
+ if (!this.sdk) {
+ const stage = endpointIdToStage(this.point.eid) as Stage
+ this.sdk = new SDK({ client: this.client, stage })
+ }
+ return this.sdk
+ }
+
+ private getOApp(callCapId: OmniAddress = this.point.address): OApp {
+ if (callCapId === this.point.address) {
+ if (!this.oapp) {
+ this.oapp = this.getSdk().getOApp(callCapId)
+ }
+ return this.oapp
+ }
+ return this.getSdk().getOApp(callCapId)
+ }
+
+ private getEndpoint(): Endpoint {
+ if (!this.endpoint) {
+ this.endpoint = this.getSdk().getEndpoint()
+ }
+ return this.endpoint
+ }
+
+ private serializeExecutorConfig(config: Uln302ExecutorConfig): Uint8Array {
+ const executorConfig: ExecutorConfig = {
+ maxMessageSize: config.maxMessageSize,
+ executor: config.executor,
+ }
+ return ExecutorConfigBcs.serialize({
+ max_message_size: executorConfig.maxMessageSize,
+ executor: executorConfig.executor,
+ }).toBytes()
+ }
+
+ private serializeUlnConfig(config: Uln302UlnUserConfig): Uint8Array {
+ const useDefaultConfirmations = config.confirmations == null
+ const useDefaultRequiredDvns = config.requiredDVNs.length === 0
+ const useDefaultOptionalDvns = config.optionalDVNs == null
+ const ulnConfig: OAppUlnConfig = {
+ useDefaultConfirmations,
+ useDefaultRequiredDvns,
+ useDefaultOptionalDvns,
+ ulnConfig: {
+ confirmations: config.confirmations ?? 0n,
+ requiredDvns: config.requiredDVNs,
+ optionalDvns: config.optionalDVNs ?? [],
+ optionalDvnThreshold: config.optionalDVNThreshold ?? 0,
+ },
+ }
+ return OAppUlnConfigBcs.serialize({
+ use_default_confirmations: ulnConfig.useDefaultConfirmations,
+ use_default_required_dvns: ulnConfig.useDefaultRequiredDvns,
+ use_default_optional_dvns: ulnConfig.useDefaultOptionalDvns,
+ uln_config: {
+ confirmations: ulnConfig.ulnConfig.confirmations,
+ required_dvns: ulnConfig.ulnConfig.requiredDvns,
+ optional_dvns: ulnConfig.ulnConfig.optionalDvns,
+ optional_dvn_threshold: ulnConfig.ulnConfig.optionalDvnThreshold,
+ },
+ }).toBytes()
+ }
+
+ private async createConfigTransactions(
+ oapp: OmniAddress,
+ uln: OmniAddress,
+ configs: Uln302SetExecutorConfig[] | Uln302SetUlnConfig[],
+ configType: number
+ ): Promise {
+ const txs: OmniTransaction[] = []
+ for (const config of configs) {
+ const tx = new Transaction()
+ let setConfigCall
+ if ('executorConfig' in config) {
+ const bytes = this.serializeExecutorConfig(config.executorConfig)
+ setConfigCall = await this.getOApp(oapp).setConfigMoveCall(tx, uln, config.eid, configType, bytes)
+ } else {
+ const bytes = this.serializeUlnConfig(config.ulnConfig)
+ const type = config.type === 'send' ? CONFIG_TYPE.SEND_ULN : CONFIG_TYPE.RECEIVE_ULN
+ setConfigCall = await this.getOApp(oapp).setConfigMoveCall(tx, uln, config.eid, type, bytes)
+ }
+ // The setConfigMoveCall returns a Call that must be
+ // populated using the endpoint's populateSetConfigTransaction to build the complete transaction
+ await this.getEndpoint().populateSetConfigTransaction(tx, setConfigCall)
+ txs.push(await this.createTransaction(tx))
+ }
+ return txs
+ }
+}
diff --git a/packages/protocol-devtools-sui/src/index.ts b/packages/protocol-devtools-sui/src/index.ts
new file mode 100644
index 0000000000..5b9e0f3793
--- /dev/null
+++ b/packages/protocol-devtools-sui/src/index.ts
@@ -0,0 +1,3 @@
+export * from './addresses'
+export * from './endpointv2'
+export * from './uln302'
diff --git a/packages/protocol-devtools-sui/src/uln302/index.ts b/packages/protocol-devtools-sui/src/uln302/index.ts
new file mode 100644
index 0000000000..7db67b18a1
--- /dev/null
+++ b/packages/protocol-devtools-sui/src/uln302/index.ts
@@ -0,0 +1 @@
+export * from './sdk'
diff --git a/packages/protocol-devtools-sui/src/uln302/sdk.ts b/packages/protocol-devtools-sui/src/uln302/sdk.ts
new file mode 100644
index 0000000000..9c99836fa4
--- /dev/null
+++ b/packages/protocol-devtools-sui/src/uln302/sdk.ts
@@ -0,0 +1,193 @@
+import { Transaction } from '@mysten/sui/transactions'
+import type {
+ IUln302,
+ Uln302ConfigType,
+ Uln302ExecutorConfig,
+ Uln302UlnConfig,
+ Uln302UlnUserConfig,
+} from '@layerzerolabs/protocol-devtools'
+import { endpointIdToStage, Stage, type EndpointId } from '@layerzerolabs/lz-definitions'
+import type { OmniAddress, OmniTransaction } from '@layerzerolabs/devtools'
+import { OmniSDK } from '@layerzerolabs/devtools-sui'
+import { SDK } from '@layerzerolabs/lz-sui-sdk-v2'
+import type { ExecutorConfig, OAppUlnConfig, UlnConfig, Uln302 as SuiUln302 } from '@layerzerolabs/lz-sui-sdk-v2'
+
+export class Uln302 extends OmniSDK implements IUln302 {
+ private sdk?: SDK
+ private uln?: SuiUln302
+
+ async getUlnConfig(
+ _eid: EndpointId,
+ _address: OmniAddress | null | undefined,
+ _type: Uln302ConfigType
+ ): Promise {
+ if (_type === 'send') {
+ const config = _address
+ ? await this.getUln().getEffectiveSendUlnConfig(_address, _eid)
+ : await this.getUln().getDefaultSendUlnConfig(_eid)
+ return this.toUlnConfig(config)
+ }
+
+ const config = _address
+ ? await this.getUln().getEffectiveReceiveUlnConfig(_address, _eid)
+ : await this.getUln().getDefaultReceiveUlnConfig(_eid)
+ return this.toUlnConfig(config)
+ }
+
+ async getAppUlnConfig(_eid: EndpointId, _address: OmniAddress, _type: Uln302ConfigType): Promise {
+ try {
+ const config: OAppUlnConfig =
+ _type === 'send'
+ ? await this.getUln().getOAppSendUlnConfig(_address, _eid)
+ : await this.getUln().getOAppReceiveUlnConfig(_address, _eid)
+ return this.toUlnConfig(config.ulnConfig)
+ } catch (error) {
+ // If the config doesn't exist, return empty config
+ if (this.isMissingSuiConfig(error)) {
+ return this.toUlnConfig({
+ confirmations: 0n,
+ requiredDvns: [],
+ optionalDvns: [],
+ optionalDvnThreshold: 0,
+ })
+ }
+ throw error
+ }
+ }
+
+ async hasAppUlnConfig(
+ _eid: EndpointId,
+ _oapp: OmniAddress,
+ _config: Uln302UlnUserConfig,
+ _type: Uln302ConfigType
+ ): Promise {
+ const current = await this.getAppUlnConfig(_eid, _oapp, _type)
+ const required = {
+ confirmations: _config.confirmations ?? current.confirmations,
+ requiredDVNs: _config.requiredDVNs,
+ optionalDVNs: _config.optionalDVNs ?? [],
+ optionalDVNThreshold: _config.optionalDVNThreshold ?? 0,
+ }
+ return (
+ current.confirmations === required.confirmations &&
+ this.equalStringArrays(current.requiredDVNs, required.requiredDVNs) &&
+ this.equalStringArrays(current.optionalDVNs, required.optionalDVNs) &&
+ current.optionalDVNThreshold === required.optionalDVNThreshold
+ )
+ }
+
+ async setDefaultUlnConfig(_eid: EndpointId, _config: Uln302UlnUserConfig): Promise {
+ const tx = new Transaction()
+ const ulnConfig: UlnConfig = {
+ confirmations: _config.confirmations ?? 0n,
+ requiredDvns: _config.requiredDVNs,
+ optionalDvns: _config.optionalDVNs ?? [],
+ optionalDvnThreshold: _config.optionalDVNThreshold ?? 0,
+ }
+ this.getUln().setDefaultSendUlnConfigMoveCall(tx, _eid, ulnConfig)
+ this.getUln().setDefaultReceiveUlnConfigMoveCall(tx, _eid, ulnConfig)
+ return this.createTransaction(tx)
+ }
+
+ async getExecutorConfig(
+ _eid: EndpointId,
+ _address?: OmniAddress | null | undefined
+ ): Promise {
+ const config = _address
+ ? await this.getUln().getEffectiveExecutorConfig(_address, _eid)
+ : await this.getUln().getDefaultExecutorConfig(_eid)
+ return {
+ maxMessageSize: Number(config.maxMessageSize),
+ executor: config.executor,
+ }
+ }
+
+ async getAppExecutorConfig(_eid: EndpointId, _address: OmniAddress): Promise {
+ try {
+ const config = await this.getUln().getOAppExecutorConfig(_address, _eid)
+ return {
+ maxMessageSize: Number(config.maxMessageSize),
+ executor: config.executor,
+ }
+ } catch (error) {
+ // If the config doesn't exist, return empty config
+ if (this.isMissingSuiConfig(error)) {
+ return {
+ maxMessageSize: 0,
+ executor: '',
+ }
+ }
+ throw error
+ }
+ }
+
+ async hasAppExecutorConfig(_eid: EndpointId, _oapp: OmniAddress, _config: Uln302ExecutorConfig): Promise {
+ const current = await this.getAppExecutorConfig(_eid, _oapp)
+ return current.maxMessageSize === _config.maxMessageSize && current.executor === _config.executor
+ }
+
+ async setDefaultExecutorConfig(_eid: EndpointId, _config: Uln302ExecutorConfig): Promise {
+ const tx = new Transaction()
+ const executorConfig: ExecutorConfig = {
+ maxMessageSize: _config.maxMessageSize,
+ executor: _config.executor,
+ }
+ this.getUln().setDefaultExecutorConfigMoveCall(tx, _eid, executorConfig)
+ return this.createTransaction(tx)
+ }
+
+ private notImplemented(method: string): never {
+ throw new TypeError(`${method}() not implemented on Sui ULN302 SDK`)
+ }
+
+ private getSdk(): SDK {
+ if (!this.sdk) {
+ const stage = endpointIdToStage(this.point.eid) as Stage
+ this.sdk = new SDK({ client: this.client, stage })
+ }
+ return this.sdk
+ }
+
+ private getUln(): SuiUln302 {
+ if (!this.uln) {
+ this.uln = this.getSdk().getUln302()
+ }
+ return this.uln
+ }
+
+ private toUlnConfig(config: UlnConfig): Uln302UlnConfig {
+ return {
+ confirmations: BigInt(config.confirmations),
+ requiredDVNs: config.requiredDvns,
+ requiredDVNCount: config.requiredDvns.length,
+ optionalDVNs: config.optionalDvns,
+ optionalDVNThreshold: config.optionalDvnThreshold,
+ }
+ }
+
+ private equalStringArrays(left: string[], right: string[]): boolean {
+ if (left.length !== right.length) {
+ return false
+ }
+ const sortedLeft = [...left].sort()
+ const sortedRight = [...right].sort()
+ return sortedLeft.every((value, index) => value === sortedRight[index])
+ }
+
+ private isMissingSuiConfig(error: unknown): boolean {
+ const message =
+ typeof error === 'string'
+ ? error.toLowerCase()
+ : error && typeof error === 'object' && 'message' in error
+ ? String((error as { message?: unknown }).message).toLowerCase()
+ : ''
+ if (!message) {
+ return false
+ }
+ // Move abort errors indicate config doesn't exist
+ return (
+ message.includes('move abort') &&
+ (message.includes('send_uln') || message.includes('receive_uln') || message.includes('executor'))
+ )
+ }
+}
diff --git a/packages/protocol-devtools-sui/tsconfig.json b/packages/protocol-devtools-sui/tsconfig.json
new file mode 100644
index 0000000000..12774c873d
--- /dev/null
+++ b/packages/protocol-devtools-sui/tsconfig.json
@@ -0,0 +1,13 @@
+{
+ "extends": "../../tsconfig.json",
+ "exclude": ["dist", "node_modules"],
+ "include": ["src", "test", "*.config.ts"],
+ "compilerOptions": {
+ "target": "es2020",
+ "experimentalDecorators": true,
+ "types": ["node", "jest"],
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ }
+}
diff --git a/packages/protocol-devtools-sui/tsup.config.ts b/packages/protocol-devtools-sui/tsup.config.ts
new file mode 100644
index 0000000000..7ef46a5ad1
--- /dev/null
+++ b/packages/protocol-devtools-sui/tsup.config.ts
@@ -0,0 +1,14 @@
+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'],
+ },
+])
diff --git a/packages/ua-devtools-starknet/.eslintignore b/packages/ua-devtools-starknet/.eslintignore
new file mode 100644
index 0000000000..de4d1f007d
--- /dev/null
+++ b/packages/ua-devtools-starknet/.eslintignore
@@ -0,0 +1,2 @@
+dist
+node_modules
diff --git a/packages/ua-devtools-starknet/.eslintrc.json b/packages/ua-devtools-starknet/.eslintrc.json
new file mode 100644
index 0000000000..be97c53fbb
--- /dev/null
+++ b/packages/ua-devtools-starknet/.eslintrc.json
@@ -0,0 +1,3 @@
+{
+ "extends": "../../.eslintrc.json"
+}
diff --git a/packages/ua-devtools-starknet/.prettierignore b/packages/ua-devtools-starknet/.prettierignore
new file mode 100644
index 0000000000..1eae0cf670
--- /dev/null
+++ b/packages/ua-devtools-starknet/.prettierignore
@@ -0,0 +1,2 @@
+dist/
+node_modules/
diff --git a/packages/ua-devtools-starknet/README.md b/packages/ua-devtools-starknet/README.md
new file mode 100644
index 0000000000..ccaec12655
--- /dev/null
+++ b/packages/ua-devtools-starknet/README.md
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+@layerzerolabs/ua-devtools-starknet
+
+
+
+
+
+
+
+
+
+
+
+Utilities for working with LayerZero Starknet contracts.
diff --git a/packages/ua-devtools-starknet/bin/test b/packages/ua-devtools-starknet/bin/test
new file mode 100755
index 0000000000..cb0085aeda
--- /dev/null
+++ b/packages/ua-devtools-starknet/bin/test
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+
+if [ -z "${LZ_DEVTOOLS_ENABLE_STARKNET_TESTS}" ]; then
+ echo 'Starknet tests can be enabled by setting LZ_DEVTOOLS_ENABLE_STARKNET_TESTS environment variable to a non-empty value'
+else
+ jest --ci "$@"
+fi
diff --git a/packages/ua-devtools-starknet/jest.config.js b/packages/ua-devtools-starknet/jest.config.js
new file mode 100644
index 0000000000..04c8477342
--- /dev/null
+++ b/packages/ua-devtools-starknet/jest.config.js
@@ -0,0 +1,14 @@
+/** @type {import('ts-jest').JestConfigWithTsJest} */
+module.exports = {
+ cache: false,
+ reporters: [['github-actions', { silent: false }], 'default'],
+ testEnvironment: 'node',
+ testTimeout: 60_000,
+ setupFilesAfterEnv: ['/jest.setup.js'],
+ moduleNameMapper: {
+ '^@/(.*)$': '/src/$1',
+ },
+ transform: {
+ '^.+\\.(t|j)sx?$': '@swc/jest',
+ },
+};
diff --git a/packages/ua-devtools-starknet/jest.setup.js b/packages/ua-devtools-starknet/jest.setup.js
new file mode 100644
index 0000000000..956faa4d5c
--- /dev/null
+++ b/packages/ua-devtools-starknet/jest.setup.js
@@ -0,0 +1,4 @@
+import * as jestExtended from 'jest-extended';
+
+// add all jest-extended matchers
+expect.extend(jestExtended);
diff --git a/packages/ua-devtools-starknet/package.json b/packages/ua-devtools-starknet/package.json
new file mode 100644
index 0000000000..8cfa2d3fe9
--- /dev/null
+++ b/packages/ua-devtools-starknet/package.json
@@ -0,0 +1,83 @@
+{
+ "name": "@layerzerolabs/ua-devtools-starknet",
+ "version": "0.1.0",
+ "description": "Utilities for LayerZero Starknet projects",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/LayerZero-Labs/devtools.git",
+ "directory": "packages/ua-devtools-starknet"
+ },
+ "license": "MIT",
+ "sideEffects": false,
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "require": "./dist/index.js",
+ "import": "./dist/index.mjs"
+ },
+ "./*": {
+ "types": "./dist/*.d.ts",
+ "require": "./dist/*.js",
+ "import": "./dist/*.mjs"
+ }
+ },
+ "main": "./dist/index.js",
+ "module": "./dist/index.mjs",
+ "types": "./dist/index.d.ts",
+ "files": [
+ "./dist/index.*"
+ ],
+ "scripts": {
+ "prebuild": "$npm_execpath tsc --noEmit",
+ "build": "$npm_execpath tsup",
+ "clean": "rm -rf dist",
+ "dev": "$npm_execpath tsup --watch",
+ "lint": "$npm_execpath eslint '**/*.{js,ts,json}'",
+ "lint:fix": "eslint --fix '**/*.{js,ts,json}'",
+ "test": "./bin/test"
+ },
+ "dependencies": {
+ "p-memoize": "~4.0.4"
+ },
+ "devDependencies": {
+ "@layerzerolabs/devtools": "~2.0.4",
+ "@layerzerolabs/devtools-starknet": "~0.1.0",
+ "@layerzerolabs/io-devtools": "~0.3.2",
+ "@layerzerolabs/lz-definitions": "^3.0.148",
+ "@layerzerolabs/lz-v2-utilities": "^3.0.148",
+ "@layerzerolabs/oft-mint-burn-starknet": "^0.2.19",
+ "@layerzerolabs/protocol-devtools": "~3.0.2",
+ "@layerzerolabs/protocol-devtools-starknet": "~0.1.0",
+ "@layerzerolabs/protocol-starknet-v2": "^0.2.19",
+ "@layerzerolabs/test-devtools": "~0.4.7",
+ "@layerzerolabs/test-devtools-starknet": "~0.0.1",
+ "@layerzerolabs/ua-devtools": "~5.0.2",
+ "@swc/core": "^1.4.0",
+ "@swc/jest": "^0.2.36",
+ "@types/jest": "^29.5.12",
+ "fast-check": "^3.15.1",
+ "jest": "^29.7.0",
+ "jest-extended": "^4.0.2",
+ "starknet": "^8.9.0",
+ "ts-node": "^10.9.2",
+ "tslib": "~2.6.2",
+ "tsup": "~8.0.1",
+ "typescript": "^5.4.4"
+ },
+ "peerDependencies": {
+ "@layerzerolabs/devtools": "~2.0.4",
+ "@layerzerolabs/devtools-starknet": "~0.1.0",
+ "@layerzerolabs/io-devtools": "~0.3.2",
+ "@layerzerolabs/lz-definitions": "^3.0.148",
+ "@layerzerolabs/lz-v2-utilities": "^3.0.148",
+ "@layerzerolabs/oft-mint-burn-starknet": "^0.2.19",
+ "@layerzerolabs/protocol-devtools": "~3.0.2",
+ "@layerzerolabs/protocol-devtools-starknet": "~0.1.0",
+ "@layerzerolabs/protocol-starknet-v2": "^0.2.19",
+ "@layerzerolabs/ua-devtools": "~5.0.2",
+ "starknet": "^8.9.0"
+ },
+ "publishConfig": {
+ "access": "public"
+ }
+}
diff --git a/packages/ua-devtools-starknet/src/index.ts b/packages/ua-devtools-starknet/src/index.ts
new file mode 100644
index 0000000000..ab18876664
--- /dev/null
+++ b/packages/ua-devtools-starknet/src/index.ts
@@ -0,0 +1 @@
+export * from './oft'
diff --git a/packages/ua-devtools-starknet/src/oft/config.ts b/packages/ua-devtools-starknet/src/oft/config.ts
new file mode 100644
index 0000000000..a24f7146fe
--- /dev/null
+++ b/packages/ua-devtools-starknet/src/oft/config.ts
@@ -0,0 +1,44 @@
+import {
+ type OmniVector,
+ type CreateTransactionsFromOmniEdges,
+ formatOmniVector,
+ createConfigureEdges,
+ createConfigureMultiple,
+ OmniSDKFactory,
+ OmniPoint,
+} from '@layerzerolabs/devtools'
+import { createModuleLogger, createWithAsyncLogger } from '@layerzerolabs/io-devtools'
+import { isOmniPointOnStarknet } from '@layerzerolabs/devtools-starknet'
+import type { IOApp, OAppConfigurator, OAppOmniGraph } from '@layerzerolabs/ua-devtools'
+import { OFT } from './sdk'
+
+const createOFTLogger = () => createModuleLogger('OFT')
+const withOFTLogger = createWithAsyncLogger(createOFTLogger)
+
+const isVectorFromStarknet = (vector: OmniVector): boolean => isOmniPointOnStarknet(vector.from)
+
+const onlyEdgesFromStarknet = (
+ createTransactions: CreateTransactionsFromOmniEdges
+): CreateTransactionsFromOmniEdges => {
+ const logger = createOFTLogger()
+
+ return (edge, sdk, graph, createSdk) => {
+ if (!isVectorFromStarknet(edge.vector)) {
+ return logger.verbose(`Ignoring connection ${formatOmniVector(edge.vector)}`), undefined
+ }
+
+ return createTransactions(edge, sdk as OFT, graph, createSdk as OmniSDKFactory)
+ }
+}
+
+export const initConfig: OAppConfigurator = createConfigureEdges(
+ onlyEdgesFromStarknet(
+ withOFTLogger(async () => {
+ const logger = createOFTLogger()
+ logger.warn('Starknet OFT initConfig is not implemented yet')
+ return undefined
+ })
+ )
+)
+
+export const initOFTAccounts = createConfigureMultiple(initConfig)
diff --git a/packages/ua-devtools-starknet/src/oft/factory.ts b/packages/ua-devtools-starknet/src/oft/factory.ts
new file mode 100644
index 0000000000..373e2340ce
--- /dev/null
+++ b/packages/ua-devtools-starknet/src/oft/factory.ts
@@ -0,0 +1,15 @@
+import pMemoize from 'p-memoize'
+import type { OAppFactory } from '@layerzerolabs/ua-devtools'
+import { OFT } from './sdk'
+import { type ConnectionFactory, createConnectionFactory, defaultRpcUrlFactory } from '@layerzerolabs/devtools-starknet'
+
+/**
+ * Syntactic sugar that creates an instance of Starknet `OFT` SDK
+ * based on an `OmniPoint` with help of an `ConnectionFactory`.
+ *
+ * @param {ConnectionFactory} connectionFactory A function that returns a `RpcProvider` based on an `EndpointId`
+ * @returns {OAppFactory}
+ */
+export const createOFTFactory = (
+ connectionFactory: ConnectionFactory = createConnectionFactory(defaultRpcUrlFactory)
+): OAppFactory => pMemoize(async (point) => new OFT(await connectionFactory(point.eid), point))
diff --git a/packages/ua-devtools-starknet/src/oft/index.ts b/packages/ua-devtools-starknet/src/oft/index.ts
new file mode 100644
index 0000000000..444d96c1a3
--- /dev/null
+++ b/packages/ua-devtools-starknet/src/oft/index.ts
@@ -0,0 +1,3 @@
+export * from './config'
+export * from './factory'
+export * from './sdk'
diff --git a/packages/ua-devtools-starknet/src/oft/sdk.ts b/packages/ua-devtools-starknet/src/oft/sdk.ts
new file mode 100644
index 0000000000..5b5cd1f32f
--- /dev/null
+++ b/packages/ua-devtools-starknet/src/oft/sdk.ts
@@ -0,0 +1,318 @@
+import type { IOApp, OAppEnforcedOptionParam } from '@layerzerolabs/ua-devtools'
+import type { EndpointId } from '@layerzerolabs/lz-definitions'
+import { formatEid, areBytes32Equal, type Bytes, type OmniAddress, type OmniTransaction } from '@layerzerolabs/devtools'
+import { OmniSDK } from '@layerzerolabs/devtools-starknet'
+import { EndpointV2, STARKNET_ENDPOINT_V2_ADDRESSES } from '@layerzerolabs/protocol-devtools-starknet'
+import { Contract } from 'starknet'
+
+// OFT ABI from oft-mint-burn-starknet package - includes enforced options functions
+const getOftAbi = (): unknown[] => {
+ try {
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
+ const pkg = require('@layerzerolabs/oft-mint-burn-starknet')
+ return pkg.abi?.oFTMintBurnAdapter
+ } catch {
+ // Fallback to generic OApp ABI if OFT package not available
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
+ const protocol = require('@layerzerolabs/protocol-starknet-v2')
+ return protocol.abi?.oApp
+ }
+}
+
+const getOFTContract = (address: string, provider: unknown): Contract => {
+ const abi = getOftAbi()
+ return new (Contract as any)({ abi, address, providerOrAccount: provider })
+}
+
+const normalizeHex = (value: string): string => (value.startsWith('0x') ? value.slice(2) : value)
+
+const toFixedBytes = (value: unknown, size: number): Buffer => {
+ if (typeof value === 'string') {
+ const hex = normalizeHex(value).padStart(size * 2, '0')
+ return Buffer.from(hex.slice(-size * 2), 'hex')
+ }
+ if (typeof value === 'bigint') {
+ const hex = value.toString(16).padStart(size * 2, '0')
+ return Buffer.from(hex.slice(-size * 2), 'hex')
+ }
+ if (typeof value === 'number') {
+ return toFixedBytes(BigInt(value), size)
+ }
+ return Buffer.alloc(size)
+}
+
+/**
+ * Convert hex string to flat calldata for Cairo ByteArray.
+ *
+ * IMPORTANT: We cannot use starknet.js's string-based ByteArray encoding
+ * because it re-encodes strings as UTF-8, corrupting bytes >= 128.
+ * For example, byte 0x80 becomes 0xc2 0x80 in UTF-8.
+ *
+ * Instead, we return the flat calldata representation that matches Cairo's
+ * ByteArray serialization format:
+ * [data_len, ...data_words, pending_word, pending_word_len]
+ *
+ * This is used with Calldata.compile() to bypass starknet.js's ByteArray handling.
+ */
+const hexToByteArrayCalldata = (hex: string): string[] => {
+ const clean = normalizeHex(hex || '')
+ if (!clean) {
+ return ['0', '0x0', '0']
+ }
+
+ const buffer = Buffer.from(clean, 'hex')
+ const calldata: string[] = []
+
+ // Each data chunk is 31 bytes
+ const chunkSize = 31
+ let offset = 0
+ const dataWords: string[] = []
+
+ // Process full 31-byte chunks
+ while (offset + chunkSize <= buffer.length) {
+ const chunk = buffer.subarray(offset, offset + chunkSize)
+ dataWords.push('0x' + chunk.toString('hex'))
+ offset += chunkSize
+ }
+
+ // Add data array: length followed by elements
+ calldata.push(dataWords.length.toString())
+ calldata.push(...dataWords)
+
+ // Remaining bytes go into pending_word
+ const remaining = buffer.subarray(offset)
+ const pendingWord = remaining.length > 0 ? '0x' + remaining.toString('hex') : '0x0'
+ calldata.push(pendingWord)
+ calldata.push(remaining.length.toString())
+
+ return calldata
+}
+
+/**
+ * Convert hex string to a string for Cairo ByteArray.
+ * This is kept for backward compatibility but should only be used
+ * for data that doesn't contain bytes >= 128.
+ */
+const _toCairoByteArray = (hex: string): string => {
+ const clean = normalizeHex(hex || '')
+ if (!clean) {
+ return ''
+ }
+ const buffer = Buffer.from(clean, 'hex')
+ return buffer.toString('latin1')
+}
+
+const fromCairoByteArray = (value: unknown): string => {
+ if (value == null) {
+ return '0x'
+ }
+ if (typeof value === 'string') {
+ return value.startsWith('0x') ? value : `0x${value}`
+ }
+ if (value instanceof Uint8Array || Buffer.isBuffer(value)) {
+ return `0x${Buffer.from(value).toString('hex')}`
+ }
+ if (typeof value === 'object' && value !== null && 'data' in value && 'pending_word' in value) {
+ const data = (value as { data?: unknown[] }).data ?? []
+ const pendingWord = (value as { pending_word?: unknown }).pending_word
+ const pendingLen = Number((value as { pending_word_len?: unknown }).pending_word_len ?? 0)
+ const chunks = data.map((entry) => toFixedBytes(entry, 31))
+ const pending = pendingLen ? toFixedBytes(pendingWord, pendingLen) : Buffer.alloc(0)
+ return `0x${Buffer.concat([...chunks, pending]).toString('hex')}`
+ }
+ return '0x'
+}
+
+export class OFT extends OmniSDK implements IOApp {
+ private oapp?: Contract
+
+ async getEndpointSDK(): Promise {
+ const endpoint = STARKNET_ENDPOINT_V2_ADDRESSES[this.point.eid]
+ if (!endpoint) {
+ throw new Error(
+ `No Starknet EndpointV2 address configured for eid ${this.point.eid} (${formatEid(this.point.eid)})`
+ )
+ }
+ return new EndpointV2(this.provider, { eid: this.point.eid, address: endpoint })
+ }
+
+ async getOwner(): Promise {
+ return this.getDelegate()
+ }
+
+ async hasOwner(_address: OmniAddress): Promise {
+ const owner = await this.getOwner()
+ return owner === _address
+ }
+
+ async setOwner(_address: OmniAddress): Promise {
+ return this.setDelegate(_address)
+ }
+
+ async getPeer(_eid: EndpointId): Promise {
+ const oapp = await this.getOApp()
+ if (!('get_peer' in oapp)) {
+ return this.notImplemented('getPeer')
+ }
+ const result = await (oapp as any).get_peer(_eid)
+ return this.parseFelt(result?.value ?? result)
+ }
+
+ async hasPeer(_eid: EndpointId, _address: OmniAddress | null | undefined): Promise {
+ const peer = await this.getPeer(_eid)
+ // Both null/undefined = no peer set
+ if (peer == null && _address == null) {
+ return true
+ }
+ // One is null, other is not
+ if (peer == null || _address == null) {
+ return false
+ }
+ // Use areBytes32Equal for address comparison to handle leading zero differences
+ return areBytes32Equal(peer, _address)
+ }
+
+ async setPeer(_eid: EndpointId, _peer: OmniAddress | null | undefined): Promise {
+ const oapp = await this.getOApp()
+ const peerValue = _peer ? { value: BigInt(_peer) } : { value: 0n }
+ const call = (oapp as any).populateTransaction.set_peer(_eid, peerValue)
+ return {
+ ...this.createTransaction([call]),
+ description: `Setting peer for ${formatEid(_eid)} to ${_peer ?? '0x0'}`,
+ }
+ }
+
+ async getDelegate(): Promise {
+ const oapp = await this.getOApp()
+ if (!('get_delegate' in oapp)) {
+ return this.notImplemented('getDelegate')
+ }
+ const result = await (oapp as any).get_delegate()
+ return this.parseFelt(result)
+ }
+
+ async isDelegate(_address: OmniAddress): Promise {
+ const delegate = await this.getDelegate()
+ return delegate === _address
+ }
+
+ async setDelegate(_address: OmniAddress): Promise {
+ const oapp = await this.getOApp()
+ const call = (oapp as any).populateTransaction.set_delegate(_address)
+ return {
+ ...this.createTransaction([call]),
+ description: `Setting delegate to ${_address}`,
+ }
+ }
+
+ async getEnforcedOptions(_eid: EndpointId, _msgType: number): Promise {
+ try {
+ const oapp = await this.getOApp()
+ if (!('get_enforced_options' in oapp)) {
+ // Contract doesn't expose this function - return empty options
+ return '0x'
+ }
+ const result = await (oapp as any).get_enforced_options(_eid, _msgType)
+ return fromCairoByteArray(result)
+ } catch (error) {
+ // If the call fails (e.g., no enforced options set), return empty options
+ if (this.isMissingStarknetConfig(error)) {
+ return '0x'
+ }
+ throw error
+ }
+ }
+
+ private isMissingStarknetConfig(error: unknown): boolean {
+ const message =
+ typeof error === 'string'
+ ? error.toLowerCase()
+ : error && typeof error === 'object' && 'message' in error
+ ? String((error as { message?: unknown }).message).toLowerCase()
+ : ''
+ if (!message) {
+ return false
+ }
+ // Common patterns for missing config in Starknet
+ return message.includes('entry point') || message.includes('not found') || message.includes('contract error')
+ }
+
+ async setEnforcedOptions(_enforcedOptions: OAppEnforcedOptionParam[]): Promise {
+ const oapp = await this.getOApp()
+ // Try both function names - the contract may use either form
+ const funcName =
+ 'set_enforced_options' in oapp
+ ? 'set_enforced_options'
+ : 'setEnforcedOptions' in oapp
+ ? 'setEnforcedOptions'
+ : null
+ if (!funcName) {
+ // Contract doesn't support enforced options - return a no-op transaction
+ this.logger.warn(`Contract at ${this.point.address} does not support setEnforcedOptions - skipping`)
+ return this.createTransaction([])
+ }
+
+ // Build calldata manually to avoid UTF-8 corruption of ByteArray data
+ // The function signature is: set_enforced_options(params: Array<{eid: u32, msg_type: u8, options: ByteArray}>)
+ // Calldata format: [array_len, param1_eid, param1_msg_type, param1_options..., param2_eid, ...]
+ const calldata: string[] = []
+
+ // Array length
+ calldata.push(_enforcedOptions.length.toString())
+
+ // Each param: eid (u32), msg_type (u8), options (ByteArray)
+ for (const { eid, option } of _enforcedOptions) {
+ calldata.push(eid.toString())
+ calldata.push(option.msgType.toString())
+ // ByteArray is serialized as: [data_len, ...data_words, pending_word, pending_word_len]
+ calldata.push(...hexToByteArrayCalldata(option.options))
+ }
+
+ const call = {
+ contractAddress: this.point.address,
+ entrypoint: funcName,
+ calldata,
+ }
+
+ return {
+ ...this.createTransaction([call]),
+ description: `Setting enforced options for ${_enforcedOptions.length} pathway(s)`,
+ }
+ }
+
+ async getCallerBpsCap(): Promise {
+ return this.notImplemented('getCallerBpsCap')
+ }
+
+ async setCallerBpsCap(_callerBpsCap: bigint): Promise {
+ return this.notImplemented('setCallerBpsCap')
+ }
+
+ private notImplemented(method: string): never {
+ throw new TypeError(`${method}() not implemented on Starknet OFT SDK`)
+ }
+
+ private async getOApp(): Promise {
+ if (!this.oapp) {
+ this.oapp = getOFTContract(this.point.address, this.provider)
+ }
+ return this.oapp!
+ }
+
+ private parseFelt(value: unknown): string | undefined {
+ if (value == null) {
+ return undefined
+ }
+ if (typeof value === 'string') {
+ return value
+ }
+ if (typeof value === 'bigint') {
+ return `0x${value.toString(16)}`
+ }
+ if (typeof value === 'object' && value !== null && 'value' in value) {
+ const feltValue = (value as { value: bigint | string }).value
+ return typeof feltValue === 'bigint' ? `0x${feltValue.toString(16)}` : String(feltValue)
+ }
+ return undefined
+ }
+}
diff --git a/packages/ua-devtools-starknet/tsconfig.json b/packages/ua-devtools-starknet/tsconfig.json
new file mode 100644
index 0000000000..12774c873d
--- /dev/null
+++ b/packages/ua-devtools-starknet/tsconfig.json
@@ -0,0 +1,13 @@
+{
+ "extends": "../../tsconfig.json",
+ "exclude": ["dist", "node_modules"],
+ "include": ["src", "test", "*.config.ts"],
+ "compilerOptions": {
+ "target": "es2020",
+ "experimentalDecorators": true,
+ "types": ["node", "jest"],
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ }
+}
diff --git a/packages/ua-devtools-starknet/tsup.config.ts b/packages/ua-devtools-starknet/tsup.config.ts
new file mode 100644
index 0000000000..7ef46a5ad1
--- /dev/null
+++ b/packages/ua-devtools-starknet/tsup.config.ts
@@ -0,0 +1,14 @@
+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'],
+ },
+])
diff --git a/packages/ua-devtools-sui/.eslintignore b/packages/ua-devtools-sui/.eslintignore
new file mode 100644
index 0000000000..de4d1f007d
--- /dev/null
+++ b/packages/ua-devtools-sui/.eslintignore
@@ -0,0 +1,2 @@
+dist
+node_modules
diff --git a/packages/ua-devtools-sui/.eslintrc.json b/packages/ua-devtools-sui/.eslintrc.json
new file mode 100644
index 0000000000..be97c53fbb
--- /dev/null
+++ b/packages/ua-devtools-sui/.eslintrc.json
@@ -0,0 +1,3 @@
+{
+ "extends": "../../.eslintrc.json"
+}
diff --git a/packages/ua-devtools-sui/.prettierignore b/packages/ua-devtools-sui/.prettierignore
new file mode 100644
index 0000000000..1eae0cf670
--- /dev/null
+++ b/packages/ua-devtools-sui/.prettierignore
@@ -0,0 +1,2 @@
+dist/
+node_modules/
diff --git a/packages/ua-devtools-sui/README.md b/packages/ua-devtools-sui/README.md
new file mode 100644
index 0000000000..43cf28ef41
--- /dev/null
+++ b/packages/ua-devtools-sui/README.md
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+@layerzerolabs/ua-devtools-sui
+
+
+
+
+
+
+
+
+
+
+
+Utilities for working with LayerZero Sui contracts.
diff --git a/packages/ua-devtools-sui/bin/test b/packages/ua-devtools-sui/bin/test
new file mode 100755
index 0000000000..2f9caec4e7
--- /dev/null
+++ b/packages/ua-devtools-sui/bin/test
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+
+if [ -z "${LZ_DEVTOOLS_ENABLE_SUI_TESTS}" ]; then
+ echo 'Sui tests can be enabled by setting LZ_DEVTOOLS_ENABLE_SUI_TESTS environment variable to a non-empty value'
+else
+ jest --ci "$@"
+fi
diff --git a/packages/ua-devtools-sui/jest.config.js b/packages/ua-devtools-sui/jest.config.js
new file mode 100644
index 0000000000..04c8477342
--- /dev/null
+++ b/packages/ua-devtools-sui/jest.config.js
@@ -0,0 +1,14 @@
+/** @type {import('ts-jest').JestConfigWithTsJest} */
+module.exports = {
+ cache: false,
+ reporters: [['github-actions', { silent: false }], 'default'],
+ testEnvironment: 'node',
+ testTimeout: 60_000,
+ setupFilesAfterEnv: ['/jest.setup.js'],
+ moduleNameMapper: {
+ '^@/(.*)$': '/src/$1',
+ },
+ transform: {
+ '^.+\\.(t|j)sx?$': '@swc/jest',
+ },
+};
diff --git a/packages/ua-devtools-sui/jest.setup.js b/packages/ua-devtools-sui/jest.setup.js
new file mode 100644
index 0000000000..956faa4d5c
--- /dev/null
+++ b/packages/ua-devtools-sui/jest.setup.js
@@ -0,0 +1,4 @@
+import * as jestExtended from 'jest-extended';
+
+// add all jest-extended matchers
+expect.extend(jestExtended);
diff --git a/packages/ua-devtools-sui/package.json b/packages/ua-devtools-sui/package.json
new file mode 100644
index 0000000000..cdad9397d7
--- /dev/null
+++ b/packages/ua-devtools-sui/package.json
@@ -0,0 +1,83 @@
+{
+ "name": "@layerzerolabs/ua-devtools-sui",
+ "version": "0.1.0",
+ "description": "Utilities for LayerZero Sui projects",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/LayerZero-Labs/devtools.git",
+ "directory": "packages/ua-devtools-sui"
+ },
+ "license": "MIT",
+ "sideEffects": false,
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "require": "./dist/index.js",
+ "import": "./dist/index.mjs"
+ },
+ "./*": {
+ "types": "./dist/*.d.ts",
+ "require": "./dist/*.js",
+ "import": "./dist/*.mjs"
+ }
+ },
+ "main": "./dist/index.js",
+ "module": "./dist/index.mjs",
+ "types": "./dist/index.d.ts",
+ "files": [
+ "./dist/index.*"
+ ],
+ "scripts": {
+ "prebuild": "$npm_execpath tsc --noEmit",
+ "build": "$npm_execpath tsup",
+ "clean": "rm -rf dist",
+ "dev": "$npm_execpath tsup --watch",
+ "lint": "$npm_execpath eslint '**/*.{js,ts,json}'",
+ "lint:fix": "eslint --fix '**/*.{js,ts,json}'",
+ "test": "./bin/test"
+ },
+ "dependencies": {
+ "p-memoize": "~4.0.4"
+ },
+ "devDependencies": {
+ "@layerzerolabs/devtools": "~2.0.4",
+ "@layerzerolabs/devtools-sui": "~0.1.0",
+ "@layerzerolabs/io-devtools": "~0.3.2",
+ "@layerzerolabs/lz-definitions": "^3.0.148",
+ "@layerzerolabs/lz-sui-oft-sdk-v2": "^3.0.156",
+ "@layerzerolabs/lz-sui-sdk-v2": "^3.0.156",
+ "@layerzerolabs/lz-v2-utilities": "^3.0.148",
+ "@layerzerolabs/protocol-devtools": "~3.0.2",
+ "@layerzerolabs/protocol-devtools-sui": "~0.1.0",
+ "@layerzerolabs/test-devtools": "~0.4.7",
+ "@layerzerolabs/test-devtools-sui": "~0.0.1",
+ "@layerzerolabs/ua-devtools": "~5.0.2",
+ "@mysten/sui": "^1.45.2",
+ "@swc/core": "^1.4.0",
+ "@swc/jest": "^0.2.36",
+ "@types/jest": "^29.5.12",
+ "fast-check": "^3.15.1",
+ "jest": "^29.7.0",
+ "jest-extended": "^4.0.2",
+ "ts-node": "^10.9.2",
+ "tslib": "~2.6.2",
+ "tsup": "~8.0.1",
+ "typescript": "^5.4.4"
+ },
+ "peerDependencies": {
+ "@layerzerolabs/devtools": "~2.0.4",
+ "@layerzerolabs/devtools-sui": "~0.1.0",
+ "@layerzerolabs/io-devtools": "~0.3.2",
+ "@layerzerolabs/lz-definitions": "^3.0.148",
+ "@layerzerolabs/lz-sui-oft-sdk-v2": "^3.0.156",
+ "@layerzerolabs/lz-sui-sdk-v2": "^3.0.156",
+ "@layerzerolabs/lz-v2-utilities": "^3.0.148",
+ "@layerzerolabs/protocol-devtools": "~3.0.2",
+ "@layerzerolabs/protocol-devtools-sui": "~0.1.0",
+ "@layerzerolabs/ua-devtools": "~5.0.2",
+ "@mysten/sui": "^1.45.2"
+ },
+ "publishConfig": {
+ "access": "public"
+ }
+}
diff --git a/packages/ua-devtools-sui/src/index.ts b/packages/ua-devtools-sui/src/index.ts
new file mode 100644
index 0000000000..ab18876664
--- /dev/null
+++ b/packages/ua-devtools-sui/src/index.ts
@@ -0,0 +1 @@
+export * from './oft'
diff --git a/packages/ua-devtools-sui/src/oft/config.ts b/packages/ua-devtools-sui/src/oft/config.ts
new file mode 100644
index 0000000000..f05c46a4b8
--- /dev/null
+++ b/packages/ua-devtools-sui/src/oft/config.ts
@@ -0,0 +1,44 @@
+import {
+ type OmniVector,
+ type CreateTransactionsFromOmniEdges,
+ formatOmniVector,
+ createConfigureEdges,
+ createConfigureMultiple,
+ OmniSDKFactory,
+ OmniPoint,
+} from '@layerzerolabs/devtools'
+import { createModuleLogger, createWithAsyncLogger } from '@layerzerolabs/io-devtools'
+import { isOmniPointOnSui } from '@layerzerolabs/devtools-sui'
+import type { IOApp, OAppConfigurator, OAppOmniGraph } from '@layerzerolabs/ua-devtools'
+import { OFT } from './sdk'
+
+const createOFTLogger = () => createModuleLogger('OFT')
+const withOFTLogger = createWithAsyncLogger(createOFTLogger)
+
+const isVectorFromSui = (vector: OmniVector): boolean => isOmniPointOnSui(vector.from)
+
+const onlyEdgesFromSui = (
+ createTransactions: CreateTransactionsFromOmniEdges
+): CreateTransactionsFromOmniEdges => {
+ const logger = createOFTLogger()
+
+ return (edge, sdk, graph, createSdk) => {
+ if (!isVectorFromSui(edge.vector)) {
+ return logger.verbose(`Ignoring connection ${formatOmniVector(edge.vector)}`), undefined
+ }
+
+ return createTransactions(edge, sdk as OFT, graph, createSdk as OmniSDKFactory)
+ }
+}
+
+export const initConfig: OAppConfigurator = createConfigureEdges(
+ onlyEdgesFromSui(
+ withOFTLogger(async () => {
+ const logger = createOFTLogger()
+ logger.warn('Sui OFT initConfig is not implemented yet')
+ return undefined
+ })
+ )
+)
+
+export const initOFTAccounts = createConfigureMultiple(initConfig)
diff --git a/packages/ua-devtools-sui/src/oft/factory.ts b/packages/ua-devtools-sui/src/oft/factory.ts
new file mode 100644
index 0000000000..74cef1ed9c
--- /dev/null
+++ b/packages/ua-devtools-sui/src/oft/factory.ts
@@ -0,0 +1,15 @@
+import pMemoize from 'p-memoize'
+import type { OAppFactory } from '@layerzerolabs/ua-devtools'
+import { OFT } from './sdk'
+import { type ConnectionFactory, createConnectionFactory, defaultRpcUrlFactory } from '@layerzerolabs/devtools-sui'
+
+/**
+ * Syntactic sugar that creates an instance of Sui `OFT` SDK
+ * based on an `OmniPoint` with help of an `ConnectionFactory`.
+ *
+ * @param {ConnectionFactory} connectionFactory A function that returns a `SuiClient` based on an `EndpointId`
+ * @returns {OAppFactory}
+ */
+export const createOFTFactory = (
+ connectionFactory: ConnectionFactory = createConnectionFactory(defaultRpcUrlFactory)
+): OAppFactory => pMemoize(async (point) => new OFT(await connectionFactory(point.eid), point))
diff --git a/packages/ua-devtools-sui/src/oft/index.ts b/packages/ua-devtools-sui/src/oft/index.ts
new file mode 100644
index 0000000000..444d96c1a3
--- /dev/null
+++ b/packages/ua-devtools-sui/src/oft/index.ts
@@ -0,0 +1,3 @@
+export * from './config'
+export * from './factory'
+export * from './sdk'
diff --git a/packages/ua-devtools-sui/src/oft/sdk.ts b/packages/ua-devtools-sui/src/oft/sdk.ts
new file mode 100644
index 0000000000..d6969491fe
--- /dev/null
+++ b/packages/ua-devtools-sui/src/oft/sdk.ts
@@ -0,0 +1,182 @@
+import { Transaction } from '@mysten/sui/transactions'
+import type { IOApp, OAppEnforcedOptionParam } from '@layerzerolabs/ua-devtools'
+import { endpointIdToStage, Stage, type EndpointId } from '@layerzerolabs/lz-definitions'
+import {
+ areBytes32Equal,
+ formatEid,
+ fromHex,
+ isZero,
+ toHex,
+ type Bytes,
+ type OmniAddress,
+ type OmniTransaction,
+} from '@layerzerolabs/devtools'
+import { OmniSDK } from '@layerzerolabs/devtools-sui'
+import { EndpointV2, SUI_ENDPOINT_V2_ADDRESSES } from '@layerzerolabs/protocol-devtools-sui'
+import { SDK } from '@layerzerolabs/lz-sui-sdk-v2'
+import type { OApp, Endpoint } from '@layerzerolabs/lz-sui-sdk-v2'
+
+export class OFT extends OmniSDK implements IOApp {
+ private sdk?: SDK
+ private oapp?: OApp
+ private endpoint?: Endpoint
+
+ async getEndpointSDK(): Promise {
+ const endpoint = SUI_ENDPOINT_V2_ADDRESSES[this.point.eid]
+ if (!endpoint) {
+ throw new Error(
+ `No Sui EndpointV2 address configured for eid ${this.point.eid} (${formatEid(this.point.eid)})`
+ )
+ }
+ return new EndpointV2(this.client, { eid: this.point.eid, address: endpoint })
+ }
+
+ async getOwner(): Promise {
+ return this.getDelegate()
+ }
+
+ async hasOwner(_address: OmniAddress): Promise {
+ const owner = await this.getOwner()
+ return owner === _address
+ }
+
+ async setOwner(_address: OmniAddress): Promise {
+ return this.setDelegate(_address)
+ }
+
+ async getPeer(_eid: EndpointId): Promise {
+ try {
+ const peer = await this.getOApp().getPeer(_eid)
+ return isZero(peer) ? undefined : toHex(peer)
+ } catch (error) {
+ if (isMissingSuiPeer(error)) {
+ return undefined
+ }
+ throw error
+ }
+ }
+
+ async hasPeer(_eid: EndpointId, _address: OmniAddress | null | undefined): Promise {
+ const peer = await this.getPeer(_eid)
+ // Use areBytes32Equal for comparison since getPeer returns 32-byte padded addresses
+ // while _address may be a 20-byte EVM address
+ return areBytes32Equal(peer, _address)
+ }
+
+ async setPeer(_eid: EndpointId, _peer: OmniAddress | null | undefined): Promise {
+ const tx = new Transaction()
+ // Peer addresses must be 32 bytes (bytes32), so we need to pad shorter addresses to 32 bytes
+ let peerBytes: Uint8Array
+ if (_peer) {
+ const rawBytes = fromHex(_peer)
+ if (rawBytes.length === 32) {
+ peerBytes = rawBytes
+ } else if (rawBytes.length <= 32) {
+ // Pad shorter addresses (20-byte EVM, 31-byte Starknet, etc.) to 32 bytes with leading zeros
+ peerBytes = new Uint8Array(32)
+ peerBytes.set(rawBytes, 32 - rawBytes.length)
+ } else {
+ throw new Error(`Invalid peer address length: ${rawBytes.length}. Expected 32 bytes or less.`)
+ }
+ } else {
+ peerBytes = new Uint8Array(32)
+ }
+ await this.getOApp().setPeerMoveCall(tx, _eid, peerBytes)
+ return this.createTransaction(tx)
+ }
+
+ async getDelegate(): Promise {
+ try {
+ return this.getEndpoint().getDelegate(this.point.address)
+ } catch (error) {
+ if (isMissingSuiPeer(error)) {
+ return undefined
+ }
+ throw error
+ }
+ }
+
+ async isDelegate(_address: OmniAddress): Promise {
+ const delegate = await this.getDelegate()
+ return delegate === _address
+ }
+
+ async setDelegate(_address: OmniAddress): Promise {
+ const tx = new Transaction()
+ await this.getOApp().setDelegateMoveCall(tx, _address)
+ return this.createTransaction(tx)
+ }
+
+ async getEnforcedOptions(_eid: EndpointId, _msgType: number): Promise {
+ try {
+ const options = await this.getOApp().getEnforcedOptions(_eid, _msgType)
+ return toHex(options)
+ } catch (error) {
+ if (isMissingSuiPeer(error)) {
+ return '0x'
+ }
+ throw error
+ }
+ }
+
+ async setEnforcedOptions(_enforcedOptions: OAppEnforcedOptionParam[]): Promise {
+ const tx = new Transaction()
+ for (const { eid, option } of _enforcedOptions) {
+ await this.getOApp().setEnforcedOptionsMoveCall(tx, eid, option.msgType, fromHex(option.options))
+ }
+ return this.createTransaction(tx)
+ }
+
+ async getCallerBpsCap(): Promise {
+ return this.notImplemented('getCallerBpsCap')
+ }
+
+ async setCallerBpsCap(_callerBpsCap: bigint): Promise {
+ return this.notImplemented('setCallerBpsCap')
+ }
+
+ private notImplemented(method: string): never {
+ throw new TypeError(`${method}() not implemented on Sui OFT SDK`)
+ }
+
+ private getSdk(): SDK {
+ if (!this.sdk) {
+ const stage = endpointIdToStage(this.point.eid) as Stage
+ this.sdk = new SDK({ client: this.client, stage })
+ }
+ return this.sdk
+ }
+
+ private getOApp(): OApp {
+ if (!this.oapp) {
+ this.oapp = this.getSdk().getOApp(this.point.address)
+ }
+ return this.oapp
+ }
+
+ private getEndpoint(): Endpoint {
+ if (!this.endpoint) {
+ this.endpoint = this.getSdk().getEndpoint()
+ }
+ return this.endpoint
+ }
+}
+
+const isMissingSuiPeer = (error: unknown): boolean => {
+ const message =
+ typeof error === 'string'
+ ? error.toLowerCase()
+ : error && typeof error === 'object' && 'message' in error
+ ? String((error as { message?: unknown }).message).toLowerCase()
+ : ''
+ if (!message) {
+ return false
+ }
+ return (
+ message.includes('missing transaction sender') ||
+ (message.includes('move abort') &&
+ // Check for peer/enforced_options patterns in the error message
+ // The error format is: oapp_peer") ... function_name: Some("get_peer")
+ (message.includes('oapp_peer') || message.includes('enforced_options')))
+ )
+}
diff --git a/packages/ua-devtools-sui/tsconfig.json b/packages/ua-devtools-sui/tsconfig.json
new file mode 100644
index 0000000000..12774c873d
--- /dev/null
+++ b/packages/ua-devtools-sui/tsconfig.json
@@ -0,0 +1,13 @@
+{
+ "extends": "../../tsconfig.json",
+ "exclude": ["dist", "node_modules"],
+ "include": ["src", "test", "*.config.ts"],
+ "compilerOptions": {
+ "target": "es2020",
+ "experimentalDecorators": true,
+ "types": ["node", "jest"],
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ }
+}
diff --git a/packages/ua-devtools-sui/tsup.config.ts b/packages/ua-devtools-sui/tsup.config.ts
new file mode 100644
index 0000000000..7ef46a5ad1
--- /dev/null
+++ b/packages/ua-devtools-sui/tsup.config.ts
@@ -0,0 +1,14 @@
+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'],
+ },
+])
diff --git a/packages/ua-devtools/src/oapp/config.ts b/packages/ua-devtools/src/oapp/config.ts
index 43c1c8b5e2..8bc8366a34 100644
--- a/packages/ua-devtools/src/oapp/config.ts
+++ b/packages/ua-devtools/src/oapp/config.ts
@@ -9,6 +9,7 @@ import {
createConfigureMultiple,
createConfigureNodes,
createConfigureEdges,
+ areBytes32Equal,
} from '@layerzerolabs/devtools'
import type { OAppConfigurator, OAppEnforcedOption, OAppEnforcedOptionParam, OAppFactory } from './types'
import { createModuleLogger, createWithAsyncLogger, printBoolean } from '@layerzerolabs/io-devtools'
@@ -137,9 +138,13 @@ export const configureSendLibraries: OAppConfigurator = withOAppLogger(
)
}
- if (!isDefaultLibrary && currentSendLibrary?.toLowerCase() === config.sendLibrary.toLowerCase()) {
+ // Skip if the current library already matches the configured library
+ // This handles both cases:
+ // 1. Non-default library that matches config
+ // 2. Default library that matches config (avoids SAME_VALUE errors)
+ if (areBytes32Equal(currentSendLibrary, config.sendLibrary)) {
logger.verbose(
- `Current sendLibrary is not default library and is already set to ${config.sendLibrary} for ${formatOmniVector({ from, to })}, skipping`
+ `Current sendLibrary is already set to ${config.sendLibrary} for ${formatOmniVector({ from, to })}${isDefaultLibrary ? ' (default)' : ''}, skipping`
)
return []
}
@@ -212,9 +217,13 @@ export const configureReceiveLibraries: OAppConfigurator = withOAppLogger(
)
}
- if (!isDefaultLibrary && currentReceiveLibrary === config.receiveLibraryConfig.receiveLibrary) {
+ // Skip if the current library already matches the configured library
+ // This handles both cases:
+ // 1. Non-default library that matches config
+ // 2. Default library that matches config (avoids SAME_VALUE errors)
+ if (areBytes32Equal(currentReceiveLibrary, config.receiveLibraryConfig.receiveLibrary)) {
logger.verbose(
- `Current recieveLibrary is not default and is already set to ${config.receiveLibraryConfig.receiveLibrary} for ${formatOmniVector({ from, to })}, skipping`
+ `Current recieveLibrary is already set to ${config.receiveLibraryConfig.receiveLibrary} for ${formatOmniVector({ from, to })}${isDefaultLibrary ? ' (default)' : ''}, skipping`
)
return []
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 52972fbc99..45d4571e27 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -4183,6 +4183,107 @@ importers:
specifier: ^3.22.4
version: 3.22.4
+ packages/devtools-starknet:
+ dependencies:
+ p-memoize:
+ specifier: ~4.0.4
+ version: 4.0.4
+ devDependencies:
+ '@layerzerolabs/devtools':
+ specifier: ~2.0.4
+ version: link:../devtools
+ '@layerzerolabs/io-devtools':
+ specifier: ~0.3.2
+ version: link:../io-devtools
+ '@layerzerolabs/lz-definitions':
+ specifier: ^3.0.148
+ version: 3.0.148
+ '@layerzerolabs/lz-v2-utilities':
+ specifier: ^3.0.148
+ version: 3.0.148
+ '@layerzerolabs/protocol-starknet-v2':
+ specifier: ^0.2.19
+ version: 0.2.20
+ '@swc/core':
+ specifier: ^1.4.0
+ version: 1.4.0
+ '@swc/jest':
+ specifier: ^0.2.36
+ version: 0.2.36(@swc/core@1.4.0)
+ '@types/jest':
+ specifier: ^29.5.12
+ version: 29.5.12
+ jest:
+ specifier: ^29.7.0
+ version: 29.7.0(@types/node@18.18.14)(ts-node@10.9.2)
+ starknet:
+ specifier: ^8.9.0
+ version: 8.9.2
+ ts-node:
+ specifier: ^10.9.2
+ version: 10.9.2(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3)
+ tslib:
+ specifier: ~2.6.2
+ version: 2.6.3
+ tsup:
+ specifier: ~8.0.1
+ version: 8.0.1(@swc/core@1.4.0)(ts-node@10.9.2)(typescript@5.5.3)
+ typescript:
+ specifier: ^5.4.4
+ version: 5.5.3
+
+ packages/devtools-sui:
+ dependencies:
+ p-memoize:
+ specifier: ~4.0.4
+ version: 4.0.4
+ devDependencies:
+ '@layerzerolabs/devtools':
+ specifier: ~2.0.4
+ version: link:../devtools
+ '@layerzerolabs/io-devtools':
+ specifier: ~0.3.2
+ version: link:../io-devtools
+ '@layerzerolabs/lz-definitions':
+ specifier: ^3.0.148
+ version: 3.0.148
+ '@layerzerolabs/lz-sui-oft-sdk-v2':
+ specifier: ^3.0.156
+ version: 3.0.156(typescript@5.5.3)
+ '@layerzerolabs/lz-sui-sdk-v2':
+ specifier: ^3.0.156
+ version: 3.0.156(typescript@5.5.3)
+ '@mysten/bcs':
+ specifier: ^1.9.2
+ version: 1.9.2
+ '@mysten/sui':
+ specifier: ^1.45.2
+ version: 1.45.2(typescript@5.5.3)
+ '@swc/core':
+ specifier: ^1.4.0
+ version: 1.4.0
+ '@swc/jest':
+ specifier: ^0.2.36
+ version: 0.2.36(@swc/core@1.4.0)
+ '@types/jest':
+ specifier: ^29.5.12
+ version: 29.5.12
+ jest:
+ specifier: ^29.7.0
+ version: 29.7.0(@types/node@18.18.14)(ts-node@10.9.2)
+ ts-node:
+ specifier: ^10.9.2
+ version: 10.9.2(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3)
+ tslib:
+ specifier: ~2.6.2
+ version: 2.6.3
+ tsup:
+ specifier: ~8.0.1
+ version: 8.0.1(@swc/core@1.4.0)(ts-node@10.9.2)(typescript@5.5.3)
+ typescript:
+ specifier: ^5.4.4
+ version: 5.5.3
+
packages/devtools-ton:
dependencies:
'@ton/core':
@@ -4273,7 +4374,7 @@ importers:
version: 2.16.2
jest:
specifier: ^29.6.2
- version: 29.7.0(@types/node@18.18.14)(ts-node@10.9.2)
+ version: 29.7.0(@types/node@18.18.14)
tsup:
specifier: ~8.0.1
version: 8.0.1(@swc/core@1.4.0)(typescript@5.5.3)
@@ -4470,7 +4571,7 @@ importers:
version: 29.5.12
jest:
specifier: ^29.7.0
- version: 29.7.0(@types/node@18.18.14)(ts-node@10.9.2)
+ version: 29.7.0(@types/node@18.18.14)
tslib:
specifier: ~2.6.2
version: 2.6.3
@@ -5064,13 +5165,13 @@ importers:
version: 3.0.148
'@layerzerolabs/lz-solana-sdk-v2':
specifier: ^3.0.118
- version: 3.0.120(@swc/core@1.4.0)(@types/node@18.18.14)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3)
+ version: 3.0.120(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3)
'@layerzerolabs/lz-v2-utilities':
specifier: ^3.0.148
version: 3.0.148
'@layerzerolabs/oft-v2-solana-sdk':
specifier: ^3.0.38
- version: 3.0.38(@swc/core@1.4.0)(@types/node@18.18.14)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3)
+ version: 3.0.38(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3)
'@layerzerolabs/protocol-devtools':
specifier: ~3.0.2
version: link:../protocol-devtools
@@ -5129,6 +5230,146 @@ importers:
specifier: ^3.22.4
version: 3.22.4
+ packages/protocol-devtools-starknet:
+ dependencies:
+ p-memoize:
+ specifier: ~4.0.4
+ version: 4.0.4
+ devDependencies:
+ '@layerzerolabs/devtools':
+ specifier: ~2.0.4
+ version: link:../devtools
+ '@layerzerolabs/devtools-starknet':
+ specifier: ~0.1.0
+ version: link:../devtools-starknet
+ '@layerzerolabs/io-devtools':
+ specifier: ~0.3.2
+ version: link:../io-devtools
+ '@layerzerolabs/lz-definitions':
+ specifier: ^3.0.148
+ version: 3.0.148
+ '@layerzerolabs/lz-v2-utilities':
+ specifier: ^3.0.148
+ version: 3.0.148
+ '@layerzerolabs/protocol-devtools':
+ specifier: ~3.0.2
+ version: link:../protocol-devtools
+ '@layerzerolabs/protocol-starknet-v2':
+ specifier: ^0.2.19
+ version: 0.2.20
+ '@layerzerolabs/test-devtools':
+ specifier: ~0.4.7
+ version: link:../test-devtools
+ '@layerzerolabs/test-devtools-starknet':
+ specifier: ~0.0.1
+ version: link:../test-devtools-starknet
+ '@swc/core':
+ specifier: ^1.4.0
+ version: 1.4.0
+ '@swc/jest':
+ specifier: ^0.2.36
+ version: 0.2.36(@swc/core@1.4.0)
+ '@types/jest':
+ specifier: ^29.5.12
+ version: 29.5.12
+ fast-check:
+ specifier: ^3.15.1
+ version: 3.23.1
+ jest:
+ specifier: ^29.7.0
+ version: 29.7.0(@types/node@18.18.14)(ts-node@10.9.2)
+ jest-extended:
+ specifier: ^4.0.2
+ version: 4.0.2(jest@29.7.0)
+ starknet:
+ specifier: ^8.9.0
+ version: 8.9.2
+ ts-node:
+ specifier: ^10.9.2
+ version: 10.9.2(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3)
+ tslib:
+ specifier: ~2.6.2
+ version: 2.6.3
+ tsup:
+ specifier: ~8.0.1
+ version: 8.0.1(@swc/core@1.4.0)(ts-node@10.9.2)(typescript@5.5.3)
+ typescript:
+ specifier: ^5.4.4
+ version: 5.5.3
+ zod:
+ specifier: ^3.22.4
+ version: 3.22.4
+
+ packages/protocol-devtools-sui:
+ dependencies:
+ p-memoize:
+ specifier: ~4.0.4
+ version: 4.0.4
+ devDependencies:
+ '@layerzerolabs/devtools':
+ specifier: ~2.0.4
+ version: link:../devtools
+ '@layerzerolabs/devtools-sui':
+ specifier: ~0.1.0
+ version: link:../devtools-sui
+ '@layerzerolabs/io-devtools':
+ specifier: ~0.3.2
+ version: link:../io-devtools
+ '@layerzerolabs/lz-definitions':
+ specifier: ^3.0.148
+ version: 3.0.148
+ '@layerzerolabs/lz-sui-sdk-v2':
+ specifier: ^3.0.156
+ version: 3.0.156(typescript@5.5.3)
+ '@layerzerolabs/lz-v2-utilities':
+ specifier: ^3.0.148
+ version: 3.0.148
+ '@layerzerolabs/protocol-devtools':
+ specifier: ~3.0.2
+ version: link:../protocol-devtools
+ '@layerzerolabs/test-devtools':
+ specifier: ~0.4.7
+ version: link:../test-devtools
+ '@layerzerolabs/test-devtools-sui':
+ specifier: ~0.0.1
+ version: link:../test-devtools-sui
+ '@mysten/sui':
+ specifier: ^1.45.2
+ version: 1.45.2(typescript@5.5.3)
+ '@swc/core':
+ specifier: ^1.4.0
+ version: 1.4.0
+ '@swc/jest':
+ specifier: ^0.2.36
+ version: 0.2.36(@swc/core@1.4.0)
+ '@types/jest':
+ specifier: ^29.5.12
+ version: 29.5.12
+ fast-check:
+ specifier: ^3.15.1
+ version: 3.23.1
+ jest:
+ specifier: ^29.7.0
+ version: 29.7.0(@types/node@18.18.14)(ts-node@10.9.2)
+ jest-extended:
+ specifier: ^4.0.2
+ version: 4.0.2(jest@29.7.0)
+ ts-node:
+ specifier: ^10.9.2
+ version: 10.9.2(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3)
+ tslib:
+ specifier: ~2.6.2
+ version: 2.6.3
+ tsup:
+ specifier: ~8.0.1
+ version: 8.0.1(@swc/core@1.4.0)(ts-node@10.9.2)(typescript@5.5.3)
+ typescript:
+ specifier: ^5.4.4
+ version: 5.5.3
+ zod:
+ specifier: ^3.22.4
+ version: 3.22.4
+
packages/script-devtools-evm-foundry:
dependencies:
'@layerzerolabs/oapp-evm':
@@ -5303,6 +5544,42 @@ importers:
specifier: ^5.4.4
version: 5.5.3
+ packages/test-devtools-starknet:
+ devDependencies:
+ fast-check:
+ specifier: ^3.15.1
+ version: 3.23.1
+ ts-node:
+ specifier: ^10.9.2
+ version: 10.9.2(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3)
+ tslib:
+ specifier: ~2.6.2
+ version: 2.6.3
+ tsup:
+ specifier: ~8.0.1
+ version: 8.0.1(@swc/core@1.4.0)(ts-node@10.9.2)(typescript@5.5.3)
+ typescript:
+ specifier: ^5.4.4
+ version: 5.5.3
+
+ packages/test-devtools-sui:
+ devDependencies:
+ fast-check:
+ specifier: ^3.15.1
+ version: 3.23.1
+ ts-node:
+ specifier: ^10.9.2
+ version: 10.9.2(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3)
+ tslib:
+ specifier: ~2.6.2
+ version: 2.6.3
+ tsup:
+ specifier: ~8.0.1
+ version: 8.0.1(@swc/core@1.4.0)(ts-node@10.9.2)(typescript@5.5.3)
+ typescript:
+ specifier: ^5.4.4
+ version: 5.5.3
+
packages/test-devtools-ton:
devDependencies:
'@swc/core':
@@ -5722,13 +5999,13 @@ importers:
version: 3.0.148
'@layerzerolabs/lz-solana-sdk-v2':
specifier: ^3.0.118
- version: 3.0.120(@swc/core@1.4.0)(@types/node@18.18.14)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3)
+ version: 3.0.120(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3)
'@layerzerolabs/lz-v2-utilities':
specifier: ^3.0.148
version: 3.0.148
'@layerzerolabs/oft-v2-solana-sdk':
specifier: ^3.0.59
- version: 3.0.66(@swc/core@1.4.0)(@types/node@18.18.14)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3)
+ version: 3.0.66(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3)
'@layerzerolabs/protocol-devtools':
specifier: ~3.0.2
version: link:../protocol-devtools
@@ -5796,6 +6073,158 @@ importers:
specifier: ^3.22.4
version: 3.22.4
+ packages/ua-devtools-starknet:
+ dependencies:
+ p-memoize:
+ specifier: ~4.0.4
+ version: 4.0.4
+ devDependencies:
+ '@layerzerolabs/devtools':
+ specifier: ~2.0.4
+ version: link:../devtools
+ '@layerzerolabs/devtools-starknet':
+ specifier: ~0.1.0
+ version: link:../devtools-starknet
+ '@layerzerolabs/io-devtools':
+ specifier: ~0.3.2
+ version: link:../io-devtools
+ '@layerzerolabs/lz-definitions':
+ specifier: ^3.0.148
+ version: 3.0.148
+ '@layerzerolabs/lz-v2-utilities':
+ specifier: ^3.0.148
+ version: 3.0.148
+ '@layerzerolabs/oft-mint-burn-starknet':
+ specifier: ^0.2.19
+ version: 0.2.20(@layerzerolabs/protocol-starknet-v2@0.2.20)
+ '@layerzerolabs/protocol-devtools':
+ specifier: ~3.0.2
+ version: link:../protocol-devtools
+ '@layerzerolabs/protocol-devtools-starknet':
+ specifier: ~0.1.0
+ version: link:../protocol-devtools-starknet
+ '@layerzerolabs/protocol-starknet-v2':
+ specifier: ^0.2.19
+ version: 0.2.20
+ '@layerzerolabs/test-devtools':
+ specifier: ~0.4.7
+ version: link:../test-devtools
+ '@layerzerolabs/test-devtools-starknet':
+ specifier: ~0.0.1
+ version: link:../test-devtools-starknet
+ '@layerzerolabs/ua-devtools':
+ specifier: ~5.0.2
+ version: link:../ua-devtools
+ '@swc/core':
+ specifier: ^1.4.0
+ version: 1.4.0
+ '@swc/jest':
+ specifier: ^0.2.36
+ version: 0.2.36(@swc/core@1.4.0)
+ '@types/jest':
+ specifier: ^29.5.12
+ version: 29.5.12
+ fast-check:
+ specifier: ^3.15.1
+ version: 3.23.1
+ jest:
+ specifier: ^29.7.0
+ version: 29.7.0(@types/node@18.18.14)(ts-node@10.9.2)
+ jest-extended:
+ specifier: ^4.0.2
+ version: 4.0.2(jest@29.7.0)
+ starknet:
+ specifier: ^8.9.0
+ version: 8.9.2
+ ts-node:
+ specifier: ^10.9.2
+ version: 10.9.2(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3)
+ tslib:
+ specifier: ~2.6.2
+ version: 2.6.3
+ tsup:
+ specifier: ~8.0.1
+ version: 8.0.1(@swc/core@1.4.0)(ts-node@10.9.2)(typescript@5.5.3)
+ typescript:
+ specifier: ^5.4.4
+ version: 5.5.3
+
+ packages/ua-devtools-sui:
+ dependencies:
+ p-memoize:
+ specifier: ~4.0.4
+ version: 4.0.4
+ devDependencies:
+ '@layerzerolabs/devtools':
+ specifier: ~2.0.4
+ version: link:../devtools
+ '@layerzerolabs/devtools-sui':
+ specifier: ~0.1.0
+ version: link:../devtools-sui
+ '@layerzerolabs/io-devtools':
+ specifier: ~0.3.2
+ version: link:../io-devtools
+ '@layerzerolabs/lz-definitions':
+ specifier: ^3.0.148
+ version: 3.0.148
+ '@layerzerolabs/lz-sui-oft-sdk-v2':
+ specifier: ^3.0.156
+ version: 3.0.156(typescript@5.5.3)
+ '@layerzerolabs/lz-sui-sdk-v2':
+ specifier: ^3.0.156
+ version: 3.0.156(typescript@5.5.3)
+ '@layerzerolabs/lz-v2-utilities':
+ specifier: ^3.0.148
+ version: 3.0.148
+ '@layerzerolabs/protocol-devtools':
+ specifier: ~3.0.2
+ version: link:../protocol-devtools
+ '@layerzerolabs/protocol-devtools-sui':
+ specifier: ~0.1.0
+ version: link:../protocol-devtools-sui
+ '@layerzerolabs/test-devtools':
+ specifier: ~0.4.7
+ version: link:../test-devtools
+ '@layerzerolabs/test-devtools-sui':
+ specifier: ~0.0.1
+ version: link:../test-devtools-sui
+ '@layerzerolabs/ua-devtools':
+ specifier: ~5.0.2
+ version: link:../ua-devtools
+ '@mysten/sui':
+ specifier: ^1.45.2
+ version: 1.45.2(typescript@5.5.3)
+ '@swc/core':
+ specifier: ^1.4.0
+ version: 1.4.0
+ '@swc/jest':
+ specifier: ^0.2.36
+ version: 0.2.36(@swc/core@1.4.0)
+ '@types/jest':
+ specifier: ^29.5.12
+ version: 29.5.12
+ fast-check:
+ specifier: ^3.15.1
+ version: 3.23.1
+ jest:
+ specifier: ^29.7.0
+ version: 29.7.0(@types/node@18.18.14)(ts-node@10.9.2)
+ jest-extended:
+ specifier: ^4.0.2
+ version: 4.0.2(jest@29.7.0)
+ ts-node:
+ specifier: ^10.9.2
+ version: 10.9.2(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3)
+ tslib:
+ specifier: ~2.6.2
+ version: 2.6.3
+ tsup:
+ specifier: ~8.0.1
+ version: 8.0.1(@swc/core@1.4.0)(ts-node@10.9.2)(typescript@5.5.3)
+ typescript:
+ specifier: ^5.4.4
+ version: 5.5.3
+
packages/verify-contract:
devDependencies:
'@ethersproject/abi':
@@ -5833,7 +6262,7 @@ importers:
version: 12.6.1
jest:
specifier: ^29.7.0
- version: 29.7.0(@types/node@18.18.14)(ts-node@10.9.2)
+ version: 29.7.0(@types/node@18.18.14)
tsup:
specifier: ^8.0.1
version: 8.0.1(@swc/core@1.4.0)(typescript@5.5.3)
@@ -9121,7 +9550,7 @@ packages:
'@ledgerhq/hw-transport': 6.31.4
'@ledgerhq/hw-transport-webhid': 6.30.0
'@ledgerhq/hw-transport-webusb': 6.29.4
- '@mysten/bcs': 1.2.0
+ '@mysten/bcs': 1.9.2
axios: 1.8.4
bech32: 2.0.0
bignumber.js: 9.1.2
@@ -9149,7 +9578,7 @@ packages:
'@ledgerhq/hw-transport': 6.31.4
'@ledgerhq/hw-transport-webhid': 6.30.0
'@ledgerhq/hw-transport-webusb': 6.29.4
- '@mysten/bcs': 1.2.0
+ '@mysten/bcs': 1.9.2
axios: 1.8.4
bech32: 2.0.0
bignumber.js: 9.1.2
@@ -9177,7 +9606,7 @@ packages:
'@ledgerhq/hw-transport': 6.31.4
'@ledgerhq/hw-transport-webhid': 6.30.0
'@ledgerhq/hw-transport-webusb': 6.29.4
- '@mysten/bcs': 1.2.0
+ '@mysten/bcs': 1.9.2
axios: 1.8.4
bech32: 2.0.0
bignumber.js: 9.1.2
@@ -10491,7 +10920,7 @@ packages:
'@metaplex-foundation/umi-program-repository': 0.9.2(@metaplex-foundation/umi@0.9.2)
'@metaplex-foundation/umi-rpc-web3js': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.0)
'@metaplex-foundation/umi-transaction-factory-web3js': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.0)
- '@noble/hashes': 1.7.1
+ '@noble/hashes': 1.8.0
'@noble/secp256k1': 1.7.1
'@solana/web3.js': 1.98.0
bip39: 3.1.0
@@ -10648,6 +11077,12 @@ packages:
dependencies:
tiny-invariant: 1.3.3
+ /@layerzerolabs/lz-definitions@3.0.156:
+ resolution: {integrity: sha512-9gXF+C3LJ3mwrrkVBvVwXaeRnZNFNFeEuA0ykp881IRMFmmcVKLYOkidFgmaItm70MishMrrt4Lo+wWuzkqqUg==}
+ dependencies:
+ tiny-invariant: 1.3.3
+ dev: true
+
/@layerzerolabs/lz-definitions@3.0.75:
resolution: {integrity: sha512-TIbFBCfuElg6WcND4HNUROTSAayBETDC0YrISVadByo3iM2baAi+rpGC1kdrOxOTRlSBetd2khTOUCd7/sZdOQ==}
dependencies:
@@ -11273,9 +11708,9 @@ packages:
'@layerzerolabs/lz-definitions': 3.0.148
'@layerzerolabs/lz-utilities': 3.0.120(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3)
'@noble/ed25519': 1.7.3
- '@noble/hashes': 1.7.1
+ '@noble/hashes': 1.8.0
'@noble/secp256k1': 1.7.1
- '@scure/base': 1.2.4
+ '@scure/base': 1.2.6
bech32: 2.0.0
memoizee: 0.4.17
transitivePeerDependencies:
@@ -11404,9 +11839,9 @@ packages:
'@layerzerolabs/lz-definitions': 3.0.148
'@layerzerolabs/lz-utilities': 3.0.120(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3)
'@noble/ed25519': 1.7.3
- '@noble/hashes': 1.7.1
+ '@noble/hashes': 1.8.0
'@noble/secp256k1': 1.7.1
- '@scure/base': 1.2.1
+ '@scure/base': 1.2.6
transitivePeerDependencies:
- '@jest/globals'
- '@swc/core'
@@ -11427,9 +11862,9 @@ packages:
'@layerzerolabs/lz-definitions': 3.0.148
'@layerzerolabs/lz-utilities': 3.0.120(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3)
'@noble/ed25519': 1.7.3
- '@noble/hashes': 1.7.1
+ '@noble/hashes': 1.8.0
'@noble/secp256k1': 1.7.1
- '@scure/base': 1.2.1
+ '@scure/base': 1.2.6
transitivePeerDependencies:
- '@jest/globals'
- '@swc/core'
@@ -11611,7 +12046,7 @@ packages:
resolution: {integrity: sha512-kk/0pPKuNzIXL1Fh8caH3daYA/Lv2OIPeD7z/VIBANdyCBiYgncF6QK8muoFJ1iIvoQSnpAmgwgww1DlnYa2NA==}
dev: true
- /@layerzerolabs/lz-solana-sdk-v2@3.0.120(@swc/core@1.4.0)(@types/node@18.18.14)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3):
+ /@layerzerolabs/lz-solana-sdk-v2@3.0.120(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3):
resolution: {integrity: sha512-D09DF3kXtwBGwyI8WcLv8AWfUHrCTJwXoMe7U+EuXH+TOsOmwVhLAk8fulRjR9fHnvQvGEyl1blHUebtY2nt3A==}
dependencies:
'@layerzerolabs/lz-corekit-solana': 3.0.120(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3)
@@ -11724,7 +12159,7 @@ packages:
- utf-8-validate
dev: true
- /@layerzerolabs/lz-solana-sdk-v2@3.0.138(@swc/core@1.4.0)(@types/node@18.18.14)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3):
+ /@layerzerolabs/lz-solana-sdk-v2@3.0.138(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3):
resolution: {integrity: sha512-e1PbfEQ/QJnHwL+wYxy/EjPsluf3y1NSZKenbwHNjF/I3BEjLRuLbE7V6rLO0HIG6rZMQXEkT/keo1+MTwd/gQ==}
dependencies:
'@layerzerolabs/lz-corekit-solana': 3.0.138(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3)
@@ -11802,6 +12237,30 @@ packages:
- utf-8-validate
dev: false
+ /@layerzerolabs/lz-sui-oft-sdk-v2@3.0.156(typescript@5.5.3):
+ resolution: {integrity: sha512-zOvWGOD9HT1WdnY2nvVaG5Zw4Cqx3XeSQqxJBobt6TP1rLVlFN226BE8LVxEws8IdoomLD2Yz3BkR0BJS7l8bw==}
+ dependencies:
+ '@layerzerolabs/lz-definitions': 3.0.156
+ '@layerzerolabs/lz-sui-sdk-v2': 3.0.156(typescript@5.5.3)
+ '@mysten/sui': 1.45.2(typescript@5.5.3)
+ transitivePeerDependencies:
+ - '@gql.tada/svelte-support'
+ - '@gql.tada/vue-support'
+ - typescript
+ dev: true
+
+ /@layerzerolabs/lz-sui-sdk-v2@3.0.156(typescript@5.5.3):
+ resolution: {integrity: sha512-pQIl91brRk274hSiNS2NqUZ9U+l8zqPzeJ/1kOKPXZdiQIaz+/H2nwb0UXayxyAwXy3kl94ju/ecdOiKvHqAag==}
+ dependencies:
+ '@layerzerolabs/lz-definitions': 3.0.156
+ '@mysten/sui': 1.45.2(typescript@5.5.3)
+ js-sha3: 0.8.0
+ transitivePeerDependencies:
+ - '@gql.tada/svelte-support'
+ - '@gql.tada/vue-support'
+ - typescript
+ dev: true
+
/@layerzerolabs/lz-ton-sdk-v2@3.0.27:
resolution: {integrity: sha512-AU1uOzmLjWvyHdJGTo689bXLsCS/QAmfQSjvZ4544muLfpGVLl3l6lOl8DwmN1UQuwKKK94C5rEvoElVUYf0zQ==}
dependencies:
@@ -11886,7 +12345,7 @@ packages:
'@ethersproject/bytes': 5.8.0
'@initia/initia.js': 1.0.4(typescript@5.5.3)
'@layerzerolabs/lz-definitions': 3.0.148
- '@mysten/sui': 1.39.0(typescript@5.5.3)
+ '@mysten/sui': 1.45.2(typescript@5.5.3)
'@solana/web3.js': 1.98.0
'@ton/core': 0.59.0(@ton/crypto@3.3.0)
'@ton/crypto': 3.3.0
@@ -11921,7 +12380,7 @@ packages:
'@ethersproject/bytes': 5.8.0
'@initia/initia.js': 1.0.4(typescript@5.5.3)
'@layerzerolabs/lz-definitions': 3.0.148
- '@mysten/sui': 1.39.0(typescript@5.5.3)
+ '@mysten/sui': 1.45.2(typescript@5.5.3)
'@solana/web3.js': 1.98.0
'@ton/core': 0.59.0(@ton/crypto@3.3.0)
'@ton/crypto': 3.3.0
@@ -11956,7 +12415,7 @@ packages:
'@ethersproject/bytes': 5.8.0
'@initia/initia.js': 1.0.4(typescript@5.5.3)
'@layerzerolabs/lz-definitions': 3.0.148
- '@mysten/sui': 1.39.0(typescript@5.5.3)
+ '@mysten/sui': 1.45.2(typescript@5.5.3)
'@solana/web3.js': 1.98.0
'@ton/core': 0.59.0(@ton/crypto@3.3.0)
'@ton/crypto': 3.3.0
@@ -12114,6 +12573,14 @@ packages:
'@layerzerolabs/lz-definitions': 3.0.148
dev: true
+ /@layerzerolabs/oft-mint-burn-starknet@0.2.20(@layerzerolabs/protocol-starknet-v2@0.2.20):
+ resolution: {integrity: sha512-Hb2N+XZq7vak1JAiz/Wj5tP9qPrn8HzwfmIZw6QtFTIA9H24yby8Leew92CDh8wuHZn2Erc0bW8GRoqqxzGARg==}
+ peerDependencies:
+ '@layerzerolabs/protocol-starknet-v2': '>=0.2.0'
+ dependencies:
+ '@layerzerolabs/protocol-starknet-v2': 0.2.20
+ dev: true
+
/@layerzerolabs/oft-v2-solana-sdk@3.0.138(@swc/core@1.4.0)(@types/node@18.18.14)(chai@4.4.1)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3):
resolution: {integrity: sha512-q2+OALy4F3cZE3Q2UGdmyiBLFROCfSo7X7rfbA8xPScdiIy6IP6p0nsDQeTGUbbMIbXzTOTrAaIPOiR+pfS4OA==}
dependencies:
@@ -12180,12 +12647,12 @@ packages:
- utf-8-validate
dev: false
- /@layerzerolabs/oft-v2-solana-sdk@3.0.38(@swc/core@1.4.0)(@types/node@18.18.14)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3):
+ /@layerzerolabs/oft-v2-solana-sdk@3.0.38(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3):
resolution: {integrity: sha512-P06/a5+ixph0u1AQkDZ0P0oFaIAdfGPl/UezMfWXUpiWLth428RT0rrMR6qI7z6X1uxqlUFNIotz2ET1fyFcpQ==}
dependencies:
'@ethersproject/bytes': 5.7.0
'@layerzerolabs/lz-foundation': 3.0.38(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3)
- '@layerzerolabs/lz-solana-sdk-v2': 3.0.138(@swc/core@1.4.0)(@types/node@18.18.14)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3)
+ '@layerzerolabs/lz-solana-sdk-v2': 3.0.138(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3)
'@layerzerolabs/lz-v2-utilities': 3.0.148
'@metaplex-foundation/beet': 0.7.2
'@metaplex-foundation/beet-solana': 0.4.1
@@ -12213,12 +12680,12 @@ packages:
- utf-8-validate
dev: true
- /@layerzerolabs/oft-v2-solana-sdk@3.0.66(@swc/core@1.4.0)(@types/node@18.18.14)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3):
+ /@layerzerolabs/oft-v2-solana-sdk@3.0.66(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3):
resolution: {integrity: sha512-ijbvj6/Gc4O4WLHfqnrBuKUtIhpaYws/ORj2apZFT1RKSgAX8CCJ9aZmn0ClamEG98i+PpXoroPPU46LMOMZyA==}
dependencies:
'@ethersproject/bytes': 5.7.0
'@layerzerolabs/lz-foundation': 3.0.66(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3)
- '@layerzerolabs/lz-solana-sdk-v2': 3.0.138(@swc/core@1.4.0)(@types/node@18.18.14)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3)
+ '@layerzerolabs/lz-solana-sdk-v2': 3.0.138(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3)
'@layerzerolabs/lz-v2-utilities': 3.0.148
'@metaplex-foundation/beet': 0.7.2
'@metaplex-foundation/beet-solana': 0.4.1
@@ -12268,6 +12735,10 @@ packages:
zod: 3.22.4
dev: true
+ /@layerzerolabs/protocol-starknet-v2@0.2.20:
+ resolution: {integrity: sha512-iWueRZJfVcL622AAo9XZQ/RmAq15cuNNFmRGAEc6YQUPJrOFkgmwrh2Bosxc2cHSQGIZFT+k/ZUS/nTLG4ttVg==}
+ dev: true
+
/@layerzerolabs/scan-client-v2@0.0.1(redaxios@0.5.1):
resolution: {integrity: sha512-eugkDt4ajC5hqEHq23oTXMf28d5HrltapT7F5FIybluS8qAwJ5nc2Bbu9JRgUDdO8LQTQpi8AoVqnMbRumD4lA==}
peerDependencies:
@@ -12817,7 +13288,7 @@ packages:
dependencies:
'@metaplex-foundation/umi': 0.9.2
'@metaplex-foundation/umi-web3js-adapters': 0.9.2(@metaplex-foundation/umi@0.9.2)(@solana/web3.js@1.98.0)
- '@noble/curves': 1.8.1
+ '@noble/curves': 1.9.6
'@solana/web3.js': 1.98.0
/@metaplex-foundation/umi-http-fetch@0.9.2(@metaplex-foundation/umi@0.9.2):
@@ -12929,33 +13400,31 @@ packages:
multiformats: 9.9.0
murmurhash3js-revisited: 3.0.0
- /@mysten/bcs@1.2.0:
- resolution: {integrity: sha512-LuKonrGdGW7dq/EM6U2L9/as7dFwnhZnsnINzB/vu08Xfrj0qzWwpLOiXagAa5yZOPLK7anRZydMonczFkUPzA==}
- dependencies:
- bs58: 6.0.0
-
- /@mysten/bcs@1.8.0:
- resolution: {integrity: sha512-bDoLN1nN+XPONsvpNyNyqYHndM3PKWS419GLeRnbLoWyNm4bnyD1X4luEpJLLDq400hBuXiCan4RWjofvyTUIQ==}
+ /@mysten/bcs@1.9.2:
+ resolution: {integrity: sha512-kBk5xrxV9OWR7i+JhL/plQrgQ2/KJhB2pB5gj+w6GXhbMQwS3DPpOvi/zN0Tj84jwPvHMllpEl0QHj6ywN7/eQ==}
dependencies:
'@mysten/utils': 0.2.0
'@scure/base': 1.2.6
- /@mysten/sui@1.39.0(typescript@5.5.3):
- resolution: {integrity: sha512-tjH4oVAODO9JWPNvIBhAvorrwh7UfX5Lwf1oBjawnpk4sAIyajD8JYJUWXdI8o1H1519/5KEKaMT3ABAwTamQg==}
+ /@mysten/sui@1.45.2(typescript@5.5.3):
+ resolution: {integrity: sha512-gftf7fNpFSiXyfXpbtP2afVEnhc7p2m/MEYc/SO5pov92dacGKOpQIF7etZsGDI1Wvhv+dpph+ulRNpnYSs7Bg==}
engines: {node: '>=18'}
dependencies:
'@graphql-typed-document-node/core': 3.2.0(graphql@16.11.0)
- '@mysten/bcs': 1.8.0
+ '@mysten/bcs': 1.9.2
'@mysten/utils': 0.2.0
- '@noble/curves': 1.9.6
+ '@noble/curves': 1.9.4
'@noble/hashes': 1.8.0
+ '@protobuf-ts/grpcweb-transport': 2.11.1
+ '@protobuf-ts/runtime': 2.11.1
+ '@protobuf-ts/runtime-rpc': 2.11.1
'@scure/base': 1.2.6
'@scure/bip32': 1.7.0
'@scure/bip39': 1.6.0
gql.tada: 1.8.13(graphql@16.11.0)(typescript@5.5.3)
graphql: 16.11.0
poseidon-lite: 0.2.1
- valibot: 0.36.0
+ valibot: 1.2.0(typescript@5.5.3)
transitivePeerDependencies:
- '@gql.tada/svelte-support'
- '@gql.tada/vue-support'
@@ -12992,12 +13461,25 @@ packages:
dependencies:
'@noble/hashes': 1.3.3
+ /@noble/curves@1.7.0:
+ resolution: {integrity: sha512-UTMhXK9SeDhFJVrHeUJ5uZlI6ajXg10O6Ddocf9S6GjbSBVZsJo88HzKwXznNfGpMTRDyJkqMjNDPYgf0qFWnw==}
+ engines: {node: ^14.21.3 || >=16}
+ dependencies:
+ '@noble/hashes': 1.6.0
+ dev: true
+
/@noble/curves@1.8.1:
resolution: {integrity: sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==}
engines: {node: ^14.21.3 || >=16}
dependencies:
'@noble/hashes': 1.7.1
+ /@noble/curves@1.9.4:
+ resolution: {integrity: sha512-2bKONnuM53lINoDrSmK8qP8W271ms7pygDhZt4SiLOoLwBtoHqeCFi6RG42V8zd3mLHuJFhU/Bmaqo4nX0/kBw==}
+ engines: {node: ^14.21.3 || >=16}
+ dependencies:
+ '@noble/hashes': 1.8.0
+
/@noble/curves@1.9.6:
resolution: {integrity: sha512-GIKz/j99FRthB8icyJQA51E8Uk5hXmdyThjgQXRKiv9h0zeRlzSCLIzFw6K1LotZ3XuB7yzlf76qk7uBmTdFqA==}
engines: {node: ^14.21.3 || >=16}
@@ -13019,6 +13501,16 @@ packages:
resolution: {integrity: sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==}
engines: {node: '>= 16'}
+ /@noble/hashes@1.6.0:
+ resolution: {integrity: sha512-YUULf0Uk4/mAA89w+k3+yUYh6NrEvxZa5T6SY3wlMvE2chHkxFUUIDI8/XW1QSC357iA5pSnqt7XEhvFOqmDyQ==}
+ engines: {node: ^14.21.3 || >=16}
+ dev: true
+
+ /@noble/hashes@1.6.1:
+ resolution: {integrity: sha512-pq5D8h10hHBjyqX+cfBm0i8JUXJ0UhczFc4r74zbuT9XgewFo2E3J1cOaGtdZynILNmQ685YWGzGE1Zv6io50w==}
+ engines: {node: ^14.21.3 || >=16}
+ dev: true
+
/@noble/hashes@1.7.1:
resolution: {integrity: sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==}
engines: {node: ^14.21.3 || >=16}
@@ -13523,6 +14015,20 @@ packages:
config-chain: 1.1.13
dev: true
+ /@protobuf-ts/grpcweb-transport@2.11.1:
+ resolution: {integrity: sha512-1W4utDdvOB+RHMFQ0soL4JdnxjXV+ddeGIUg08DvZrA8Ms6k5NN6GBFU2oHZdTOcJVpPrDJ02RJlqtaoCMNBtw==}
+ dependencies:
+ '@protobuf-ts/runtime': 2.11.1
+ '@protobuf-ts/runtime-rpc': 2.11.1
+
+ /@protobuf-ts/runtime-rpc@2.11.1:
+ resolution: {integrity: sha512-4CqqUmNA+/uMz00+d3CYKgElXO9VrEbucjnBFEjqI4GuDrEQ32MaI3q+9qPBvIGOlL4PmHXrzM32vBPWRhQKWQ==}
+ dependencies:
+ '@protobuf-ts/runtime': 2.11.1
+
+ /@protobuf-ts/runtime@2.11.1:
+ resolution: {integrity: sha512-KuDaT1IfHkugM2pyz+FwiY80ejWrkH1pAtOBOZFuR6SXEFTsnb/jiQWQ1rCIrcKx2BtyxnxW6BWwsVSA/Ie+WQ==}
+
/@protobufjs/aspromise@1.1.2:
resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==}
@@ -13969,10 +14475,6 @@ packages:
/@scure/base@1.1.5:
resolution: {integrity: sha512-Brj9FiG2W1MRQSTB212YVPRrcbjkv48FoZi/u4l/zds/ieRrqsh7aUf6CLwkAq61oKXr/ZlTzlY66gLIj3TFTQ==}
- /@scure/base@1.2.1:
- resolution: {integrity: sha512-DGmGtC8Tt63J5GfHgfl5CuAXh96VF/LD8K9Hr/Gv0J2lAoRGlPOMpqMpMbCTOoOJMZCk2Xt+DskdDyn6dEFdzQ==}
- dev: true
-
/@scure/base@1.2.4:
resolution: {integrity: sha512-5Yy9czTO47mqz+/J8GM6GIId4umdCk1wc1q8rKERQulIoc8VP9pzDcghv10Tl2E7R96ZUx/PhND3ESYUQX8NuQ==}
@@ -14039,6 +14541,13 @@ packages:
'@noble/hashes': 1.8.0
'@scure/base': 1.2.6
+ /@scure/starknet@1.1.0:
+ resolution: {integrity: sha512-83g3M6Ix2qRsPN4wqLDqiRZ2GBNbjVWfboJE/9UjfG+MHr6oDSu/CWgy8hsBSJejr09DkkL+l0Ze4KVrlCIdtQ==}
+ dependencies:
+ '@noble/curves': 1.7.0
+ '@noble/hashes': 1.6.1
+ dev: true
+
/@sentry/core@5.30.0:
resolution: {integrity: sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg==}
engines: {node: '>=6'}
@@ -14617,8 +15126,8 @@ packages:
resolution: {integrity: sha512-nz3Q5OeyGFpFCR+erX2f6JPt3sKhzhYcSycBCSPkWjzSVDh/Rr1FqTVMRe58FKO16/ivTUcuJjeS5MyBvpkbzA==}
dependencies:
'@babel/runtime': 7.25.6
- '@noble/curves': 1.8.1
- '@noble/hashes': 1.7.1
+ '@noble/curves': 1.9.6
+ '@noble/hashes': 1.8.0
'@solana/buffer-layout': 4.0.1
agentkeepalive: 4.5.0
bigint-buffer: 1.1.5
@@ -14687,6 +15196,14 @@ packages:
- utf-8-validate
dev: true
+ /@starknet-io/types-js@0.8.4:
+ resolution: {integrity: sha512-0RZ3TZHcLsUTQaq1JhDSCM8chnzO4/XNsSCozwDET64JK5bjFDIf2ZUkta+tl5Nlbf4usoU7uZiDI/Q57kt2SQ==}
+ dev: true
+
+ /@starknet-io/types-js@0.9.2:
+ resolution: {integrity: sha512-vWOc0FVSn+RmabozIEWcEny1I73nDGTvOrLYJsR1x7LGA3AZmqt4i/aW69o/3i2NN5CVP8Ok6G1ayRQJKye3Wg==}
+ dev: true
+
/@swc/core-darwin-arm64@1.4.0:
resolution: {integrity: sha512-UTJ/Vz+s7Pagef6HmufWt6Rs0aUu+EJF4Pzuwvr7JQQ5b1DZeAAUeUtkUTFx/PvCbM8Xfw4XdKBUZfrIKCfW8A==}
engines: {node: '>=10'}
@@ -15952,6 +16469,16 @@ packages:
web3-utils: 1.10.4
dev: true
+ /abi-wan-kanabi@2.2.4:
+ resolution: {integrity: sha512-0aA81FScmJCPX+8UvkXLki3X1+yPQuWxEkqXBVKltgPAK79J+NB+Lp5DouMXa7L6f+zcRlIA/6XO7BN/q9fnvg==}
+ hasBin: true
+ dependencies:
+ ansicolors: 0.3.2
+ cardinal: 2.1.1
+ fs-extra: 10.1.0
+ yargs: 17.7.2
+ dev: true
+
/abitype@1.0.8(typescript@5.5.3)(zod@3.22.4):
resolution: {integrity: sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg==}
peerDependencies:
@@ -16692,7 +17219,7 @@ packages:
/bip39@3.1.0:
resolution: {integrity: sha512-c9kiwdk45Do5GL0vJMe7tS95VjCii65mYAH7DfWl3uW8AVzXKQVUm64i3hzVybBDMp9r7j9iNxR85+ul8MdN/A==}
dependencies:
- '@noble/hashes': 1.7.1
+ '@noble/hashes': 1.8.0
/bl@1.2.3:
resolution: {integrity: sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==}
@@ -17146,6 +17673,14 @@ packages:
tslib: 2.6.3
upper-case-first: 2.0.2
+ /cardinal@2.1.1:
+ resolution: {integrity: sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw==}
+ hasBin: true
+ dependencies:
+ ansicolors: 0.3.2
+ redeyed: 2.1.1
+ dev: true
+
/caseless@0.12.0:
resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==}
@@ -20812,7 +21347,7 @@ packages:
engines: {node: '>= 6'}
dependencies:
agent-base: 6.0.2
- debug: 4.3.7
+ debug: 4.4.0(supports-color@8.1.1)
transitivePeerDependencies:
- supports-color
@@ -22493,6 +23028,27 @@ packages:
supports-color: 8.1.1
dev: true
+ /jest@29.7.0(@types/node@18.18.14):
+ resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+ hasBin: true
+ peerDependencies:
+ node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
+ peerDependenciesMeta:
+ node-notifier:
+ optional: true
+ dependencies:
+ '@jest/core': 29.7.0(ts-node@10.9.2)
+ '@jest/types': 29.6.3
+ import-local: 3.2.0
+ jest-cli: 29.7.0(@types/node@18.18.14)(ts-node@10.9.2)
+ transitivePeerDependencies:
+ - '@types/node'
+ - babel-plugin-macros
+ - supports-color
+ - ts-node
+ dev: true
+
/jest@29.7.0(@types/node@18.18.14)(ts-node@10.9.2):
resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -22525,7 +23081,7 @@ packages:
dependencies:
'@jest/core': 29.7.0(ts-node@10.9.2)
'@jest/types': 29.6.3
- import-local: 3.1.0
+ import-local: 3.2.0
jest-cli: 29.7.0(@types/node@22.15.3)(ts-node@10.9.2)
transitivePeerDependencies:
- '@types/node'
@@ -23043,6 +23599,10 @@ packages:
dependencies:
js-tokens: 4.0.0
+ /lossless-json@4.3.0:
+ resolution: {integrity: sha512-ToxOC+SsduRmdSuoLZLYAr5zy1Qu7l5XhmPWM3zefCZ5IcrzW/h108qbJUKfOlDlhvhjUK84+8PSVX0kxnit0g==}
+ dev: true
+
/loupe@2.3.7:
resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==}
dependencies:
@@ -24799,6 +25359,12 @@ packages:
resolution: {integrity: sha512-FSD2AmfdbkYwl7KDExYQlVvIrFz6Yd83pGfaGjBzM9F6rpq8g652Q4Yq5QD4c+nf4g2AgeElv1y+8ajUPiOYMg==}
dev: false
+ /redeyed@2.1.1:
+ resolution: {integrity: sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ==}
+ dependencies:
+ esprima: 4.0.1
+ dev: true
+
/reduce-flatten@2.0.0:
resolution: {integrity: sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==}
engines: {node: '>=6'}
@@ -25764,6 +26330,22 @@ packages:
dependencies:
type-fest: 0.7.1
+ /starknet@8.9.2:
+ resolution: {integrity: sha512-+dp+o2w67fV6JyVOVkYeM1Ec71aORHc/JrF4VHLlfeGee0nLilooCQLE2u6hUcSGQG2x2/fvzkxYpIN+k1JBvA==}
+ engines: {node: '>=22'}
+ dependencies:
+ '@noble/curves': 1.7.0
+ '@noble/hashes': 1.6.1
+ '@scure/base': 1.2.6
+ '@scure/starknet': 1.1.0
+ '@starknet-io/starknet-types-08': /@starknet-io/types-js@0.8.4
+ '@starknet-io/starknet-types-09': /@starknet-io/types-js@0.9.2
+ abi-wan-kanabi: 2.2.4
+ lossless-json: 4.3.0
+ pako: 2.1.0
+ ts-mixer: 6.0.4
+ dev: true
+
/statuses@2.0.1:
resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
engines: {node: '>= 0.8'}
@@ -26505,6 +27087,10 @@ packages:
yargs-parser: 21.1.1
dev: true
+ /ts-mixer@6.0.4:
+ resolution: {integrity: sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==}
+ dev: true
+
/ts-mocha@11.1.0(mocha@11.7.1)(ts-node@10.9.2):
resolution: {integrity: sha512-yT7FfzNRCu8ZKkYvAOiH01xNma/vLq6Vit7yINKYFNVP8e5UyrYXSOMIipERTpzVKJQ4Qcos5bQo1tNERNZevQ==}
engines: {node: '>= 6.X.X'}
@@ -27215,9 +27801,6 @@ packages:
'@types/istanbul-lib-coverage': 2.0.6
convert-source-map: 2.0.0
- /valibot@0.36.0:
- resolution: {integrity: sha512-CjF1XN4sUce8sBK9TixrDqFM7RwNkuXdJu174/AwmQUB62QbCQADg5lLe8ldBalFgtj1uKj+pKwDJiNo4Mn+eQ==}
-
/valibot@0.37.0(typescript@5.5.3):
resolution: {integrity: sha512-FQz52I8RXgFgOHym3XHYSREbNtkgSjF9prvMFH1nBsRyfL6SfCzoT1GuSDTlbsuPubM7/6Kbw0ZMQb8A+V+VsQ==}
peerDependencies:
@@ -27228,6 +27811,16 @@ packages:
dependencies:
typescript: 5.5.3
+ /valibot@1.2.0(typescript@5.5.3):
+ resolution: {integrity: sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==}
+ peerDependencies:
+ typescript: '>=5'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+ dependencies:
+ typescript: 5.5.3
+
/validate-npm-package-license@3.0.4:
resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
dependencies: