Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/devtools-starknet/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dist
node_modules
3 changes: 3 additions & 0 deletions packages/devtools-starknet/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "../../.eslintrc.json"
}
2 changes: 2 additions & 0 deletions packages/devtools-starknet/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dist/
node_modules/
5 changes: 5 additions & 0 deletions packages/devtools-starknet/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# @layerzerolabs/devtools-starknet

## 0.1.0

- Initial release.
9 changes: 9 additions & 0 deletions packages/devtools-starknet/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<p align="center">
<a href="https://layerzero.network">
<img alt="LayerZero" style="max-width: 500px" src="https://d3a2dpnnrypp5h.cloudfront.net/bridge-app/lz.png"/>
</a>
</p>

<h1 align="center">@layerzerolabs/devtools-starknet</h1>

Utilities for working with LayerZero Starknet contracts.
66 changes: 66 additions & 0 deletions packages/devtools-starknet/package.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
9 changes: 9 additions & 0 deletions packages/devtools-starknet/src/common/addresses.ts
Original file line number Diff line number Diff line change
@@ -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}`)
}
}
1 change: 1 addition & 0 deletions packages/devtools-starknet/src/common/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './addresses'
22 changes: 22 additions & 0 deletions packages/devtools-starknet/src/connection/factory.ts
Original file line number Diff line number Diff line change
@@ -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<Record<EndpointId, string | null>> = {}): 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) }))
2 changes: 2 additions & 0 deletions packages/devtools-starknet/src/connection/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './factory'
export * from './types'
4 changes: 4 additions & 0 deletions packages/devtools-starknet/src/connection/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import type { RpcProvider } from 'starknet'
import type { EndpointId } from '@layerzerolabs/lz-definitions'

export type ConnectionFactory = (eid: EndpointId) => Promise<RpcProvider>
4 changes: 4 additions & 0 deletions packages/devtools-starknet/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './common'
export * from './connection'
export * from './omnigraph'
export * from './transactions'
10 changes: 10 additions & 0 deletions packages/devtools-starknet/src/omnigraph/coordinates.ts
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions packages/devtools-starknet/src/omnigraph/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './coordinates'
export * from './sdk'
export * from './types'
24 changes: 24 additions & 0 deletions packages/devtools-starknet/src/omnigraph/sdk.ts
Original file line number Diff line number Diff line change
@@ -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),
}
}
}
3 changes: 3 additions & 0 deletions packages/devtools-starknet/src/omnigraph/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import type { IOmniSDK as IOmniSDKAbstract } from '@layerzerolabs/devtools'

export interface IOmniSDK extends IOmniSDKAbstract {}
2 changes: 2 additions & 0 deletions packages/devtools-starknet/src/transactions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './serde'
export * from './signer'
6 changes: 6 additions & 0 deletions packages/devtools-starknet/src/transactions/serde.ts
Original file line number Diff line number Diff line change
@@ -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[]
59 changes: 59 additions & 0 deletions packages/devtools-starknet/src/transactions/signer.ts
Original file line number Diff line number Diff line change
@@ -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<string> {
throw new Error('Starknet OmniSigner does not support offline signing. Use signAndSend instead.')
}

async signAndSend(transaction: OmniTransaction): Promise<OmniTransactionResponse<OmniTransactionReceipt>> {
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<Account>
export type StarknetProviderFactory = (eid: EndpointId) => Promise<RpcProvider>

export const createStarknetSignerFactory =
(
accountFactory: StarknetAccountFactory,
providerFactory: StarknetProviderFactory
): OmniSignerFactory<OmniSigner<OmniTransactionResponse<OmniTransactionReceipt>>> =>
async (eid: EndpointId) =>
new OmniSignerStarknet(eid, await providerFactory(eid), await accountFactory(eid))
8 changes: 8 additions & 0 deletions packages/devtools-starknet/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "dist"
},
"include": ["src"]
}
9 changes: 9 additions & 0 deletions packages/devtools-starknet/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { defineConfig } from 'tsup'

export default defineConfig({
entry: ['src/index.ts'],
format: ['cjs', 'esm'],
dts: true,
sourcemap: true,
clean: true,
})
2 changes: 2 additions & 0 deletions packages/devtools-sui/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dist
node_modules
3 changes: 3 additions & 0 deletions packages/devtools-sui/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "../../.eslintrc.json"
}
2 changes: 2 additions & 0 deletions packages/devtools-sui/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dist/
node_modules/
5 changes: 5 additions & 0 deletions packages/devtools-sui/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# @layerzerolabs/devtools-sui

## 0.1.0

- Initial release.
9 changes: 9 additions & 0 deletions packages/devtools-sui/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<p align="center">
<a href="https://layerzero.network">
<img alt="LayerZero" style="max-width: 500px" src="https://d3a2dpnnrypp5h.cloudfront.net/bridge-app/lz.png"/>
</a>
</p>

<h1 align="center">@layerzerolabs/devtools-sui</h1>

Utilities for working with LayerZero Sui contracts.
68 changes: 68 additions & 0 deletions packages/devtools-sui/package.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
9 changes: 9 additions & 0 deletions packages/devtools-sui/src/common/addresses.ts
Original file line number Diff line number Diff line change
@@ -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}`)
}
}
1 change: 1 addition & 0 deletions packages/devtools-sui/src/common/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './addresses'
17 changes: 17 additions & 0 deletions packages/devtools-sui/src/connection/factory.ts
Original file line number Diff line number Diff line change
@@ -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<Record<EndpointId, string | null>> = {}): 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) }))
Loading
Loading