Skip to content

Commit ff79dbb

Browse files
authored
sdk: Multi Transceiver Support SDK TS changes (#535)
* Update Ntt and NttTransceiver interfaces to be more generic * Update SolanaNtt to store a list of Program<NttBindings.Transceiver<IdlVersion>>. This mimics EvmNtt which stores list of EvmNttWormholeTranceiver. * Reserve transceiver at index 0 for Wormhole transceiver in Solana and EVM * Refactor transceiver specific functions into SolanaNttWormholeTransceiver * Add Wormhole-specific wrapper methods and anchor-idl 3.0.0 bindings for backwards compatibility
1 parent 350f989 commit ff79dbb

File tree

25 files changed

+3336
-3110
lines changed

25 files changed

+3336
-3110
lines changed

cli/src/index.ts

+5-6
Original file line numberDiff line numberDiff line change
@@ -675,7 +675,7 @@ yargs(hideBin(process.argv))
675675
await signSendWait(ctx, tx, signer.signer)
676676
}
677677
for (const transceiver of missingConfig.transceiverPeers) {
678-
const tx = ntt.setWormholeTransceiverPeer(transceiver, signer.address.address)
678+
const tx = ntt.setTransceiverPeer(0, transceiver, signer.address.address)
679679
await signSendWait(ctx, tx, signer.signer)
680680
}
681681
for (const evmChain of missingConfig.evmChains) {
@@ -696,10 +696,9 @@ yargs(hideBin(process.argv))
696696
continue;
697697
}
698698
const solanaNtt = ntt as SolanaNtt<Network, SolanaChains>;
699-
const tx = solanaNtt.registerTransceiver({
699+
const tx = solanaNtt.registerWormholeTransceiver({
700700
payer: signer.address.address as AccountAddress<SolanaChains>,
701701
owner: signer.address.address as AccountAddress<SolanaChains>,
702-
transceiver: solanaNtt.program.programId
703702
})
704703
try {
705704
await signSendWait(ctx, tx, signer.signer)
@@ -1283,7 +1282,7 @@ async function deploySolana<N extends Network, C extends SolanaChains>(
12831282
// time by checking it here and failing early (not to mention better
12841283
// diagnostics).
12851284

1286-
const emitter = NTT.pdas(providedProgramId).emitterAccount().toBase58();
1285+
const emitter = NTT.transceiverPdas(providedProgramId).emitterAccount().toBase58();
12871286
const payerKeypair = Keypair.fromSecretKey(new Uint8Array(JSON.parse(fs.readFileSync(payer).toString())));
12881287

12891288
// this is not super pretty... I want to initialise the 'ntt' object, but
@@ -1779,7 +1778,7 @@ async function getPdas<N extends Network, C extends Chain>(chain: C, ntt: Ntt<N,
17791778
}
17801779
const solanaNtt = ntt as SolanaNtt<N, SolanaChains>;
17811780
const config = solanaNtt.pdas.configAccount();
1782-
const emitter = solanaNtt.pdas.emitterAccount();
1781+
const emitter = NTT.transceiverPdas(solanaNtt.program.programId).emitterAccount();
17831782
const outboxRateLimit = solanaNtt.pdas.outboxRateLimitAccount();
17841783
const tokenAuthority = solanaNtt.pdas.tokenAuthority();
17851784
const lutAccount = solanaNtt.pdas.lutAccount();
@@ -1818,7 +1817,7 @@ async function nttFromManager<N extends Network, C extends Chain>(
18181817
ntt: {
18191818
manager: nativeManagerAddress,
18201819
token: null,
1821-
transceiver: { wormhole: null },
1820+
transceiver: {},
18221821
}
18231822
});
18241823
const diff = await onlyManager.verifyAddresses();

evm/ts/src/ntt.ts

+104-41
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ import {
1515
toUniversal,
1616
universalAddress,
1717
} from "@wormhole-foundation/sdk-definitions";
18-
import type { AnyEvmAddress, EvmChains, EvmPlatformType } from "@wormhole-foundation/sdk-evm";
18+
import type {
19+
AnyEvmAddress,
20+
EvmChains,
21+
EvmPlatformType,
22+
} from "@wormhole-foundation/sdk-evm";
1923
import {
2024
EvmAddress,
2125
EvmPlatform,
@@ -26,6 +30,7 @@ import {
2630
import "@wormhole-foundation/sdk-evm-core";
2731

2832
import {
33+
EvmNttTransceiver,
2934
Ntt,
3035
NttTransceiver,
3136
WormholeNttTransceiver,
@@ -39,7 +44,10 @@ import {
3944
} from "./bindings.js";
4045

4146
export class EvmNttWormholeTranceiver<N extends Network, C extends EvmChains>
42-
implements NttTransceiver<N, C, WormholeNttTransceiver.VAA> {
47+
implements
48+
WormholeNttTransceiver<N, C>,
49+
EvmNttTransceiver<N, C, WormholeNttTransceiver.VAA>
50+
{
4351
transceiver: NttTransceiverBindings.NttTransceiver;
4452
constructor(
4553
readonly manager: EvmNtt<N, C>,
@@ -52,15 +60,26 @@ export class EvmNttWormholeTranceiver<N extends Network, C extends EvmChains>
5260
);
5361
}
5462

63+
async getTransceiverType(): Promise<string> {
64+
// NOTE: We hardcode the type here as transceiver type is only available for versions >1.1.0
65+
// For those versions, we can return `this.transceiver.getTransceiverType()` directly
66+
return "wormhole";
67+
}
68+
5569
getAddress(): ChainAddress<C> {
56-
return { chain: this.manager.chain, address: toUniversal(this.manager.chain, this.address) };
70+
return {
71+
chain: this.manager.chain,
72+
address: toUniversal(this.manager.chain, this.address),
73+
};
5774
}
5875

5976
encodeFlags(flags: { skipRelay: boolean }): Uint8Array {
6077
return new Uint8Array([flags.skipRelay ? 1 : 0]);
6178
}
6279

63-
async *setPeer<P extends Chain>(peer: ChainAddress<P>): AsyncGenerator<EvmUnsignedTransaction<N, C>> {
80+
async *setPeer<P extends Chain>(
81+
peer: ChainAddress<P>
82+
): AsyncGenerator<EvmUnsignedTransaction<N, C>> {
6483
const tx = await this.transceiver.setWormholePeer.populateTransaction(
6584
toChainId(peer.chain),
6685
universalAddress(peer)
@@ -74,8 +93,14 @@ export class EvmNttWormholeTranceiver<N extends Network, C extends EvmChains>
7493
}
7594

7695
async *setPauser(pauser: AccountAddress<C>) {
77-
const canonicalPauser = canonicalAddress({chain: this.manager.chain, address: pauser});
78-
const tx = await this.transceiver.transferPauserCapability.populateTransaction(canonicalPauser);
96+
const canonicalPauser = canonicalAddress({
97+
chain: this.manager.chain,
98+
address: pauser,
99+
});
100+
const tx =
101+
await this.transceiver.transferPauserCapability.populateTransaction(
102+
canonicalPauser
103+
);
79104
yield this.manager.createUnsignedTx(tx, "WormholeTransceiver.setPauser");
80105
}
81106

@@ -102,7 +127,10 @@ export class EvmNttWormholeTranceiver<N extends Network, C extends EvmChains>
102127
toChainId(chain),
103128
isEvm
104129
);
105-
yield this.manager.createUnsignedTx(tx, "WormholeTransceiver.setIsEvmChain");
130+
yield this.manager.createUnsignedTx(
131+
tx,
132+
"WormholeTransceiver.setIsEvmChain"
133+
);
106134
}
107135

108136
async *receive(attestation: WormholeNttTransceiver.VAA) {
@@ -122,10 +150,11 @@ export class EvmNttWormholeTranceiver<N extends Network, C extends EvmChains>
122150
}
123151

124152
async *setIsWormholeRelayingEnabled(destChain: Chain, enabled: boolean) {
125-
const tx = await this.transceiver.setIsWormholeRelayingEnabled.populateTransaction(
126-
toChainId(destChain),
127-
enabled
128-
);
153+
const tx =
154+
await this.transceiver.setIsWormholeRelayingEnabled.populateTransaction(
155+
toChainId(destChain),
156+
enabled
157+
);
129158
yield this.manager.createUnsignedTx(
130159
tx,
131160
"WormholeTransceiver.setWormholeRelayingEnabled"
@@ -139,10 +168,11 @@ export class EvmNttWormholeTranceiver<N extends Network, C extends EvmChains>
139168
}
140169

141170
async *setIsSpecialRelayingEnabled(destChain: Chain, enabled: boolean) {
142-
const tx = await this.transceiver.setIsSpecialRelayingEnabled.populateTransaction(
143-
toChainId(destChain),
144-
enabled
145-
);
171+
const tx =
172+
await this.transceiver.setIsSpecialRelayingEnabled.populateTransaction(
173+
toChainId(destChain),
174+
enabled
175+
);
146176
yield this.manager.createUnsignedTx(
147177
tx,
148178
"WormholeTransceiver.setSpecialRelayingEnabled"
@@ -151,7 +181,8 @@ export class EvmNttWormholeTranceiver<N extends Network, C extends EvmChains>
151181
}
152182

153183
export class EvmNtt<N extends Network, C extends EvmChains>
154-
implements Ntt<N, C> {
184+
implements Ntt<N, C>
185+
{
155186
tokenAddress: string;
156187
readonly chainId: bigint;
157188
manager: NttManagerBindings.NttManager;
@@ -182,17 +213,32 @@ export class EvmNtt<N extends Network, C extends EvmChains>
182213
this.provider
183214
);
184215

185-
if (contracts.ntt.transceiver.wormhole != null) {
186-
this.xcvrs = [
187-
// Enable more Transceivers here
188-
new EvmNttWormholeTranceiver(
189-
this,
190-
contracts.ntt.transceiver.wormhole,
191-
abiBindings!
192-
),
216+
this.xcvrs = [];
217+
if (
218+
"wormhole" in contracts.ntt.transceiver &&
219+
contracts.ntt.transceiver["wormhole"]
220+
) {
221+
const transceiverTypes = [
222+
"wormhole", // wormhole xcvr should be ix 0
223+
...Object.keys(contracts.ntt.transceiver).filter((transceiverType) => {
224+
transceiverType !== "wormhole";
225+
}),
193226
];
194-
} else {
195-
this.xcvrs = [];
227+
transceiverTypes.map((transceiverType) => {
228+
// we currently only support wormhole transceivers
229+
if (transceiverType !== "wormhole") {
230+
throw new Error(`Unsupported transceiver type: ${transceiverType}`);
231+
}
232+
233+
// Enable more Transceivers here
234+
this.xcvrs.push(
235+
new EvmNttWormholeTranceiver(
236+
this,
237+
contracts.ntt!.transceiver[transceiverType]!,
238+
abiBindings!
239+
)
240+
);
241+
});
196242
}
197243
}
198244

@@ -211,12 +257,12 @@ export class EvmNtt<N extends Network, C extends EvmChains>
211257
}
212258

213259
async *pause() {
214-
const tx = await this.manager.pause.populateTransaction()
260+
const tx = await this.manager.pause.populateTransaction();
215261
yield this.createUnsignedTx(tx, "Ntt.pause");
216262
}
217263

218264
async *unpause() {
219-
const tx = await this.manager.unpause.populateTransaction()
265+
const tx = await this.manager.unpause.populateTransaction();
220266
yield this.createUnsignedTx(tx, "Ntt.unpause");
221267
}
222268

@@ -230,13 +276,17 @@ export class EvmNtt<N extends Network, C extends EvmChains>
230276

231277
async *setOwner(owner: AnyEvmAddress) {
232278
const canonicalOwner = new EvmAddress(owner).toString();
233-
const tx = await this.manager.transferOwnership.populateTransaction(canonicalOwner);
279+
const tx = await this.manager.transferOwnership.populateTransaction(
280+
canonicalOwner
281+
);
234282
yield this.createUnsignedTx(tx, "Ntt.setOwner");
235283
}
236284

237285
async *setPauser(pauser: AnyEvmAddress) {
238286
const canonicalPauser = new EvmAddress(pauser).toString();
239-
const tx = await this.manager.transferPauserCapability.populateTransaction(canonicalPauser);
287+
const tx = await this.manager.transferPauserCapability.populateTransaction(
288+
canonicalPauser
289+
);
240290
yield this.createUnsignedTx(tx, "Ntt.setPauser");
241291
}
242292

@@ -398,9 +448,14 @@ export class EvmNtt<N extends Network, C extends EvmChains>
398448
}
399449

400450
async *setWormholeTransceiverPeer(peer: ChainAddress<C>) {
401-
// TODO: we only have one right now, so just set the peer on that one
402-
// in the future, these should(?) be keyed by attestation type
403-
yield* this.xcvrs[0]!.setPeer(peer);
451+
yield* this.setTransceiverPeer(0, peer);
452+
}
453+
454+
async *setTransceiverPeer(ix: number, peer: ChainAddress<C>) {
455+
if (ix >= this.xcvrs.length) {
456+
throw new Error("Transceiver not found");
457+
}
458+
yield* this.xcvrs[ix]!.setPeer(peer);
404459
}
405460

406461
async *transfer(
@@ -475,7 +530,9 @@ export class EvmNtt<N extends Network, C extends EvmChains>
475530
}
476531

477532
async getOutboundLimit(): Promise<bigint> {
478-
const encoded: EncodedTrimmedAmount = (await this.manager.getOutboundLimitParams()).limit;
533+
const encoded: EncodedTrimmedAmount = (
534+
await this.manager.getOutboundLimitParams()
535+
).limit;
479536
const trimmedAmount: TrimmedAmount = decodeTrimmedAmount(encoded);
480537
const tokenDecimals = await this.getTokenDecimals();
481538

@@ -492,7 +549,9 @@ export class EvmNtt<N extends Network, C extends EvmChains>
492549
}
493550

494551
async getInboundLimit(fromChain: Chain): Promise<bigint> {
495-
const encoded: EncodedTrimmedAmount = (await this.manager.getInboundLimitParams(toChainId(fromChain))).limit;
552+
const encoded: EncodedTrimmedAmount = (
553+
await this.manager.getInboundLimitParams(toChainId(fromChain))
554+
).limit;
496555
const trimmedAmount: TrimmedAmount = decodeTrimmedAmount(encoded);
497556
const tokenDecimals = await this.getTokenDecimals();
498557

@@ -547,7 +606,7 @@ export class EvmNtt<N extends Network, C extends EvmChains>
547606
manager: this.managerAddress,
548607
token: this.tokenAddress,
549608
transceiver: {
550-
wormhole: this.xcvrs[0]?.address,
609+
...(this.xcvrs.length > 0 && { wormhole: this.xcvrs[0]!.address }),
551610
},
552611
// TODO: what about the quoter?
553612
};
@@ -556,7 +615,7 @@ export class EvmNtt<N extends Network, C extends EvmChains>
556615
manager: this.managerAddress,
557616
token: await this.manager.token(),
558617
transceiver: {
559-
wormhole: (await this.manager.getTransceivers())[0]! // TODO: make this more generic
618+
wormhole: (await this.manager.getTransceivers())[0]!, // TODO: make this more generic
560619
},
561620
};
562621

@@ -569,7 +628,7 @@ export class EvmNtt<N extends Network, C extends EvmChains>
569628
delete a[k];
570629
}
571630
}
572-
}
631+
};
573632

574633
deleteMatching(remote, local);
575634

@@ -612,14 +671,18 @@ function untrim(trimmed: TrimmedAmount, toDecimals: number): bigint {
612671
return scale(amount, fromDecimals, toDecimals);
613672
}
614673

615-
function scale(amount: bigint, fromDecimals: number, toDecimals: number): bigint {
674+
function scale(
675+
amount: bigint,
676+
fromDecimals: number,
677+
toDecimals: number
678+
): bigint {
616679
if (fromDecimals == toDecimals) {
617680
return amount;
618681
}
619682

620683
if (fromDecimals > toDecimals) {
621-
return amount / (10n ** BigInt(fromDecimals - toDecimals));
684+
return amount / 10n ** BigInt(fromDecimals - toDecimals);
622685
} else {
623-
return amount * (10n ** BigInt(toDecimals - fromDecimals));
686+
return amount * 10n ** BigInt(toDecimals - fromDecimals);
624687
}
625688
}

0 commit comments

Comments
 (0)