diff --git a/.env.example b/.env.example index 91962fe..f756c4b 100644 --- a/.env.example +++ b/.env.example @@ -1,81 +1,18 @@ -# ══════════════════════════════════════════════════════════════════ -# TON STARTER KIT - Environment Configuration -# ══════════════════════════════════════════════════════════════════ -# -# Copy this file to .env and fill in your actual values -# All addresses below are from the TON CCIP Staging Environment -# -# ══════════════════════════════════════════════════════════════════ +# Fill in these values to begin: +SEPOLIA_PRIVATE_KEY=0x +TON_API_KEY= +TON_MNEMONIC="" -# ────────────────────────────────────────────────────────────────── -# SEPOLIA (EVM) CONFIGURATION -# ────────────────────────────────────────────────────────────────── +# Fill in these values after deployment: +EVM_RECEIVER_ADDRESS= +TON_RECEIVER_ADDRESS= -# Sepolia RPC endpoint (get from Alchemy, Infura, or public RPC) +# Pre-set values: SEPOLIA_RPC_URL=https://ethereum-sepolia-rpc.publicnode.com - -# Your Sepolia private key (DO NOT COMMIT THIS!) -SEPOLIA_PRIVATE_KEY=0x - -# Sepolia CCIP Router (Staging Environment) SEPOLIA_ROUTER=0xEA5d21863357870Be141b318A4d4E9709Ac3F5ce - -# Sepolia CCIP OnRamp (Optional - for reference) SEPOLIA_ONRAMP=0xFB34b9969Dd201cc9A04E604a6D40AF917b6C1E8 - -# Sepolia Chain Selector SEPOLIA_CHAIN_SELECTOR=16015286601757825753 - -# ────────────────────────────────────────────────────────────────── -# TON TESTNET CONFIGURATION -# ────────────────────────────────────────────────────────────────── - -# TON Testnet RPC endpoint TON_RPC_URL=https://testnet.toncenter.com/api/v2/jsonRPC - -# Your TON wallet mnemonic (24 words, space-separated, DO NOT COMMIT!) -TON_MNEMONIC=word1 word2 word3 ... word24 - -# TON CCIP Router (Staging Environment) TON_ROUTER=EQDrkhDYT8czFZuYNPlFMJ5ICD8FQoEW0b1KvITMVljC3ZTV - -# TON CCIP OffRamp (Staging Environment) -# Note: In current staging deployment, Router acts as its own OffRamp TON_OFFRAMP=EQDrkhDYT8czFZuYNPlFMJ5ICD8FQoEW0b1KvITMVljC3ZTV - -# TON Chain Selector TON_CHAIN_SELECTOR=1399300952838017768 - -# ────────────────────────────────────────────────────────────────── -# DEPLOYED CONTRACT ADDRESSES (Fill after deployment) -# ────────────────────────────────────────────────────────────────── - -# Your deployed EVM receiver contract address (Sepolia) -EVM_RECEIVER_ADDRESS= - -# Your deployed TON receiver contract address (TON Testnet) -TON_RECEIVER_ADDRESS= - -# ────────────────────────────────────────────────────────────────── -# OPTIONAL: ADDITIONAL NETWORKS (For future expansion) -# ────────────────────────────────────────────────────────────────── - -# Arbitrum Sepolia -ARBITRUM_SEPOLIA_RPC_URL=https://sepolia-rollup.arbitrum.io/rpc -ARBITRUM_SEPOLIA_ROUTER= - -# Avalanche Fuji -AVALANCHE_FUJI_RPC_URL=https://api.avax-test.network/ext/bc/C/rpc -AVALANCHE_FUJI_ROUTER= - -# ══════════════════════════════════════════════════════════════════ -# NOTES -# ══════════════════════════════════════════════════════════════════ -# -# 1. Get Sepolia ETH from: https://faucets.chain.link/sepolia -# 2. Get TON testnet tokens from: @testgiver_ton_bot on Telegram -# 3. Never commit your .env file to version control -# 4. All addresses above are for STAGING environment (not production) -# 5. Staging addresses may change during redeployments -# -# ══════════════════════════════════════════════════════════════════ diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..652b930 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @smartcontractkit/devrel diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6cee857 --- /dev/null +++ b/LICENSE @@ -0,0 +1,9 @@ +Copyright (c) 2025 SmartContract ChainLink Limited SEZC + +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md index 0d9d5a8..f9f324b 100644 --- a/README.md +++ b/README.md @@ -1,57 +1,7 @@ -# TON Starter Kit +# Chainlink CCIP TON Starter Kit A starter kit for working with Chainlink CCIP cross-chain messaging between TON and EVM chains. -## Table of Contents - -- [Environment Context](#environment-context) -- [Repo Structure](#repo-structure) -- [Prerequisites](#prerequisites) -- [TON Wallet Setup](#ton-wallet-setup) -- [Getting Test Funds](#getting-test-funds) -- [Tutorial](#tutorial) - - [Step 0: Deploy Receiver Contracts](#step-0-deploy-receiver-contracts) - - [Step 1: Send Message from EVM to TON](#step-1-send-message-from-evm-to-ton) - - [Step 2: Send Message from TON to EVM](#step-2-send-message-from-ton-to-evm) -- [Network Information](#network-information) -- [Token Transfers](#token-transfers) -- [Fee Payments](#fee-payments) -- [Key Implementation Details](#key-implementation-details) -- [Troubleshooting](#troubleshooting) -- [Configuration Reference](#configuration-reference) -- [Resources](#resources) - -## Environment Context - -> ⚠️ **Staging Environment Notice** -> -> This starter kit targets the **non-production staging environment**. While high uptime is a goal, it does not carry production-level SLAs. -> -> - **Redeployments may occur**, potentially changing contract addresses -> - **Critical updates** (including address changes) will be communicated via designated channels -> - **Current functionality** is limited to arbitrary messaging -> - **Token transfers** will be integrated in a future release - -## Repo Structure - -There are 3 main folders in the repo: **contracts**, **scripts**, and **chainlink-ton**. - -### Contracts -The `contracts/` directory contains smart contracts for both chains: -- `MessageReceiver.sol` - EVM receiver contract that implements the CCIP `IAny2EVMMessageReceiver` interface -- `MessageReceiver.tolk` - TON receiver contract that handles CCIP messages and sends confirmation - -### Scripts -The scripts are organized by cross-chain direction. For a deep dive into how these scripts handle **encoding**, **decoding**, and **byte-level execution**, please read the [**Script Execution Guide**](./scripts/README.md). - -- **`scripts/evm2ton/`** - Scripts for sending messages from EVM → TON -- **`scripts/ton2evm/`** - Scripts for sending messages from TON → EVM -- **`scripts/utils/`** - Helper scripts for checking message status -- **`scripts/deploy/`** - Contract deployment scripts - -### chainlink-ton -Internal dependency providing TON contract wrappers, types, and TL-B bindings for CCIP interactions. This submodule contains the official TON smart contract implementations and TypeScript interfaces. - ## Prerequisites Before you begin, ensure you have: @@ -66,54 +16,75 @@ Before you begin, ensure you have: To interact with TON testnet, you need a wallet and its 24-word recovery phrase (mnemonic). 1. Download [TON Keeper](https://tonkeeper.com/) on iOS or Android -2. Create a new wallet and **save your 24-word recovery phrase** - this is your `TON_MNEMONIC` -3. Switch to testnet: Settings (gear icon) → Dev Menu → Switch to Testnet -4. Select the **V4R2** wallet version (the scripts use WalletContractV4) -5. Copy your V4R2 wallet address for receiving testnet funds +2. On the first run, you'll have to create a new wallet +3. Copy the 24-word recovery phrase by selecting the Gear icon then Backup +4. Select "Back Up Manually" and Continue to save the recovery phrase to a safe location. This is your `TON_MNEMONIC` +5. Go back to the main Wallet screen, select the wallet drop-down, and choose "Add Wallet" +6. Scroll to the bottom and select "Testnet Account" +7. Type in the 24-word recovery phrase you saved earlier and select "Continue" +8. Name the wallet "Testnet Wallet" and select Continue +9. Select the Gear icon and select V4R2 as the wallet version (this starter kit uses V4R2) +10. Save the `TON_MNEMONIC` to your `.env` file in quotes (e.g. `TON_MNEMONIC="..."`) + +### Get TON Center API Key + +To avoid rate limits when deploying and interacting with TON testnet, get a free API key: -> **Note**: TON Keeper shows multiple addresses (V3R1, V3R2, V4R2, W5) from the same mnemonic. Each wallet version has a different address. This starter kit uses **V4R2**. +1. Visit the TON Center API Bot: [@tonapibot](https://t.me/tonapibot) on Telegram +2. Send `/start` to the bot +3. Follow the instructions to get your free testnet API key +4. Add it to your `.env` file: `TON_API_KEY="your_api_key_here"` -## Getting Test Funds +Without an API key, you will encounter "429 Too Many Requests" errors during deployment. -### EVM Sepolia Faucets +### Getting Test Funds + +#### EVM Sepolia Faucets - [Chainlink Sepolia Faucet](https://faucets.chain.link/sepolia) - [Alchemy Sepolia Faucet](https://www.alchemy.com/faucets/ethereum-sepolia) - [Google Cloud Sepolia Faucet](https://cloud.google.com/application/web3/faucet/ethereum/sepolia) -### TON Testnet Faucets +#### TON Testnet Faucets - **Telegram Bot**: [@testgiver_ton_bot](https://t.me/testgiver_ton_bot) - Primary faucet for TON testnet - [Chainstack TON Faucet](https://faucet.chainstack.com/ton-testnet-faucet) -## Tutorial +## Setup -This tutorial will guide you through deploying receiver contracts and sending cross-chain messages in both directions. - -### Step 0: Deploy Receiver Contracts - -Before sending messages, you need to deploy receiver contracts on both chains. - -#### 0.1 Clone and Install +### Clone and Install ```bash git clone https://github.com/smartcontractkit/ton-starter-kit.git cd ton-starter-kit +git submodule update --init --recursive npm install ``` -#### 0.2 Configure Environment +### Configure Environment ```bash cp .env.example .env ``` -Edit `.env` and fill in the required values. All variables are documented with comments in `.env.example`. +Edit `.env` and fill in the required values: +- `SEPOLIA_PRIVATE_KEY` - Your EVM wallet private key +- `TON_MNEMONIC` - Your 24-word TON wallet mnemonic +- `TON_API_KEY` - Your TON Center API key to avoid rate limits -#### 0.3 Get Test Funds +Then source the `.env` file: -- **Sepolia ETH**: Visit [Chainlink Faucet](https://faucets.chain.link/sepolia) -- **TON Testnet**: Message [@testgiver_ton_bot](https://t.me/testgiver_ton_bot) on Telegram +```bash +source .env +``` + +## Tutorial + +This tutorial will guide you through deploying receiver contracts and sending cross-chain messages in both directions. -#### 0.4 Deploy EVM Receiver (Sepolia) +### Deploy Receiver Contracts + +Before sending messages, you need to deploy receiver contracts on both chains. + +#### Deploy EVM Receiver (Sepolia) **Using Hardhat (Recommended)** @@ -121,288 +92,71 @@ Edit `.env` and fill in the required values. All variables are documented with c npm run deploy:evm ``` -After deployment, add the contract address to your `.env` file as `EVM_RECEIVER_ADDRESS`. +After deployment, add the contract address to your `.env` file as `EVM_RECEIVER_ADDRESS` and source the `.env` file: -**Using Remix (Alternative)** - -1. Open [Remix IDE](https://remix.ethereum.org) -2. Create a new file `MessageReceiver.sol` and copy contents from `contracts/MessageReceiver.sol` -3. Compile with Solidity 0.8.24 -4. Deploy to Sepolia with constructor parameter: `0xEA5d21863357870Be141b318A4d4E9709Ac3F5ce` (Sepolia Router) -5. Add deployed address to `.env` as `EVM_RECEIVER_ADDRESS` +```bash +source .env +``` -#### 0.5 Deploy TON Receiver +#### Deploy TON Receiver ```bash npm run deploy:ton ``` -This will: -1. Compile the `MessageReceiver.tolk` contract -2. Deploy it to TON Testnet with the OffRamp address -3. Output the deployed contract address +After deployment, add the contract address to your `.env` file as `TON_RECEIVER_ADDRESS` and source the `.env` file: -After deployment, add the contract address to your `.env` file as `TON_RECEIVER_ADDRESS`. +```bash +source .env +``` **Verify on TON Explorer** View your deployed contract at: ``` -https://testnet.tonviewer.com/ +https://testnet.tonviewer.com/ ``` -**Note:** The TON deployment script requires: -- `TON_MNEMONIC` in your `.env` file -- At least 0.5 TON in your wallet for deployment gas fees - -### Step 1: Send Message from EVM to TON - -**Time Required:** ~20 minutes (including 15 minutes for CCIP delivery) - -**What You'll Need:** -- Sepolia ETH (at least 0.01 ETH for gas) -- TON receiver deployed (from Step 0.5) -- `TON_RECEIVER_ADDRESS` set in `.env` - -**Steps:** - -1. **Send a message from EVM to TON**: - ```bash - npm run evm2ton:send - ``` - - You will see output like: - ``` - ✅ Transaction confirmed in block: 9477779 - 📋 Message ID: 0x000... - ⏳ Expected delivery: 5-15 minutes - ``` - -2. **Wait for CCIP delivery** (5-15 minutes) - -3. **Check the message on TON**: - ```bash - npm run utils:checkTON - ``` - - Expected output: - ``` - ✅ Latest message received - 📋 Message ID: 0x000... - 📝 Data: Hello TON from EVM - ``` - -4. **Track on CCIP Explorer** (Optional): - - Use the **source EVM transaction hash** from step 1 to track delivery: - ``` - https://ccip.chain.link/tx/ - ``` +### Send Message from EVM to TON -> **NOTE**: Since end-to-end transaction time depends primarily on the time to finality on the source blockchain (Ethereum Sepolia in this case), it's recommended to wait 5-15 minutes before checking the message status. - -### Step 2: Send Message from TON to EVM - -**Time Required:** ~20 minutes (including 15 minutes for CCIP delivery) - -**What You'll Need:** -- TON testnet funds (at least 0.1 TON for sending messages) -- EVM receiver deployed (from Step 0.4) -- `EVM_RECEIVER_ADDRESS` set in `.env` - -**Steps:** +```bash +npm run evm2ton:send +``` -1. **Send a message from TON to EVM**: - ```bash - npm run ton2evm:send - ``` - - You will see output like: - ``` - ✅ Transaction submitted! - 📋 Hash: 0x397dac... - ⏳ Expected delivery: 5-15 minutes - ``` - -2. **Wait for CCIP delivery** (5-15 minutes) - -3. **Check the message on EVM**: - ```bash - npm run utils:checkEVM - ``` - - Expected output: - ``` - ✅ Latest message received - 📋 Message ID: 0x000... - 📝 Data: Hello EVM from TON - 🔗 Source: TON Testnet (1399300952838017768) - 🔗 TX Hash: 0xabc123... <- Use this for CCIP Explorer - ``` - -4. **Track on CCIP Explorer** (Optional): +#### Track on CCIP Explorer - For TON → EVM, use the **destination EVM transaction hash** from step 3: - ``` - https://ccip.chain.link/tx/ - ``` - - > **Note**: TON source transaction hash support on CCIP Explorer is coming soon. For now, run `npm run utils:checkEVM` to find the destination EVM transaction hash where the message was delivered. - -## Network Information - -### Staging Environment - -| Chain | Network | Chain Selector | Router Address | -|-------|---------|----------------|----------------| -| **EVM** | Sepolia Testnet | `16015286601757825753` | `0xEA5d21863357870Be141b318A4d4E9709Ac3F5ce` | -| **TON** | TON Testnet | `1399300952838017768` | `EQDrkhDYT8czFZuYNPlFMJ5ICD8FQoEW0b1KvITMVljC3ZTV` | - -### Additional Components - -| Component | EVM (Sepolia) | TON (Testnet) | -|-----------|---------------|---------------| -| **OnRamp** | `0xFB34b9969Dd201cc9A04E604a6D40AF917b6C1E8` | `EQDTIBzONmN64tMmLymf0-jtc_AAWfDlXiZcr7ja5ri7ak53` | -| **OffRamp** | `0x93Bb167Ebd91987f9Dff6B954b9Eead469d2b849` | `EQCfLpla6865euCU2-TPlzy8vKQKT8rFKHoAvorKBC1RudIO` | - -### Block Explorers -- **Sepolia**: https://sepolia.etherscan.io -- **TON Testnet**: https://testnet.tonviewer.com or https://testnet.tonscan.org - -## Token Transfers - -### TON → EVM Token Transfer - -> 🚧 **Coming Soon** - Token transfer functionality will be available in a future release. - -**Planned features:** -- Transfer CCIP-BnM test tokens from TON to EVM chains -- Support for multiple token types -- Atomic message + token transfers - -### EVM → TON Token Transfer - -> 🚧 **Coming Soon** - Token transfer functionality will be available in a future release. - -**Planned features:** -- Transfer CCIP-BnM test tokens from EVM chains to TON -- Bridge tokens across multiple EVM chains via TON -- Programmable token transfers with data - -## Fee Payments - -### Current: Native Token Payments (TON → EVM) - -Currently, fees for TON → EVM messages are paid in **native TON tokens**. The fee token address is: +Use the **source EVM transaction hash** to track delivery on CCIP Explorer: ``` -0:0000000000000000000000000000000000000000000000000000000000000001 +https://ccip.chain.link/ ``` -Message value starts at **0.05 TON** and should be increased for complex receiver logic. - -### Future: LINK Token Support +> **NOTE**: It may take up to 15 minutes for the message to be finalized. -> 🚧 **Coming Soon** - LINK token payment support will be available in a future release. +#### Check the message on TON -**Planned features:** -- Pay CCIP fees using LINK tokens on TON -- Consistent cross-chain fee payment experience -- Fee estimation tools for LINK payments - -## Key Implementation Details - -### TON → EVM Address Encoding - -EVM addresses must be **left-padded to 32 bytes**: - -```typescript -const addressBytes = Buffer.from(evmAddress.slice(2), 'hex'); // 20 bytes -const paddedAddress = Buffer.concat([ - Buffer.alloc(12, 0), // 12 zero bytes - addressBytes // 20-byte address -]); // Total: 32 bytes +```bash +npm run utils:checkTON ``` -### EVM → TON Address Encoding +### Send Message from TON to EVM -TON addresses must be **36 bytes total**: - -```typescript -// 4-byte workchain (int32, big-endian) + 32-byte account hash -const workchainBytes = new Uint8Array(4); -new DataView(workchainBytes.buffer).setInt32(0, tonAddr.workChain, false); -const receiverBytes = ethers.concat([ - workchainBytes, // 4 bytes - accountId // 32 bytes -]); // Total: 36 bytes +```bash +npm run ton2evm:send ``` -### ExtraArgs Encoding (TON → EVM) - -The `gasLimit` field is **optional** and must be encoded using the `storeMaybeUint` pattern: +#### Track on CCIP Explorer + +Use the **destination EVM transaction hash** to track delivery on CCIP Explorer: -```typescript -const extraArgs = beginCell() - .storeUint(0x181dcf10, 32) // Version tag for GenericExtraArgsV2 - .storeBit(true) // ✅ "gasLimit is present" bit - .storeUint(1000000, 256) // gasLimit value - .storeBit(true) // allowOutOfOrderExecution - .endCell(); ``` - -**Critical:** Always include `.storeBit(true)` before storing the optional `gasLimit` value. Omitting this causes **exit code 9 (Cell Underflow)**. - -### ExtraArgs Encoding (EVM → TON) - -For TON destinations, `allowOutOfOrderExecution` **must be `true`**: - -```typescript -const extraArgs = ethers.AbiCoder.defaultAbiCoder().encode( - ['uint256', 'bool'], - [1000000, true] // ✅ MUST be true for TON -); +https://ccip.chain.link/ ``` -## Troubleshooting - -### Common Issues - -| Chain | Symptom | Cause | Fix | -|-------|---------|-------|-----| -| **EVM** | "Insufficient fee" revert | Gas limit too low or fee estimation failed | Increase `gasLimit` in `extraArgs`; Add 10-20% buffer to fee quote | -| **EVM** | Message not received | Invalid receiver address format | Verify 36-byte encoding for TON addresses (4-byte workchain + 32-byte hash) | -| **TON** | Exit code 9 (Cell Underflow) | Malformed Cell structure or missing "maybe" bit | Check `extraArgs` encoding: ensure `.storeBit(true)` before optional `gasLimit` | -| **TON** | Exit code 258 (FeeTokenNotSupported) | Wrong fee token address | Use `0:0000...0001` (NOT `...0000`) | -| **TON** | Exit code 0xFFFF (Wrong Opcode) | Message sent to wrong contract | Verify Router and receiver addresses | -| **Both** | Delayed delivery (>15 min) | Staging environment slow processing | Wait up to 20-30 minutes; Contact Chainlink Labs if persistent | +> **NOTE**: It may take up to 15 minutes for the message to be finalized. -## Configuration Reference +#### Check the message on EVM -All network constants are defined in `config/constants.ts`: - -```typescript -export const SEPOLIA_CHAIN_SELECTOR = 16015286601757825753n; -export const TON_CHAIN_SELECTOR = 1399300952838017768n; - -export const SEPOLIA_ROUTER = '0xEA5d21863357870Be141b318A4d4E9709Ac3F5ce'; -export const TON_ROUTER = 'EQDrkhDYT8czFZuYNPlFMJ5ICD8FQoEW0b1KvITMVljC3ZTV'; - -export const TON_FEE_TOKEN = '0:0000000000000000000000000000000000000000000000000000000000000001'; +```bash +npm run utils:checkEVM ``` - -## Resources - -- [Chainlink CCIP Documentation](https://docs.chain.link/ccip) -- [TON Documentation](https://docs.ton.org) -- [@ton/core NPM Package](https://www.npmjs.com/package/@ton/core) -- [TON Utils Go Library](https://github.com/xssnick/tonutils-go) -- [ethers.js Documentation](https://docs.ethers.org) - -## License - -MIT - -## Disclaimer - -This tutorial represents an educational example to use a Chainlink system, product, or service and is provided to demonstrate how to interact with Chainlink's systems, products, and services to integrate them into your own. This template is provided "AS IS" and "AS AVAILABLE" without warranties of any kind, it has not been audited, and it may be missing key checks or error handling to make the usage of the system, product or service more clear. Do not use the code in this example in a production environment without completing your own audits and application of best practices. Neither Chainlink Labs, the Chainlink Foundation, nor Chainlink node operators are responsible for unintended outputs that are generated due to errors in code. diff --git a/config/constants.ts b/config/constants.ts index 46881a8..4d41509 100644 --- a/config/constants.ts +++ b/config/constants.ts @@ -27,8 +27,19 @@ export const AVALANCHE_FUJI = { EXPLORER: 'https://testnet.snowtrace.io', }; +const TON_RPC_URL = (() => { + let url = process.env.TON_RPC_URL || 'https://testnet.toncenter.com/api/v2/jsonRPC'; + const apiKey = process.env.TON_API_KEY; + + if (apiKey && !url.includes('api_key=')) { + url += `${url.includes('?') ? '&' : '?'}api_key=${apiKey}`; + } + + return url; +})(); + export const TON_TESTNET = { - RPC_URL: process.env.TON_RPC_URL!, + RPC_URL: TON_RPC_URL, ROUTER: process.env.TON_ROUTER!, OFFRAMP: process.env.TON_OFFRAMP!, CHAIN_SELECTOR: BigInt(process.env.TON_CHAIN_SELECTOR!), diff --git a/package.json b/package.json index 5b31e61..720dec7 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,5 @@ { - "name": "ccip-ton-typescript-test", - "version": "1.0.0", - "description": "TypeScript examples for cross-chain messaging between TON and EVM chains using Chainlink CCIP", - "main": "index.js", + "name": "ccip-ton-starter-kit", "scripts": { "deploy:evm": "npx hardhat run scripts/deploy/deployReceiver.ts --network sepolia", "deploy:ton": "tsx scripts/deploy/deployTONReceiver.ts", @@ -11,12 +8,8 @@ "utils:checkEVM": "tsx scripts/utils/checkMessageOnEVM.ts", "utils:checkTON": "tsx scripts/utils/checkMessageOnTON.ts" }, - "keywords": [], - "author": "", - "license": "ISC", - "type": "module", "dependencies": { - "@chainlink/contracts-ccip": "^1.6.2", + "@chainlink/contracts-ccip": "1.6.2", "@openzeppelin/contracts": "^5.0.2", "@ton/core": "^0.62.0", "@ton/crypto": "^3.3.0", diff --git a/scripts/deploy/deployTONReceiver.ts b/scripts/deploy/deployTONReceiver.ts index f57ad7c..3d18d77 100644 --- a/scripts/deploy/deployTONReceiver.ts +++ b/scripts/deploy/deployTONReceiver.ts @@ -14,8 +14,8 @@ async function main() { // TON OffRamp address from staging environment const TON_OFFRAMP = TON_TESTNET.OFFRAMP; - // Connect to TON - const endpoint = process.env.TON_RPC_URL || 'https://testnet.toncenter.com/api/v2/jsonRPC'; + // Connect to TON (API key is automatically included if TON_API_KEY is set in .env) + const endpoint = TON_TESTNET.RPC_URL; const client = new TonClient({ endpoint }); // Load wallet from mnemonic diff --git a/scripts/evm2ton/sendMessage.ts b/scripts/evm2ton/sendMessage.ts index b3e4967..a5b6c94 100644 --- a/scripts/evm2ton/sendMessage.ts +++ b/scripts/evm2ton/sendMessage.ts @@ -44,13 +44,6 @@ async function sendEVMToTON() { // Chain selector from Network Information const destChainSelector = TON_TESTNET.CHAIN_SELECTOR const tonReceiverAddr = CONTRACTS.TON_RECEIVER - - console.log('📍 Router:', SEPOLIA.ROUTER) - console.log('📍 TON Receiver:', tonReceiverAddr) - console.log('📍 Destination: TON Testnet (', destChainSelector.toString(), ')\n') - - // Convert TON address to bytes - console.log('🔧 Encoding TON receiver address...') const tonAddr = Address.parse(tonReceiverAddr) // FROM chainlink-ton/pkg/ccip/codec/addresscodec.go: @@ -65,23 +58,14 @@ async function sendEVMToTON() { workchainBytes, // 4 bytes for workchain (int32) accountId // 32 bytes for address hash ]) - console.log('✅ TON address encoded:', ethers.hexlify(receiverBytes).slice(0, 20) + '...') - // Simple text message data - console.log('🔧 Building message data...') const messageData = ethers.toUtf8Bytes('Hello TON from EVM') - console.log('✅ Message data built') - - // "EVM > TON Schema" - GenericExtraArgsV2 - // Must be TRUE for TON destinations - console.log('🔧 Building extraArgs...') const abiCoder = ethers.AbiCoder.defaultAbiCoder() const extraArgs = abiCoder.encode( ['uint256', 'bool'], [100_000_000, true] // gasLimit in nanoTON (0.1 TON = 100,000,000 nanoTON), allowOutOfOrderExecution ) const extraArgsWithTag = ethers.concat(['0x181dcf10', extraArgs]) // GENERIC_EXTRA_ARGS_V2_TAG - console.log('✅ ExtraArgs built\n') const message = { receiver: receiverBytes, @@ -91,17 +75,8 @@ async function sendEVMToTON() { extraArgs: extraArgsWithTag } - // "EVM Fee Estimation" - console.log('💰 Getting fee quote...') const fee = await router.getFee(destChainSelector, message) - console.log(' Fee:', ethers.formatEther(fee), 'ETH') - - // "Add 10-20% buffer for safety" const feeWithBuffer = (fee * 110n) / 100n - console.log(' Fee with buffer:', ethers.formatEther(feeWithBuffer), 'ETH\n') - - // Send message - console.log('📤 Sending transaction...') const tx = await router.ccipSend(destChainSelector, message, { value: feeWithBuffer }) diff --git a/scripts/ton2evm/sendMessage.ts b/scripts/ton2evm/sendMessage.ts index fd015d6..dfbbb32 100644 --- a/scripts/ton2evm/sendMessage.ts +++ b/scripts/ton2evm/sendMessage.ts @@ -45,38 +45,19 @@ async function sendTONToEVM() { const destChainSelector = SEPOLIA.CHAIN_SELECTOR const evmReceiverAddr = CONTRACTS.EVM_RECEIVER const feeToken = Address.parseRaw('0:0000000000000000000000000000000000000000000000000000000000000001') - - console.log('📍 Router (from 1-pager):', routerAddress.toString()) - console.log('📍 EVM Receiver:', evmReceiverAddr) - console.log('📍 Destination Chain:', 'Sepolia (', destChainSelector.toString(), ')') - console.log('📍 Following 1-pager TypeScript example EXACTLY\n') - - // Build ExtraArgs - EXACTLY as per 1-pager TypeScript example - console.log('🔧 Building ExtraArgs (1-pager format)...') const extraArgs = beginCell() .storeUint(0x181dcf10, 32) // GenericExtraArgsV2 tag .storeBit(true) // gasLimit IS present .storeUint(1_000_000, 256) // gasLimit value (EVM gas units) .storeBit(true) // allowOutOfOrderExecution .endCell() - console.log('✅ ExtraArgs built') - // Build message data - EXACTLY as per 1-pager - console.log('🔧 Building message data...') - const data = beginCell() + const data = beginCell() .storeStringTail('Hello EVM from TON') .endCell() - console.log('✅ Message data built') - - // Prepare receiver: left-pad to 32 bytes - EXACTLY as per 1-pager - console.log('🔧 Encoding receiver address...') + const addrBytes = Buffer.from(evmReceiverAddr.slice(2), 'hex') const padded = Buffer.concat([Buffer.alloc(12, 0), addrBytes]) - console.log(' Padded length:', padded.length, 'bytes') - console.log('✅ Receiver address encoded\n') - - // Build CCIPSend message - EXACTLY as per 1-pager TypeScript example - console.log('🔧 Building CCIPSend message (1-pager format)...') const ccipSend = beginCell() .storeUint(0x31768d95, 32) // CCIPSend opcode .storeUint(0, 64) // queryID @@ -88,11 +69,6 @@ async function sendTONToEVM() { .storeAddress(feeToken) // feeToken (native TON) .storeRef(extraArgs) // extraArgs .endCell() - console.log('✅ CCIPSend message constructed\n') - - // Send transaction - EXACTLY as per 1-pager - // Value should cover CCIP fee + gas. Use Router_GetValidatedFee for exact fee. - console.log('📤 Sending transaction to Router...') const seqno = await walletContract.getSeqno() await walletContract.sendTransfer({ @@ -106,15 +82,8 @@ async function sendTONToEVM() { }) ] }) - + console.log('✅ Transaction sent!\n') - console.log('📋 Test Details:') - console.log(' Following: 1-pager TypeScript example EXACTLY') - console.log(' Opcode: 0x31768d95 (Router_CCIPSend)') - console.log(' Destination: Router (not wrapped, direct)') - console.log(' Structure: As documented in 1-pager\n') - console.log('⏳ Message is being processed by CCIP network...') - console.log('⏳ Expected delivery: 5-15 minutes (staging environment)\n') console.log('🔍 Monitor your transaction:') console.log(` ${TON_TESTNET.EXPLORER}/${wallet.address.toString()}\n`) console.log('🔍 Monitor delivery on Sepolia:')