-
Notifications
You must be signed in to change notification settings - Fork 44
/
tokenBridge.ts
167 lines (142 loc) · 5.9 KB
/
tokenBridge.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
import type { Chain, Network, TokenId } from "@wormhole-foundation/sdk";
import { TokenTransfer, Wormhole, amount, isTokenId, wormhole } from "@wormhole-foundation/sdk";
// Import the platform-specific packages
import evm from "@wormhole-foundation/sdk/evm";
import solana from "@wormhole-foundation/sdk/solana";
import type { SignerStuff } from "./helpers/index.js";
import { getSigner, waitLog } from "./helpers/index.js";
(async function () {
// Init Wormhole object, passing config for which network
// to use (e.g. Mainnet/Testnet) and what Platforms to support
const wh = await wormhole("Testnet", [evm, solana]);
// Grab chain Contexts -- these hold a reference to a cached rpc client
const sendChain = wh.getChain("Avalanche");
const rcvChain = wh.getChain("Solana");
// Shortcut to allow transferring native gas token
const token = Wormhole.tokenId(sendChain.chain, "native");
// A TokenId is just a `{chain, address}` pair and an alias for ChainAddress
// The `address` field must be a parsed address.
// You can get a TokenId (or ChainAddress) prepared for you
// by calling the static `chainAddress` method on the Wormhole class.
// e.g.
// wAvax on Solana
// const token = Wormhole.tokenId("Solana", "3Ftc5hTz9sG4huk79onufGiebJNDMZNL8HYgdMJ9E7JR");
// wSol on Avax
// const token = Wormhole.tokenId("Avalanche", "0xb10563644a6AB8948ee6d7f5b0a1fb15AaEa1E03");
// Normalized given token decimals later but can just pass bigints as base units
// Note: The Token bridge will dedust past 8 decimals
// this means any amount specified past that point will be returned
// to the caller
const amt = "0.05";
// With automatic set to true, perform an automatic transfer. This will invoke a relayer
// contract intermediary that knows to pick up the transfers
// With automatic set to false, perform a manual transfer from source to destination
// of the token
// On the destination side, a wrapped version of the token will be minted
// to the address specified in the transfer VAA
const automatic = false;
// The automatic relayer has the ability to deliver some native gas funds to the destination account
// The amount specified for native gas will be swapped for the native gas token according
// to the swap rate provided by the contract, denominated in native gas tokens
const nativeGas = automatic ? "0.01" : undefined;
// Get signer from local key but anything that implements
// Signer interface (e.g. wrapper around web wallet) should work
const source = await getSigner(sendChain);
const destination = await getSigner(rcvChain);
// Used to normalize the amount to account for the tokens decimals
const decimals = isTokenId(token)
? Number(await wh.getDecimals(token.chain, token.address))
: sendChain.config.nativeTokenDecimals;
// Set this to true if you want to perform a round trip transfer
const roundTrip: boolean = false;
// Set this to the transfer txid of the initiating transaction to recover a token transfer
// and attempt to fetch details about its progress.
let recoverTxid = undefined;
// recoverTxid = "0xa4e0a2c1c994fe3298b5646dfd5ce92596dc1a589f42e241b7f07501a5a5a39f";
// Finally create and perform the transfer given the parameters set above
const xfer = !recoverTxid
? // Perform the token transfer
await tokenTransfer(
wh,
{
token,
amount: amount.units(amount.parse(amt, decimals)),
source,
destination,
delivery: {
automatic,
nativeGas: nativeGas ? amount.units(amount.parse(nativeGas, decimals)) : undefined,
},
},
roundTrip,
)
: // Recover the transfer from the originating txid
await TokenTransfer.from(wh, {
chain: source.chain.chain,
txid: recoverTxid,
});
const receipt = await waitLog(wh, xfer);
// Log out the results
console.log(receipt);
})();
async function tokenTransfer<N extends Network>(
wh: Wormhole<N>,
route: {
token: TokenId;
amount: bigint;
source: SignerStuff<N, Chain>;
destination: SignerStuff<N, Chain>;
delivery?: {
automatic: boolean;
nativeGas?: bigint;
};
payload?: Uint8Array;
},
roundTrip?: boolean,
): Promise<TokenTransfer<N>> {
// EXAMPLE_TOKEN_TRANSFER
// Create a TokenTransfer object to track the state of the transfer over time
const xfer = await wh.tokenTransfer(
route.token,
route.amount,
route.source.address,
route.destination.address,
route.delivery?.automatic ?? false,
route.payload,
route.delivery?.nativeGas,
);
const quote = await TokenTransfer.quoteTransfer(
wh,
route.source.chain,
route.destination.chain,
xfer.transfer,
);
console.log(quote);
if (xfer.transfer.automatic && quote.destinationToken.amount < 0)
throw "The amount requested is too low to cover the fee and any native gas requested.";
// 1) Submit the transactions to the source chain, passing a signer to sign any txns
console.log("Starting transfer");
const srcTxids = await xfer.initiateTransfer(route.source.signer);
console.log(`Started transfer: `, srcTxids);
// If automatic, we're done
if (route.delivery?.automatic) return xfer;
// 2) Wait for the VAA to be signed and ready (not required for auto transfer)
console.log("Getting Attestation");
const attestIds = await xfer.fetchAttestation(60_000);
console.log(`Got Attestation: `, attestIds);
// 3) Redeem the VAA on the dest chain
console.log("Completing Transfer");
const destTxids = await xfer.completeTransfer(route.destination.signer);
console.log(`Completed Transfer: `, destTxids);
// EXAMPLE_TOKEN_TRANSFER
// If no need to send back, dip
if (!roundTrip) return xfer;
const { destinationToken: token } = quote;
return await tokenTransfer(wh, {
...route,
token: token.token,
amount: token.amount,
source: route.destination,
destination: route.source,
});
}