Skip to content
This repository was archived by the owner on Jun 16, 2025. It is now read-only.

Commit a544b5f

Browse files
committed
re-use existing CCTP serde, actually deserialize created VAA to correct type
1 parent f1bc1a7 commit a544b5f

File tree

13 files changed

+196
-293
lines changed

13 files changed

+196
-293
lines changed

solana/ts/src/cctp/messageTransmitter/index.ts

Lines changed: 5 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,7 @@ import { IDL, MessageTransmitter } from "../types/message_transmitter";
66
import { MessageSent } from "./MessageSent";
77
import { MessageTransmitterConfig } from "./MessageTransmitterConfig";
88
import { UsedNonses } from "./UsedNonces";
9-
10-
export const PROGRAM_IDS = ["CCTPmbSD7gX1bxKPAmg77w8oFzNFpaQiQUWD43TKaecd"] as const;
11-
12-
export type ProgramId = (typeof PROGRAM_IDS)[number] | string;
9+
import { CircleContracts } from "@wormhole-foundation/sdk-base/contracts";
1310

1411
export type ReceiveTokenMessengerMinterMessageAccounts = {
1512
authority: PublicKey;
@@ -28,15 +25,11 @@ export type ReceiveTokenMessengerMinterMessageAccounts = {
2825
};
2926

3027
export class MessageTransmitterProgram {
31-
private _programId: ProgramId;
32-
3328
program: Program<MessageTransmitter>;
3429

35-
constructor(connection: Connection, programId?: ProgramId) {
36-
this._programId = programId ?? testnet();
37-
this.program = new Program(IDL, new PublicKey(this._programId), {
38-
connection,
39-
});
30+
constructor(connection: Connection, private contracts: CircleContracts) {
31+
const programId = new PublicKey(contracts.messageTransmitter);
32+
this.program = new Program(IDL, new PublicKey(programId), { connection });
4033
}
4134

4235
get ID(): PublicKey {
@@ -71,23 +64,7 @@ export class MessageTransmitterProgram {
7164
}
7265

7366
tokenMessengerMinterProgram(): TokenMessengerMinterProgram {
74-
switch (this._programId) {
75-
case testnet(): {
76-
return new TokenMessengerMinterProgram(
77-
this.program.provider.connection,
78-
"CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3",
79-
);
80-
}
81-
case mainnet(): {
82-
return new TokenMessengerMinterProgram(
83-
this.program.provider.connection,
84-
"CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3",
85-
);
86-
}
87-
default: {
88-
throw new Error("unsupported network");
89-
}
90-
}
67+
return new TokenMessengerMinterProgram(this.program.provider.connection, this.contracts);
9168
}
9269

9370
receiveTokenMessengerMinterMessageAccounts(
@@ -133,11 +110,3 @@ export class MessageTransmitterProgram {
133110
.instruction();
134111
}
135112
}
136-
137-
export function mainnet(): ProgramId {
138-
return "CCTPmbSD7gX1bxKPAmg77w8oFzNFpaQiQUWD43TKaecd";
139-
}
140-
141-
export function testnet(): ProgramId {
142-
return "CCTPmbSD7gX1bxKPAmg77w8oFzNFpaQiQUWD43TKaecd";
143-
}

solana/ts/src/cctp/messages.ts

Lines changed: 52 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ethers } from "ethers";
1+
import { CircleBridge, UniversalAddress } from "@wormhole-foundation/sdk-definitions";
22

33
export type Cctp = {
44
version: number;
@@ -13,9 +13,9 @@ export type Cctp = {
1313
// Taken from https://developers.circle.com/stablecoins/docs/message-format.
1414
export class CctpMessage {
1515
cctp: Cctp;
16-
message: Buffer;
16+
message: CctpTokenBurnMessage;
1717

18-
constructor(cctp: Cctp, message: Buffer) {
18+
constructor(cctp: Cctp, message: CctpTokenBurnMessage) {
1919
this.cctp = cctp;
2020
this.message = message;
2121
}
@@ -30,31 +30,60 @@ export class CctpMessage {
3030

3131
static decode(buf: Readonly<Buffer>): CctpMessage {
3232
const version = buf.readUInt32BE(0);
33-
const sourceDomain = buf.readUInt32BE(4);
34-
const destinationDomain = buf.readUInt32BE(8);
35-
const nonce = buf.readBigUInt64BE(12);
36-
const sender = Array.from(buf.slice(20, 52));
37-
const recipient = Array.from(buf.slice(52, 84));
38-
const targetCaller = Array.from(buf.slice(84, 116));
39-
const message = buf.subarray(116);
33+
34+
const [msg] = CircleBridge.deserialize(new Uint8Array(buf));
35+
const {
36+
sourceDomain,
37+
destinationDomain,
38+
nonce,
39+
sender,
40+
recipient,
41+
destinationCaller,
42+
payload,
43+
} = msg;
44+
45+
const { burnToken, mintRecipient, amount, messageSender } = payload;
46+
const header: Cctp = {
47+
version,
48+
sourceDomain,
49+
destinationDomain,
50+
nonce,
51+
sender: Array.from(sender.toUint8Array()),
52+
recipient: Array.from(recipient.toUint8Array()),
53+
targetCaller: Array.from(destinationCaller.toUint8Array()),
54+
};
4055

4156
return new CctpMessage(
42-
{
57+
header,
58+
new CctpTokenBurnMessage(
59+
header,
4360
version,
44-
sourceDomain,
45-
destinationDomain,
46-
nonce,
47-
sender,
48-
recipient,
49-
targetCaller,
50-
},
51-
message,
61+
Array.from(burnToken.toUint8Array()),
62+
Array.from(mintRecipient.toUint8Array()),
63+
amount,
64+
Array.from(messageSender.toUint8Array()),
65+
),
5266
);
5367
}
5468

5569
encode(): Buffer {
5670
const { cctp, message } = this;
57-
return Buffer.concat([encodeCctp(cctp), message]);
71+
return Buffer.from(
72+
CircleBridge.serialize({
73+
sourceDomain: cctp.sourceDomain,
74+
destinationDomain: cctp.destinationDomain,
75+
nonce: cctp.nonce,
76+
sender: new UniversalAddress(new Uint8Array(cctp.sender)),
77+
recipient: new UniversalAddress(new Uint8Array(cctp.recipient)),
78+
destinationCaller: new UniversalAddress(new Uint8Array(cctp.targetCaller)),
79+
payload: {
80+
burnToken: new UniversalAddress(new Uint8Array(message.burnTokenAddress)),
81+
mintRecipient: new UniversalAddress(new Uint8Array(message.mintRecipient)),
82+
amount: message.amount,
83+
messageSender: new UniversalAddress(new Uint8Array(message.sender)),
84+
},
85+
}),
86+
);
5887
}
5988
}
6089

@@ -91,63 +120,11 @@ export class CctpTokenBurnMessage {
91120
}
92121

93122
static decode(buf: Readonly<Buffer>): CctpTokenBurnMessage {
94-
const { cctp, message } = CctpMessage.decode(buf);
95-
const version = message.readUInt32BE(0);
96-
const burnTokenAddress = Array.from(message.subarray(4, 36));
97-
const mintRecipient = Array.from(message.subarray(36, 68));
98-
const amount = BigInt(ethers.BigNumber.from(message.subarray(68, 100)).toString());
99-
const sender = Array.from(message.subarray(100, 132));
100-
101-
return new CctpTokenBurnMessage(
102-
cctp,
103-
version,
104-
burnTokenAddress,
105-
mintRecipient,
106-
amount,
107-
sender,
108-
);
123+
const { message } = CctpMessage.decode(buf);
124+
return message;
109125
}
110126

111127
encode(): Buffer {
112-
const buf = Buffer.alloc(132);
113-
114-
const { cctp, version, burnTokenAddress, mintRecipient, amount, sender } = this;
115-
116-
let offset = 0;
117-
offset = buf.writeUInt32BE(version, offset);
118-
buf.set(burnTokenAddress, offset);
119-
offset += 32;
120-
buf.set(mintRecipient, offset);
121-
offset += 32;
122-
123-
// Special handling w/ uint256. This value will most likely encoded in < 32 bytes, so we
124-
// jump ahead by 32 and subtract the length of the encoded value.
125-
const encodedAmount = ethers.utils.arrayify(ethers.BigNumber.from(amount.toString()));
126-
buf.set(encodedAmount, (offset += 32) - encodedAmount.length);
127-
128-
buf.set(sender, offset);
129-
offset += 32;
130-
131-
return Buffer.concat([encodeCctp(cctp), buf]);
128+
return new CctpMessage(this.cctp, this).encode();
132129
}
133130
}
134-
135-
function encodeCctp(cctp: Cctp): Buffer {
136-
const buf = Buffer.alloc(116);
137-
138-
const { version, sourceDomain, destinationDomain, nonce, sender, recipient, targetCaller } =
139-
cctp;
140-
141-
let offset = 0;
142-
offset = buf.writeUInt32BE(version, offset);
143-
offset = buf.writeUInt32BE(sourceDomain, offset);
144-
offset = buf.writeUInt32BE(destinationDomain, offset);
145-
offset = buf.writeBigUInt64BE(nonce, offset);
146-
buf.set(sender, offset);
147-
offset += 32;
148-
buf.set(recipient, offset);
149-
offset += 32;
150-
buf.set(targetCaller, offset);
151-
152-
return buf;
153-
}

solana/ts/src/cctp/tokenMessengerMinter/index.ts

Lines changed: 6 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
1-
import { Program } from "anchor-0.29.0";
21
import { Connection, PublicKey } from "@solana/web3.js";
2+
import { CircleContracts } from "@wormhole-foundation/sdk-base/contracts";
3+
import { Program } from "anchor-0.29.0";
34
import { MessageTransmitterProgram } from "../messageTransmitter";
45
import { IDL, TokenMessengerMinter } from "../types/token_messenger_minter";
56
import { RemoteTokenMessenger } from "./RemoteTokenMessenger";
67

7-
export const PROGRAM_IDS = ["CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3"] as const;
8-
9-
export type ProgramId = (typeof PROGRAM_IDS)[number] | string;
10-
118
export type DepositForBurnWithCallerAccounts = {
129
senderAuthority: PublicKey;
1310
messageTransmitterConfig: PublicKey;
@@ -21,15 +18,11 @@ export type DepositForBurnWithCallerAccounts = {
2118
};
2219

2320
export class TokenMessengerMinterProgram {
24-
private _programId: ProgramId;
25-
2621
program: Program<TokenMessengerMinter>;
2722

28-
constructor(connection: Connection, programId?: ProgramId) {
29-
this._programId = programId ?? testnet();
30-
this.program = new Program(IDL, new PublicKey(this._programId), {
31-
connection,
32-
});
23+
constructor(connection: Connection, private contracts: CircleContracts) {
24+
const programId = new PublicKey(contracts.tokenMessenger);
25+
this.program = new Program(IDL, programId, { connection });
3326
}
3427

3528
get ID(): PublicKey {
@@ -89,23 +82,7 @@ export class TokenMessengerMinterProgram {
8982
}
9083

9184
messageTransmitterProgram(): MessageTransmitterProgram {
92-
switch (this._programId) {
93-
case testnet(): {
94-
return new MessageTransmitterProgram(
95-
this.program.provider.connection,
96-
"CCTPmbSD7gX1bxKPAmg77w8oFzNFpaQiQUWD43TKaecd",
97-
);
98-
}
99-
case mainnet(): {
100-
return new MessageTransmitterProgram(
101-
this.program.provider.connection,
102-
"CCTPmbSD7gX1bxKPAmg77w8oFzNFpaQiQUWD43TKaecd",
103-
);
104-
}
105-
default: {
106-
throw new Error("unsupported network");
107-
}
108-
}
85+
return new MessageTransmitterProgram(this.program.provider.connection, this.contracts);
10986
}
11087

11188
depositForBurnWithCallerAccounts(
@@ -126,11 +103,3 @@ export class TokenMessengerMinterProgram {
126103
};
127104
}
128105
}
129-
130-
export function mainnet(): ProgramId {
131-
return "CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3";
132-
}
133-
134-
export function testnet(): ProgramId {
135-
return "CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3";
136-
}

solana/ts/src/matchingEngine/index.ts

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -68,39 +68,30 @@ import {
6868
ReserveFastFillSequenceCompositeOpts,
6969
} from "./types";
7070

71-
export const PROGRAM_IDS = [
72-
"MatchingEngine11111111111111111111111111111",
73-
"mPydpGUWxzERTNpyvTKdvS7v8kvw5sgwfiP8WQFrXVS",
74-
] as const;
75-
export type ProgramId = (typeof PROGRAM_IDS)[number] | string;
71+
export * from "./types";
7672

7773
export const FEE_PRECISION_MAX = 1_000_000n;
7874

7975
export const CPI_EVENT_IX_SELECTOR = Uint8Array.from([228, 69, 165, 46, 81, 203, 154, 29]);
8076

8177
export class MatchingEngineProgram {
82-
private _programId: ProgramId;
8378
private _mint: PublicKey;
8479
private _custodian?: Custodian;
8580

8681
pdas: ReturnType<typeof programDerivedAddresses>;
8782

8883
program: Program<MatchingEngineType>;
8984

90-
constructor(
91-
connection: Connection,
92-
programId: ProgramId,
93-
private _addresses: MatchingEngine.Addresses,
94-
) {
95-
this._programId = programId;
85+
constructor(connection: Connection, private _addresses: MatchingEngine.Addresses) {
86+
const programId = _addresses.matchingEngine;
9687
this._mint = new PublicKey(_addresses.cctp.usdcMint);
9788
this.pdas = programDerivedAddresses(
9889
new PublicKey(programId),
9990
this._mint,
10091
this.coreBridgeProgramId,
10192
);
10293
this.program = new Program(
103-
{ ...(IDL as any), address: this._programId },
94+
{ ...(IDL as any), address: programId },
10495
{
10596
connection,
10697
},
@@ -2237,24 +2228,23 @@ export class MatchingEngineProgram {
22372228
}
22382229

22392230
upgradeManagerProgram(): UpgradeManagerProgram {
2240-
return new UpgradeManagerProgram(
2241-
this.program.provider.connection,
2242-
this._addresses.upgradeManager!,
2243-
{ ...this._addresses, tokenRouter: this._addresses.tokenRouter! },
2244-
);
2231+
return new UpgradeManagerProgram(this.program.provider.connection, {
2232+
...this._addresses,
2233+
tokenRouter: this._addresses.tokenRouter!,
2234+
});
22452235
}
22462236

22472237
tokenMessengerMinterProgram(): TokenMessengerMinterProgram {
22482238
return new TokenMessengerMinterProgram(
22492239
this.program.provider.connection,
2250-
this._addresses.cctp.tokenMessenger,
2240+
this._addresses.cctp,
22512241
);
22522242
}
22532243

22542244
messageTransmitterProgram(): MessageTransmitterProgram {
22552245
return new MessageTransmitterProgram(
22562246
this.program.provider.connection,
2257-
this._addresses.cctp.messageTransmitter,
2247+
this._addresses.cctp,
22582248
);
22592249
}
22602250

solana/ts/src/protocol/matchingEngine.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import {
2929
SolanaUnsignedTransaction,
3030
} from "@wormhole-foundation/sdk-solana";
3131
import { vaaHash } from "../common";
32-
import { AuctionParameters, MatchingEngineProgram, ProgramId } from "../matchingEngine";
32+
import { AuctionParameters, MatchingEngineProgram } from "../matchingEngine";
3333
import { SolanaWormholeCore } from "@wormhole-foundation/sdk-solana-core";
3434

3535
export class SolanaMatchingEngine<N extends Network, C extends SolanaChains>
@@ -44,7 +44,7 @@ export class SolanaMatchingEngine<N extends Network, C extends SolanaChains>
4444
readonly _connection: Connection,
4545
readonly _contracts: Contracts & MatchingEngine.Addresses,
4646
) {
47-
super(_connection, _contracts.matchingEngine, _contracts);
47+
super(_connection, _contracts);
4848

4949
this.coreBridge = new SolanaWormholeCore(_network, _chain, _connection, {
5050
...this._contracts,

0 commit comments

Comments
 (0)