-
Notifications
You must be signed in to change notification settings - Fork 182
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' of https://github.com/LayerZero-Labs/devtools int…
…o feat/oft-alt-example
- Loading branch information
Showing
20 changed files
with
892 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@layerzerolabs/devtools-evm-hardhat": patch | ||
--- | ||
|
||
Updating devtools dependency |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.turbo | ||
dist | ||
node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"extends": "../../.eslintrc.json" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{ | ||
"jsc": { | ||
"parser": { | ||
"syntax": "typescript", | ||
"decorators": true | ||
}, | ||
"transform": { | ||
"legacyDecorator": true | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<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-ton</h1> | ||
|
||
<!-- The badges section --> | ||
<p align="center"> | ||
<!-- Shields.io NPM published package version --> | ||
<a href="https://www.npmjs.com/package/@layerzerolabs/devtools-ton"><img alt="NPM Version" src="https://img.shields.io/npm/v/@layerzerolabs/devtools-ton"/></a> | ||
<!-- Shields.io NPM downloads --> | ||
<a href="https://www.npmjs.com/package/@layerzerolabs/devtools-ton"><img alt="Downloads" src="https://img.shields.io/npm/dm/@layerzerolabs/devtools-ton"/></a> | ||
<!-- Shields.io license badge --> | ||
<a href="https://www.npmjs.com/package/@layerzerolabs/devtools-ton"><img alt="NPM License" src="https://img.shields.io/npm/l/@layerzerolabs/devtools-ton"/></a> | ||
</p> | ||
|
||
Utilities for working with LayerZero TON contracts. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import type { Config } from 'jest' | ||
|
||
const config: Config = { | ||
cache: false, | ||
reporters: [['github-actions', { silent: false }], 'default'], | ||
testEnvironment: 'node', | ||
testTimeout: 60_000, | ||
moduleNameMapper: { | ||
'^@/(.*)$': '<rootDir>/src/$1', | ||
}, | ||
transform: { | ||
'^.+\\.(t|j)sx?$': '@swc/jest', | ||
}, | ||
} | ||
|
||
export default config |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
{ | ||
"name": "@layerzerolabs/devtools-ton", | ||
"version": "0.0.1", | ||
"description": "Developer utilities for working with LayerZero TON contracts", | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/LayerZero-Labs/devtools.git", | ||
"directory": "packages/devtools-ton" | ||
}, | ||
"license": "MIT", | ||
"exports": { | ||
".": { | ||
"types": "./dist/index.d.ts", | ||
"require": "./dist/index.js", | ||
"import": "./dist/index.mjs" | ||
}, | ||
"./package.json": "./package.json" | ||
}, | ||
"main": "dist/index.js", | ||
"module": "dist/index.mjs", | ||
"types": "dist/index.d.ts", | ||
"files": [ | ||
"dist" | ||
], | ||
"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" | ||
}, | ||
"devDependencies": { | ||
"@layerzerolabs/devtools": "~0.4.1", | ||
"@layerzerolabs/io-devtools": "~0.1.13", | ||
"@layerzerolabs/lz-definitions": "^3.0.12", | ||
"@swc/core": "^1.4.0", | ||
"@swc/jest": "^0.2.36", | ||
"@ton/core": "^0.59.0", | ||
"@ton/crypto": "^3.3.0", | ||
"@ton/ton": "^15.1.0", | ||
"@types/jest": "^29.5.12", | ||
"fast-check": "^3.16.0", | ||
"jest": "^29.7.0", | ||
"ton-crypto": "^3.2.0", | ||
"ts-node": "^10.9.2", | ||
"tslib": "~2.6.2", | ||
"tsup": "~8.0.1", | ||
"typescript": "^5.4.4" | ||
}, | ||
"peerDependencies": { | ||
"@layerzerolabs/devtools": "~0.4.1", | ||
"@layerzerolabs/io-devtools": "~0.1.13", | ||
"@layerzerolabs/lz-definitions": "^3.0.12", | ||
"@ton/core": "^0.59.0", | ||
"@ton/crypto": "^3.3.0", | ||
"@ton/ton": "^15.1.0" | ||
}, | ||
"publishConfig": { | ||
"access": "public" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './transactions' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export * from './serde' | ||
export * from './signer' | ||
export * from './state' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import { createModuleLogger } from '@layerzerolabs/io-devtools' | ||
import { | ||
beginCell, | ||
Cell, | ||
loadMessage, | ||
loadMessageRelaxed, | ||
type Message, | ||
storeMessage, | ||
storeMessageRelaxed, | ||
type MessageRelaxed, | ||
} from '@ton/core' | ||
|
||
/** | ||
* Serializes a Message object into a string that can be passed to OmniTransaction data | ||
* | ||
* @param {Message} message | ||
* @returns {string} Base64 serialized message string | ||
*/ | ||
export const serializeMessage = (message: Message): string => | ||
beginCell().store(storeMessage(message)).endCell().toBoc().toString('base64') | ||
|
||
/** | ||
* Serializes a MessageRelaxed object into a string that can be passed to OmniTransaction data | ||
* | ||
* @param {MessageRelaxed} message | ||
* @returns {string} Base64 serialized message string | ||
*/ | ||
export const serializeMessageRelaxed = (message: MessageRelaxed): string => | ||
messageRelaxedToCell(message).toBoc().toString('base64') | ||
|
||
/** | ||
* Deserializes a Message object from Base64 serialized representation | ||
* | ||
* @param {string} data | ||
* @returns {Message} | ||
*/ | ||
export const deserializeMessage = (data: string): Message => loadMessage(Cell.fromBase64(data).beginParse()) | ||
|
||
/** | ||
* Deserializes a MessageRelaxed object from Base64 serialized representation | ||
* | ||
* @param {string} data | ||
* @returns {MessageRelaxed} | ||
*/ | ||
export const deserializeMessageRelaxed = (data: string): MessageRelaxed => | ||
loadMessageRelaxed(Cell.fromBase64(data).beginParse()) | ||
|
||
/** | ||
* Tries to deserialize Base64 serialized data into a Message or MessageRelaxed object | ||
* | ||
* @param {string} data | ||
* @returns {Message | MessageRelaxed} | ||
*/ | ||
export const deserialize = (data: string): Message | MessageRelaxed => { | ||
const logger = createModuleLogger(`devtools-ton/transactions/serde:deserialize`) | ||
|
||
try { | ||
return deserializeMessageRelaxed(data) | ||
} catch (errorMessageRelaxed) { | ||
logger.verbose(`Failed to deserialize data as MessageRelaxed, trying Message: ${errorMessageRelaxed}`) | ||
|
||
try { | ||
return deserializeMessage(data) | ||
} catch (errorMessage) { | ||
logger.verbose(`Failed to deserialize data as Message: ${errorMessageRelaxed}`) | ||
|
||
throw new Error( | ||
`Failed to deserialize data.\n\nDeserializing as MessageRelaxed:\n\n${errorMessageRelaxed}\n\nDeserializing as Message:\n\n${errorMessage}` | ||
) | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Helper function for partial serialization, | ||
* mostly to work around jest expectation issues with Message equality | ||
* | ||
* @param {Message} message | ||
* @returns {Cell} | ||
*/ | ||
export const messageToCell = (message: Message): Cell => beginCell().store(storeMessage(message)).endCell() | ||
|
||
/** | ||
* Helper function for partial serialization, | ||
* mostly to work around jest expectation issues with MessageRelaxed equality | ||
* | ||
* @param {MessageRelaxed} message | ||
* @returns {Cell} | ||
*/ | ||
export const messageRelaxedToCell = (message: MessageRelaxed): Cell => | ||
beginCell().store(storeMessageRelaxed(message)).endCell() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
import { | ||
type OmniPoint, | ||
OmniSignerBase, | ||
type OmniTransaction, | ||
type OmniTransactionResponse, | ||
type OmniSigner, | ||
AsyncRetriable, | ||
} from '@layerzerolabs/devtools' | ||
import type { EndpointId } from '@layerzerolabs/lz-definitions' | ||
import type { Cell, OpenedContract, Contract, ContractProvider, MessageRelaxed } from '@ton/core' | ||
import { TonClient } from '@ton/ton' | ||
import type { KeyPair } from '@ton/crypto' | ||
import { deserializeMessageRelaxed } from './serde' | ||
import assert from 'assert' | ||
import { createIsCellInTransaction, hasTransactionBounced, isTransactionSuccessful } from './state' | ||
import { createModuleLogger, Logger } from '@layerzerolabs/io-devtools' | ||
|
||
export interface IWalletContractCreateTransferArgs { | ||
seqno: number | ||
secretKey: Buffer | ||
messages: MessageRelaxed[] | ||
} | ||
|
||
export interface IWalletContract extends Contract { | ||
/** | ||
* Get Wallet Seqno | ||
*/ | ||
getSeqno(provider: ContractProvider): Promise<number> | ||
|
||
/** | ||
* Create transfer cell | ||
* @param args | ||
*/ | ||
createTransfer(args: IWalletContractCreateTransferArgs): Promise<Cell> | Cell | ||
|
||
/** | ||
* Send signed transfer | ||
*/ | ||
send(provider: ContractProvider, message: Cell): Promise<void> | ||
} | ||
|
||
export class OmniSignerTON<TWalletContract extends IWalletContract> extends OmniSignerBase implements OmniSigner { | ||
// Due to generic type parameter resolution limitations, this needs to be typed as OpenedContract<IWalletContract> | ||
// rather than OpenedContract<TWalletContract> | ||
// | ||
// Using TWalletContract as the type parameter breaks the type inference for the OpenedContract type | ||
protected readonly openWallet: OpenedContract<IWalletContract> | ||
|
||
constructor( | ||
eid: EndpointId, | ||
public readonly keyPair: KeyPair, | ||
public readonly endpoint: string, | ||
public readonly wallet: TWalletContract, | ||
public readonly client: TonClient = new TonClient({ endpoint }), | ||
protected readonly logger: Logger = createModuleLogger('OmniSignerTON') | ||
) { | ||
super(eid) | ||
|
||
this.openWallet = client.open(wallet) | ||
} | ||
|
||
override getPoint(): OmniPoint | Promise<OmniPoint> { | ||
return { eid: this.eid, address: this.keyPair.publicKey.toString('base64') } | ||
} | ||
|
||
override sign(_transaction: OmniTransaction): Promise<string> { | ||
throw new Error('Method not implemented.') | ||
} | ||
|
||
override async signAndSend(omniTransaction: OmniTransaction): Promise<OmniTransactionResponse> { | ||
const seqno = await this.openWallet.getSeqno() | ||
const cell = await this.wallet.createTransfer({ | ||
seqno, | ||
secretKey: this.keyPair.secretKey, | ||
messages: [deserializeMessageRelaxed(omniTransaction.data)], | ||
}) | ||
|
||
await this.openWallet.send(cell) | ||
|
||
const transaction = await this.waitForCellSubmitted(cell) | ||
const transactionHash = transaction.hash().toString('base64') | ||
|
||
return { | ||
transactionHash, | ||
// This wait function does not really wait at this moment, it just gets the transaction state again | ||
// and checks whether the phases have completed successfully | ||
// | ||
// API V3 is required to investigate transaction traces for pending and finalized states. | ||
// | ||
// The guarantee that we rely on is that if two transactions have been submitted in order, | ||
// their messages will be executed in order even if the later transactions don't wait for the earlier ones to finish | ||
wait: async () => { | ||
const transactionState = await this.client.getTransaction( | ||
this.wallet.address, | ||
transaction.lt.toString(), | ||
transactionHash | ||
) | ||
assert(transactionState != null, `Transaction '${transactionHash}' missing from the API`) | ||
assert(!hasTransactionBounced(transactionState), `Transaction '${transactionHash}' has bounced`) | ||
assert(isTransactionSuccessful(transactionState), `Transaction '${transactionHash}' has not succeeded`) | ||
|
||
return { transactionHash } | ||
}, | ||
} | ||
} | ||
|
||
@AsyncRetriable({ | ||
// We'll use the AsyncRetriable with infinite retries to provide us with a exponential backoff retry logic | ||
enabled: true, | ||
numAttempts: Number.POSITIVE_INFINITY, | ||
maxDelay: 1_000, | ||
}) | ||
protected async waitForCellSubmitted(cell: Cell, limit: number = 100) { | ||
const transactions = await this.client.getTransactions(this.wallet.address, { | ||
limit, | ||
}) | ||
|
||
const transaction = transactions.find(createIsCellInTransaction(cell)) | ||
assert(transaction != null, `Failed to locate cell ${cell.toString()} among the last ${limit} transactions`) | ||
|
||
return transaction | ||
} | ||
} |
Oops, something went wrong.