Skip to content

Commit

Permalink
feat(target_chains/ton): add ton js sdk (#2044)
Browse files Browse the repository at this point in the history
* init ton js sdk

* fix docs

* fix

* address comments
  • Loading branch information
cctdaniel authored Oct 16, 2024
1 parent fa4b9b9 commit 5ad83ce
Show file tree
Hide file tree
Showing 13 changed files with 621 additions and 87 deletions.
285 changes: 249 additions & 36 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@ packages:
- "target_chains/solana/sdk/js/solana_utils"
- "target_chains/solana/sdk/js/pyth_solana_receiver"
- "target_chains/ton/contracts"
- "target_chains/ton/sdk/js"
- "contract_manager"
7 changes: 4 additions & 3 deletions target_chains/ton/contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@
"test": "jest --verbose"
},
"devDependencies": {
"@pythnetwork/pyth-ton-js": "workspace:*",
"@pythnetwork/price-service-sdk": "workspace:*",
"@pythnetwork/xc-admin-common": "workspace:*",
"@pythnetwork/hermes-client": "workspace:*",
"@ton/blueprint": "^0.22.0",
"@ton/core": "~0",
"@ton/crypto": "^3.2.0",
"@ton/core": "^0.59.0",
"@ton/crypto": "^3.3.0",
"@ton/sandbox": "^0.20.0",
"@ton/test-utils": "^0.4.2",
"@ton/ton": "^13.11.2",
"@ton/ton": "^15.1.0",
"@types/jest": "^29.5.12",
"@types/node": "^20.14.10",
"@wormhole-foundation/sdk-definitions": "^0.10.7",
Expand Down
46 changes: 1 addition & 45 deletions target_chains/ton/contracts/tests/utils.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,12 @@
import { DataSource } from "@pythnetwork/xc-admin-common";
import { Cell, Transaction, beginCell } from "@ton/core";
import { Cell, Transaction } from "@ton/core";
import { Buffer } from "buffer";

const GOVERNANCE_MAGIC = 0x5054474d;
const GOVERNANCE_MODULE = 1;
const AUTHORIZE_UPGRADE_CONTRACT_ACTION = 0;
const TARGET_CHAIN_ID = 1;

export function createCellChain(buffer: Buffer): Cell {
let chunks = bufferToChunks(buffer, 127);
let lastCell: Cell | null = null;
// Iterate through chunks in reverse order
for (let i = chunks.length - 1; i >= 0; i--) {
const chunk = chunks[i];
const cellBuilder = beginCell();
const buffer = Buffer.from(chunk);
cellBuilder.storeBuffer(buffer);

if (lastCell) {
cellBuilder.storeRef(lastCell);
}

lastCell = cellBuilder.endCell();
}

// lastCell will be the root cell of our chain
if (!lastCell) {
throw new Error("Failed to create cell chain");
}
return lastCell;
}

function bufferToChunks(
buff: Buffer,
chunkSizeBytes: number = 127
): Uint8Array[] {
const chunks: Uint8Array[] = [];
const uint8Array = new Uint8Array(
buff.buffer,
buff.byteOffset,
buff.byteLength
);

for (let offset = 0; offset < uint8Array.length; offset += chunkSizeBytes) {
const remainingBytes = Math.min(chunkSizeBytes, uint8Array.length - offset);
const chunk = uint8Array.subarray(offset, offset + remainingBytes);
chunks.push(chunk);
}

return chunks;
}

// Helper function to parse DataSource from a Cell
export function parseDataSource(cell: Cell): DataSource {
const slice = cell.beginParse();
Expand Down
2 changes: 1 addition & 1 deletion target_chains/ton/contracts/wrappers/BaseWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
SendMode,
toNano,
} from "@ton/core";
import { createCellChain } from "../tests/utils";
import { createCellChain } from "@pythnetwork/pyth-ton-js";
import { createGuardianSetsDict } from "../tests/utils/wormhole";
import { HexString, Price } from "@pythnetwork/price-service-sdk";
import { DataSource } from "@pythnetwork/xc-admin-common";
Expand Down
2 changes: 1 addition & 1 deletion target_chains/ton/contracts/wrappers/PythTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import {
} from "@ton/core";
import { BaseWrapper } from "./BaseWrapper";
import { HexString, Price } from "@pythnetwork/price-service-sdk";
import { createCellChain } from "@pythnetwork/pyth-ton-js";
import { DataSource } from "@pythnetwork/xc-admin-common";
import { createCellChain } from "../tests/utils";

export type PythTestConfig = {
priceFeedId: HexString;
Expand Down
2 changes: 1 addition & 1 deletion target_chains/ton/contracts/wrappers/WormholeTest.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Cell, contractAddress, ContractProvider, Sender } from "@ton/core";
import { BaseWrapper } from "./BaseWrapper";
import { createCellChain } from "../tests/utils";
import { createCellChain } from "@pythnetwork/pyth-ton-js";
import { parseGuardianSetKeys } from "../tests/utils/wormhole";

export type WormholeTestConfig = {
Expand Down
6 changes: 6 additions & 0 deletions target_chains/ton/sdk/js/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
root: true,
parser: "@typescript-eslint/parser",
plugins: ["@typescript-eslint"],
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
};
1 change: 1 addition & 0 deletions target_chains/ton/sdk/js/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lib
106 changes: 106 additions & 0 deletions target_chains/ton/sdk/js/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Pyth Network TON SDK

This SDK provides a JavaScript interface for interacting with the Pyth Network on the TON blockchain.

## Installation

```bash
npm install @pythnetwork/pyth-ton-js
```

## Usage

Here's a basic example of how to use the SDK:

```bash
npm install @pythnetwork/pyth-ton-js @pythnetwork/hermes-client @ton/core @ton/ton @ton/crypto
```

```typescript
import { TonClient, Address, WalletContractV4 } from "@ton/ton";
import { toNano } from "@ton/core";
import { mnemonicToPrivateKey } from "@ton/crypto";
import { HermesClient } from "@pythnetwork/hermes-client";
import {
PythContract,
PYTH_CONTRACT_ADDRESS_TESTNET,
} from "@pythnetwork/pyth-ton-js";

const BTC_PRICE_FEED_ID =
"0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43";

async function main() {
const client = new TonClient({
endpoint: "https://testnet.toncenter.com/api/v2/jsonRPC",
apiKey: "your-api-key-here", // Optional, but note that without api-key you need to send requests once per second
});

const contractAddress = Address.parse(PYTH_CONTRACT_ADDRESS_TESTNET);
const contract = client.open(PythContract.createFromAddress(contractAddress));

const guardianSetIndex = await contract.getCurrentGuardianSetIndex();
console.log("Guardian Set Index:", guardianSetIndex);

const price = await contract.getPriceUnsafe(BTC_PRICE_FEED_ID);
console.log("BTC Price from TON contract:", price);

const hermesEndpoint = "https://hermes.pyth.network";
const hermesClient = new HermesClient(hermesEndpoint);

const priceIds = [BTC_PRICE_FEED_ID];
const latestPriceUpdates = await hermesClient.getLatestPriceUpdates(
priceIds,
{
encoding: "hex",
}
);
console.log("Hermes BTC price:", latestPriceUpdates.parsed?.[0].price);

const updateData = Buffer.from(latestPriceUpdates.binary.data[0], "hex");
console.log("Update data:", updateData);

const updateFee = await contract.getUpdateFee(updateData);
console.log("Update fee:", updateFee);

const mnemonic = "your mnemonic here";
const key = await mnemonicToPrivateKey(mnemonic.split(" "));
const wallet = WalletContractV4.create({
publicKey: key.publicKey,
workchain: 0,
});
const provider = client.open(wallet);

await contract.sendUpdatePriceFeeds(
provider.sender(key.secretKey),
updateData,
156000000n + BigInt(updateFee) // 156000000 = 390000 (estimated gas used for the transaction, this is defined in contracts/common/gas.fc as UPDATE_PRICE_FEEDS_GAS) * 400 (current settings in basechain are as follows: 1 unit of gas costs 400 nanotons)
);
console.log("Price feeds updated successfully.");

const updatedPrice = await contract.getPriceUnsafe(BTC_PRICE_FEED_ID);
console.log("Updated BTC Price from TON contract:", updatedPrice);
}

main().catch(console.error);
```

## API Reference

### `PythContract`

The main class for interacting with the Pyth contract on TON.

#### Methods:

- `getCurrentGuardianSetIndex(provider: ContractProvider): Promise<number>`
- `getPriceUnsafe(provider: ContractProvider, priceFeedId: string): Promise<PriceInfo>`
- `getPriceNoOlderThan(provider: ContractProvider, timePeriod: number, priceFeedId: string): Promise<PriceInfo>`
- `getEmaPriceUnsafe(provider: ContractProvider, priceFeedId: string): Promise<PriceInfo>`
- `getEmaPriceNoOlderThan(provider: ContractProvider, timePeriod: number, priceFeedId: string): Promise<PriceInfo>`
- `getUpdateFee(provider: ContractProvider, vm: Buffer): Promise<number>`
- `getSingleUpdateFee(provider: ContractProvider): Promise<number>`
- `sendUpdatePriceFeeds(provider: ContractProvider, via: Sender, updateData: Buffer, updateFee: bigint): Promise<void>`

### Constants

- `PYTH_CONTRACT_ADDRESS_TESTNET`: The address of the Pyth contract on the TON testnet.
51 changes: 51 additions & 0 deletions target_chains/ton/sdk/js/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"name": "@pythnetwork/pyth-ton-js",
"version": "0.1.0",
"description": "Pyth Network TON Utilities",
"homepage": "https://pyth.network",
"author": {
"name": "Pyth Data Association"
},
"main": "lib/index.js",
"types": "lib/index.d.ts",
"files": [
"lib/**/*"
],
"repository": {
"type": "git",
"url": "https://github.com/pyth-network/pyth-crosschain",
"directory": "target_chains/ton/sdk/js"
},
"publishConfig": {
"access": "public"
},
"scripts": {
"test": "jest --passWithNoTests",
"build": "tsc",
"format": "prettier --write \"src/**/*.ts\"",
"lint": "eslint src/",
"prepublishOnly": "pnpm run build && pnpm test && pnpm run lint",
"preversion": "pnpm run lint",
"version": "pnpm run format && git add -A src"
},
"keywords": [
"pyth",
"oracle"
],
"license": "Apache-2.0",
"devDependencies": {
"@ton/core": "^0.59.0",
"@ton/ton": "^15.1.0",
"@ton/crypto": "^3.3.0",
"@types/node": "^18.11.18",
"@typescript-eslint/eslint-plugin": "^5.21.0",
"@typescript-eslint/parser": "^5.21.0",
"eslint": "^8.14.0",
"jest": "^29.4.1",
"prettier": "^2.6.2",
"ts-jest": "^29.0.5",
"typescript": "^4.6.3",
"ts-node": "^10.9.2"
},
"dependencies": {}
}
Loading

0 comments on commit 5ad83ce

Please sign in to comment.