Skip to content

Commit d01ef48

Browse files
evgenikonik-suri
authored andcommitted
Create a demo repository for an NTT transfer using the Wormhole TS SDK
1 parent 7a77ae0 commit d01ef48

12 files changed

+7240
-1
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/node_modules

LICENCE

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Copyright 2022 Wormhole Project Contributors
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.

README.md

+86-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,86 @@
1-
# demo-ntt-ts-sdk
1+
2+
# NTT deployment testing with Wormhole TS-SDK
3+
4+
## Overview
5+
6+
This project demonstrates the use of the Wormhole TS-SDK to facilitate token transfers between different blockchain networks, after performing a deployment of the [Native Token Transfer](https://docs.wormhole.com/wormhole/native-token-transfers/overview) framework. Before running the script, you need to set up the necessary configurations and provide your deployment details.
7+
8+
## Prerequisites
9+
10+
Ensure you have the following installed on your system:
11+
12+
- Node.js & TypeScript
13+
- npm or yarn
14+
15+
## Setup
16+
17+
1. **Clone the Repository:**
18+
19+
```bash
20+
git clone https://github.com/wormhole-foundation/demo-ntt-ts-sdk.git
21+
cd /demo-ntt-ts-sdk
22+
```
23+
24+
2. **Install Dependencies:**
25+
26+
```bash
27+
npm install
28+
```
29+
30+
or
31+
32+
```bash
33+
yarn
34+
```
35+
36+
3. **Update Configuration:**
37+
38+
- **Reference `deployment.json`:**
39+
40+
The `example-deployment.json` file contains an example deployment file for your blockchain networks. You should have a similar file in your project after going through the an NTT [deployment](https://docs.wormhole.com/wormhole/native-token-transfers/deployment/installation)
41+
42+
- **Update `const.ts`:**
43+
44+
Update the `TEST_NTT_TOKENS` object in the `const.ts` file with your token, manager, and transceiver details from the `deployment.json` file:
45+
46+
```typescript
47+
export const TEST_NTT_SPL22_TOKENS: NttContracts = {
48+
Solana: {
49+
token: "NTTSolanaTokenAddress",
50+
manager: "NTTSolanaManagerAddress",
51+
transceiver: {
52+
wormhole: "NTTSolanaTransceiverAddress",
53+
},
54+
},
55+
BaseSepolia: {
56+
token: "NTTBaseSepoliaTokenAddress",
57+
manager: "NTTBaseSepoliaManagerAddress",
58+
transceiver: { wormhole: "NTTBaseSepoliaTransceiverAddress" },
59+
},
60+
};
61+
```
62+
63+
- **Set Private Keys:**
64+
65+
You need to set your Ethereum and Solana private keys for this example. You can either set the env variables `ETH_PRIVATE_KEY` and `SOL_PRIVATE_KEY` OR replace this constants:
66+
67+
```typescript
68+
export const DEVNET_SOL_PRIVATE_KEY = encoding.b58.encode(
69+
new Uint8Array(
70+
[218, 95 /* ... rest of the key */]
71+
)
72+
);
73+
74+
export const DEVNET_ETH_PRIVATE_KEY =
75+
"0xYourEthereumPrivateKey";
76+
```
77+
78+
## Running the Script
79+
80+
```bash
81+
npx ts-node index.ts
82+
```
83+
84+
**Finality delay:**
85+
86+
When executing the script, you may see log messages like *Retrying Wormholescan:GetVaaByTxHash, attempt 100/750*. This is expected due to the time required for the source blockchain, like Ethereum, to reach finality, which can take up to 15 minutes. The Wormhole guardian network needs this time to produce a valid attestation (VAA). The retry attempts ensure the transaction is fully confirmed and secure before proceeding.

SECURITY.md

+194
Large diffs are not rendered by default.

const.ts

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { Ntt } from "@wormhole-foundation/sdk-definitions-ntt";
2+
import { Chain, encoding } from "@wormhole-foundation/sdk";
3+
4+
export type NttContracts = {
5+
[key in Chain]?: Ntt.Contracts;
6+
};
7+
8+
export const DEVNET_SOL_PRIVATE_KEY = encoding.b58.encode(
9+
new Uint8Array(
10+
[218,95 //.. rest of the key
11+
])
12+
);
13+
export const DEVNET_ETH_PRIVATE_KEY =
14+
"0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d"; // Ganache default private key
15+
16+
export const TEST_NTT_TOKENS: NttContracts = {
17+
Solana: {
18+
token: "5trJHKSB7M6w1sC74YkxZb5D7GxA9bL6WzP4ht8FDs5V",
19+
manager: "NTueGPu3ckEwiQXprSjAfHC7YybrJNAG39X2AKEG9So",
20+
transceiver: {
21+
wormhole: "NTueGPu3ckEwiQXprSjAfHC7YybrJNAG39X2AKEG9So",
22+
},
23+
},
24+
BaseSepolia: {
25+
token: "0xaBc1234567890fDb48D63F11dFdc364201C9DE67",
26+
manager: "0xD456789a1230Cc48fDb48D63F11dFdc364201C9DE",
27+
transceiver: { wormhole: "0x9876aBcDeF01234567890Fdb48D63F11dFdc3642" },
28+
},
29+
};

example-deployment.json

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
{
2+
"network": "Testnet",
3+
"chains": {
4+
"BaseSepolia": {
5+
"version": "1.1.0",
6+
"mode": "burning",
7+
"paused": false,
8+
"owner": "0xAB12CD34EF56GH78IJ90KL12MN34OP56QR78ST90",
9+
"manager": "0x98BA7C65F43210EDCB8765F4A3E9876C12AB56CD",
10+
"token": "0x1234567890abcdef1234567890abcdef12345678",
11+
"transceivers": {
12+
"threshold": 1,
13+
"wormhole": {
14+
"address": "0xabcdef1234567890abcdef1234567890abcdef12",
15+
"pauser": "0xAB12CD34EF56GH78IJ90KL12MN34OP56QR78ST90"
16+
}
17+
},
18+
"limits": {
19+
"outbound": "184467440737.095516150000000000",
20+
"inbound": {
21+
"Solana": "1000.000000000000000000"
22+
}
23+
},
24+
"pauser": "0xAB12CD34EF56GH78IJ90KL12MN34OP56QR78ST90"
25+
},
26+
"Solana": {
27+
"version": "2.0.0",
28+
"mode": "burning",
29+
"paused": false,
30+
"owner": "4d6f2E8F3d7F1e2Cd7c67D36fE4c1A678C8e8E3F",
31+
"manager": "7gHdE5F2w3D5e67F4C5f3eE9d1eD8C7eA2f9cD7F",
32+
"token": "6f7gH4D5f6c3D2e6a7e4F8G3d2e7c4F5d3F6b5eC",
33+
"transceivers": {
34+
"threshold": 1,
35+
"wormhole": {
36+
"address": "B3Cd6eF7G4c2E8d7A5f6F4C3eD7F4B6cE8d3f6G7"
37+
}
38+
},
39+
"limits": {
40+
"outbound": "1000.100000000",
41+
"inbound": {
42+
"BaseSepolia": "1000.000000000"
43+
}
44+
}
45+
}
46+
}
47+
}

helpers.ts

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import {
2+
Chain,
3+
ChainAddress,
4+
ChainContext,
5+
Network,
6+
Signer,
7+
Wormhole,
8+
chainToPlatform,
9+
10+
} from "@wormhole-foundation/sdk";
11+
12+
import evm from "@wormhole-foundation/sdk/platforms/evm";
13+
import solana from "@wormhole-foundation/sdk/platforms/solana";
14+
import { NttContracts, DEVNET_SOL_PRIVATE_KEY, DEVNET_ETH_PRIVATE_KEY, TEST_NTT_TOKENS} from "./const";
15+
import { NttRoute } from "@wormhole-foundation/sdk-route-ntt";
16+
17+
export interface SignerStuff<N extends Network, C extends Chain> {
18+
chain: ChainContext<N, C>;
19+
signer: Signer<N, C>;
20+
address: ChainAddress<C>;
21+
}
22+
23+
export async function getSigner<N extends Network, C extends Chain>(
24+
chain: ChainContext<N, C>
25+
): Promise<SignerStuff<N, C>> {
26+
// Read in from `.env`
27+
(await import("dotenv")).config();
28+
29+
let signer: Signer;
30+
const platform = chainToPlatform(chain.chain);
31+
switch (platform) {
32+
case "Solana":
33+
signer = await solana.getSigner(
34+
await chain.getRpc(),
35+
getEnv("OTHER_SOL_PRIVATE_KEY", DEVNET_SOL_PRIVATE_KEY),
36+
{ debug: false }
37+
);
38+
break;
39+
case "Evm":
40+
signer = await evm.getSigner(
41+
await chain.getRpc(),
42+
getEnv("ETH_PRIVATE_KEY", DEVNET_ETH_PRIVATE_KEY)
43+
);
44+
break;
45+
default:
46+
throw new Error("Unrecognized platform: " + platform);
47+
}
48+
49+
return {
50+
chain,
51+
signer: signer as Signer<N, C>,
52+
address: Wormhole.chainAddress(chain.chain, signer.address()),
53+
};
54+
}
55+
56+
// Use .env.example as a template for your .env file and populate it with secrets
57+
// for funded accounts on the relevant chain+network combos to run the example
58+
function getEnv(key: string, dev?: string): string {
59+
// If we're in the browser, return empty string
60+
if (typeof process === undefined) return "";
61+
// Otherwise, return the env var or error
62+
const val = process.env[key];
63+
if (!val) {
64+
if (dev) return dev;
65+
throw new Error(
66+
`Missing env var ${key}, did you forget to set values in '.env'?`
67+
);
68+
}
69+
70+
return val;
71+
}
72+
73+
// Reformat NTT contracts to fit TokenConfig for Route
74+
function reformat(contracts: NttContracts) {
75+
return Object.entries(TEST_NTT_TOKENS).map(([chain, contracts]) => {
76+
const { token, manager, transceiver: xcvrs } = contracts!;
77+
const transceiver = Object.entries(xcvrs).map(([k, v]) => {
78+
return { type: k as NttRoute.TransceiverType, address: v };
79+
});
80+
return { chain: chain as Chain, token, manager, transceiver };
81+
});
82+
}
83+
84+
export const NttTokens = {
85+
Test: reformat(TEST_NTT_TOKENS),
86+
};

index.ts

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import {
2+
TransactionId,
3+
Wormhole,
4+
amount,
5+
signSendWait,
6+
} from "@wormhole-foundation/sdk";
7+
import evm from "@wormhole-foundation/sdk/platforms/evm";
8+
import solana from "@wormhole-foundation/sdk/platforms/solana";
9+
10+
// register protocol implementations
11+
import "@wormhole-foundation/sdk-evm-ntt";
12+
import "@wormhole-foundation/sdk-solana-ntt";
13+
import { TEST_NTT_TOKENS } from "./const";
14+
import { getSigner } from "./helpers";
15+
16+
17+
(async function () {
18+
const wh = new Wormhole("Testnet", [solana.Platform, evm.Platform]);
19+
const src = wh.getChain("BaseSepolia");
20+
const dst = wh.getChain("Solana");
21+
22+
const srcSigner = await getSigner(src);
23+
const dstSigner = await getSigner(dst);
24+
25+
const srcNtt = await src.getProtocol("Ntt", {
26+
ntt: TEST_NTT_TOKENS[src.chain],
27+
});
28+
const dstNtt = await dst.getProtocol("Ntt", {
29+
ntt: TEST_NTT_TOKENS[dst.chain],
30+
});
31+
32+
const amt = amount.units(
33+
amount.parse("0.01", await srcNtt.getTokenDecimals())
34+
);
35+
36+
const xfer = () =>
37+
srcNtt.transfer(srcSigner.address.address, amt, dstSigner.address, {
38+
queue: false,
39+
automatic: false,
40+
gasDropoff: 0n,
41+
});
42+
43+
// Initiate the transfer (or set to recoverTxids to complete transfer)
44+
const txids: TransactionId[] = await signSendWait(src, xfer(), srcSigner.signer);
45+
console.log("Source txs", txids);
46+
47+
const vaa = await wh.getVaa(
48+
txids[txids.length - 1]!.txid,
49+
"Ntt:WormholeTransfer",
50+
25 * 60 * 1000
51+
);
52+
console.log(vaa);
53+
54+
const dstTxids = await signSendWait(
55+
dst,
56+
dstNtt.redeem([vaa!], dstSigner.address.address),
57+
dstSigner.signer
58+
);
59+
console.log("dstTxids", dstTxids);
60+
})();

0 commit comments

Comments
 (0)