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

Commit 739b4f6

Browse files
committed
Start to add protocol definition
1 parent 3b08683 commit 739b4f6

File tree

11 files changed

+923
-397
lines changed

11 files changed

+923
-397
lines changed

solana/ts/src/matchingEngine/index.ts

Lines changed: 62 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ import {
5454
ReservedFastFillSequence,
5555
RouterEndpoint,
5656
} from "./state";
57-
import { ChainId, toChainId, isChainId } from "@wormhole-foundation/sdk-base";
57+
import { ChainId, toChainId, isChainId, Chain } from "@wormhole-foundation/sdk-base";
58+
import { programDerivedAddresses } from "./pdas";
5859

5960
export const PROGRAM_IDS = [
6061
"MatchingEngine11111111111111111111111111111",
@@ -218,12 +219,16 @@ export type ReserveFastFillSequenceCompositeOpts = {
218219
export class MatchingEngineProgram {
219220
private _programId: ProgramId;
220221
private _mint: PublicKey;
222+
private _custodian?: Custodian;
223+
224+
pdas: ReturnType<typeof programDerivedAddresses>;
221225

222226
program: Program<MatchingEngine>;
223227

224228
constructor(connection: Connection, programId: ProgramId, mint: PublicKey) {
225229
this._programId = programId;
226230
this._mint = mint;
231+
this.pdas = programDerivedAddresses(new PublicKey(programId), mint);
227232
this.program = new Program(
228233
{ ...(IDL as any), address: this._programId },
229234
{
@@ -346,21 +351,16 @@ export class MatchingEngineProgram {
346351
return this.program.addEventListener("fastFillRedeemed", callback);
347352
}
348353

349-
eventAuthorityAddress(): PublicKey {
350-
return PublicKey.findProgramAddressSync([Buffer.from("__event_authority")], this.ID)[0];
351-
}
352-
353-
custodianAddress(): PublicKey {
354-
return Custodian.address(this.ID);
354+
/** Get the cached Custodian if it exists, otherwise fetch the latest and cache it */
355+
async getCustodian(skipCache?: boolean): Promise<Custodian> {
356+
if (this._custodian === undefined || skipCache)
357+
this._custodian = await this.fetchCustodian();
358+
return this._custodian!;
355359
}
356360

357-
async fetchCustodian(input?: { address: PublicKey }): Promise<Custodian> {
358-
const addr = input === undefined ? this.custodianAddress() : input.address;
359-
return this.program.account.custodian.fetch(addr);
360-
}
361-
362-
auctionConfigAddress(id: number): PublicKey {
363-
return AuctionConfig.address(this.ID, id);
361+
/** Fetch the latest custodian data */
362+
async fetchCustodian(): Promise<Custodian> {
363+
return this.program.account.custodian.fetch(this.custodianAddress());
364364
}
365365

366366
async fetchAuctionConfig(input: number | { address: PublicKey }): Promise<AuctionConfig> {
@@ -370,20 +370,12 @@ export class MatchingEngineProgram {
370370

371371
async fetchAuctionParameters(id?: number): Promise<AuctionParameters> {
372372
if (id === undefined) {
373-
const { auctionConfigId } = await this.fetchCustodian();
373+
const { auctionConfigId } = await this.getCustodian();
374374
id = auctionConfigId;
375375
}
376376
return this.fetchAuctionConfig(id).then((config) => config.parameters);
377377
}
378378

379-
cctpMintRecipientAddress(): PublicKey {
380-
return splToken.getAssociatedTokenAddressSync(this.mint, this.custodianAddress(), true);
381-
}
382-
383-
routerEndpointAddress(chain: ChainId): PublicKey {
384-
return RouterEndpoint.address(this.ID, chain);
385-
}
386-
387379
async fetchRouterEndpoint(input: ChainId | { address: PublicKey }): Promise<RouterEndpoint> {
388380
const addr =
389381
typeof input == "object" && "address" in input
@@ -397,39 +389,18 @@ export class MatchingEngineProgram {
397389
return info;
398390
}
399391

400-
auctionAddress(vaaHash: VaaHash): PublicKey {
401-
return Auction.address(this.ID, vaaHash);
402-
}
403-
404392
async fetchAuction(input: VaaHash | { address: PublicKey }): Promise<Auction> {
405393
const addr = "address" in input ? input.address : this.auctionAddress(input);
406394
// @ts-ignore This is BS. This is correct.
407395
return this.program.account.auction.fetch(addr);
408396
}
409397

410-
async proposalAddress(proposalId?: Uint64): Promise<PublicKey> {
411-
if (proposalId === undefined) {
412-
const { nextProposalId } = await this.fetchCustodian();
413-
proposalId = nextProposalId;
414-
}
415-
416-
return Proposal.address(this.ID, proposalId);
417-
}
418-
419398
async fetchProposal(input?: { address: PublicKey }): Promise<Proposal> {
420399
const addr = input === undefined ? await this.proposalAddress() : input.address;
421400
// @ts-ignore This is BS. This is correct.
422401
return this.program.account.proposal.fetch(addr);
423402
}
424403

425-
coreMessageAddress(auction: PublicKey): PublicKey {
426-
return coreMessageAddress(this.ID, auction);
427-
}
428-
429-
cctpMessageAddress(auction: PublicKey): PublicKey {
430-
return cctpMessageAddress(this.ID, auction);
431-
}
432-
433404
async reclaimCctpMessageIx(
434405
accounts: {
435406
payer: PublicKey;
@@ -440,48 +411,20 @@ export class MatchingEngineProgram {
440411
return reclaimCctpMessageIx(this.messageTransmitterProgram(), accounts, cctpAttestation);
441412
}
442413

443-
preparedOrderResponseAddress(fastVaaHash: VaaHash): PublicKey {
444-
return PreparedOrderResponse.address(this.ID, fastVaaHash);
445-
}
446-
447414
async fetchPreparedOrderResponse(
448415
input: VaaHash | { address: PublicKey },
449416
): Promise<PreparedOrderResponse> {
450417
const addr = "address" in input ? input.address : this.preparedOrderResponseAddress(input);
451418
return this.program.account.preparedOrderResponse.fetch(addr);
452419
}
453420

454-
preparedCustodyTokenAddress(preparedOrderResponse: PublicKey): PublicKey {
455-
return PublicKey.findProgramAddressSync(
456-
[Buffer.from("prepared-custody"), preparedOrderResponse.toBuffer()],
457-
this.ID,
458-
)[0];
459-
}
460-
461-
auctionCustodyTokenAddress(auction: PublicKey): PublicKey {
462-
return PublicKey.findProgramAddressSync(
463-
[Buffer.from("auction-custody"), auction.toBuffer()],
464-
this.ID,
465-
)[0];
466-
}
467-
468421
async fetchAuctionCustodyTokenBalance(auction: PublicKey): Promise<bigint> {
469422
return splToken
470423
.getAccount(this.program.provider.connection, this.auctionCustodyTokenAddress(auction))
471424
.then((token) => token.amount)
472425
.catch((_) => 0n);
473426
}
474427

475-
localCustodyTokenAddress(sourceChain: ChainId): PublicKey {
476-
const encodedSourceChain = Buffer.alloc(2);
477-
encodedSourceChain.writeUInt16BE(sourceChain);
478-
479-
return PublicKey.findProgramAddressSync(
480-
[Buffer.from("local-custody"), encodedSourceChain],
481-
this.ID,
482-
)[0];
483-
}
484-
485428
async fetchLocalCustodyTokenBalance(sourceChain: ChainId): Promise<bigint> {
486429
return splToken
487430
.getAccount(
@@ -492,32 +435,20 @@ export class MatchingEngineProgram {
492435
.catch((_) => 0n);
493436
}
494437

495-
fastFillAddress(sourceChain: ChainId, orderSender: Array<number>, sequence: Uint64): PublicKey {
496-
return FastFill.address(this.ID, sourceChain, orderSender, sequence);
497-
}
498-
499438
fetchFastFill(
500439
input: [ChainId, Array<number>, Uint64] | { address: PublicKey },
501440
): Promise<FastFill> {
502441
const addr = "address" in input ? input.address : this.fastFillAddress(...input);
503442
return this.program.account.fastFill.fetch(addr);
504443
}
505444

506-
fastFillSequencerAddress(sourceChain: ChainId, sender: Array<number>): PublicKey {
507-
return FastFillSequencer.address(this.ID, sourceChain, sender);
508-
}
509-
510445
fetchFastFillSequencer(
511446
input: [ChainId, Array<number>] | { address: PublicKey },
512447
): Promise<FastFillSequencer> {
513448
const addr = "address" in input ? input.address : this.fastFillSequencerAddress(...input);
514449
return this.program.account.fastFillSequencer.fetch(addr);
515450
}
516451

517-
reservedFastFillSequenceAddress(fastVaaHash: VaaHash): PublicKey {
518-
return ReservedFastFillSequence.address(this.ID, fastVaaHash);
519-
}
520-
521452
fetchReservedFastFillSequence(
522453
input: VaaHash | { address: PublicKey },
523454
): Promise<ReservedFastFillSequence> {
@@ -526,19 +457,6 @@ export class MatchingEngineProgram {
526457
return this.program.account.reservedFastFillSequence.fetch(addr);
527458
}
528459

529-
transferAuthorityAddress(auction: PublicKey, offerPrice: Uint64): PublicKey {
530-
const encodedOfferPrice = Buffer.alloc(8);
531-
writeUint64BE(encodedOfferPrice, offerPrice);
532-
return PublicKey.findProgramAddressSync(
533-
[Buffer.from("transfer-authority"), auction.toBuffer(), encodedOfferPrice],
534-
this.ID,
535-
)[0];
536-
}
537-
538-
auctionHistoryAddress(id: Uint64): PublicKey {
539-
return AuctionHistory.address(this.ID, id);
540-
}
541-
542460
// Anchor is having trouble deserializing the account data here. Manually deserializing
543461
// the auction history is a workaround, and necessary after changing the redeemer message
544462
// length from a u32 to a u16.
@@ -1134,7 +1052,7 @@ export class MatchingEngineProgram {
11341052
proposal ??= await this.proposalAddress(opts.proposalId);
11351053

11361054
if (auctionConfig === undefined) {
1137-
const { auctionConfigId } = await this.fetchCustodian();
1055+
const { auctionConfigId } = await this.getCustodian(true);
11381056
// Add 1 to the current auction config ID to get the next one.
11391057
auctionConfig = this.auctionConfigAddress(auctionConfigId + 1);
11401058
}
@@ -1290,8 +1208,8 @@ export class MatchingEngineProgram {
12901208
async placeInitialOfferCctpIx(
12911209
accounts: {
12921210
payer: PublicKey;
1293-
feePayer?: PublicKey;
12941211
fastVaa: PublicKey;
1212+
feePayer?: PublicKey;
12951213
offerToken?: PublicKey;
12961214
auction?: PublicKey;
12971215
auctionConfig?: PublicKey;
@@ -1306,10 +1224,9 @@ export class MatchingEngineProgram {
13061224
[approveIx: TransactionInstruction, placeInitialOfferCctpIx: TransactionInstruction]
13071225
> {
13081226
const { payer, feePayer, fastVaa } = accounts;
1227+
let { auction, auctionConfig, offerToken, fromRouterEndpoint, toRouterEndpoint } = accounts;
13091228

13101229
const { offerPrice } = args;
1311-
1312-
let { auction, auctionConfig, offerToken, fromRouterEndpoint, toRouterEndpoint } = accounts;
13131230
let { totalDeposit } = args;
13141231

13151232
offerToken ??= await splToken.getAssociatedTokenAddress(this.mint, payer);
@@ -1329,7 +1246,7 @@ export class MatchingEngineProgram {
13291246
}
13301247
toRouterEndpoint ??= this.routerEndpointAddress(toChainId(fastMarketOrder.targetChain));
13311248

1332-
const custodianData = await this.fetchCustodian();
1249+
const custodianData = await this.getCustodian();
13331250
fetchedConfigId = custodianData.auctionConfigId;
13341251

13351252
const notionalDeposit = await this.computeNotionalSecurityDeposit(
@@ -1342,7 +1259,7 @@ export class MatchingEngineProgram {
13421259

13431260
if (auctionConfig === undefined) {
13441261
if (fetchedConfigId === null) {
1345-
const custodianData = await this.fetchCustodian();
1262+
const custodianData = await this.getCustodian();
13461263
fetchedConfigId = custodianData.auctionConfigId;
13471264
}
13481265
auctionConfig = this.auctionConfigAddress(fetchedConfigId);
@@ -1740,7 +1657,7 @@ export class MatchingEngineProgram {
17401657
sequence ??= fastFillSeeds.sequence;
17411658
}
17421659

1743-
const { feeRecipientToken } = await this.fetchCustodian();
1660+
const { feeRecipientToken } = await this.getCustodian();
17441661

17451662
return this.program.methods
17461663
.settleAuctionNoneLocal()
@@ -1820,7 +1737,7 @@ export class MatchingEngineProgram {
18201737
tokenMessengerMinterProgram,
18211738
} = await this.burnAndPublishAccounts(auction, { targetChain });
18221739

1823-
const { feeRecipientToken } = await this.fetchCustodian();
1740+
const { feeRecipientToken } = await this.getCustodian();
18241741

18251742
return this.program.methods
18261743
.settleAuctionNoneCctp()
@@ -2558,6 +2475,46 @@ export class MatchingEngineProgram {
25582475
(uint64ToBigInt(amountIn) * BigInt(securityDepositBps)) / FEE_PRECISION_MAX
25592476
);
25602477
}
2478+
2479+
async proposalAddress(proposalId?: Uint64): Promise<PublicKey> {
2480+
if (proposalId === undefined) {
2481+
// Intentionally skip cache to get a fresh proposal ID.
2482+
({ nextProposalId: proposalId } = await this.getCustodian(true));
2483+
}
2484+
2485+
return this.pdas.proposal(proposalId);
2486+
}
2487+
2488+
// TODO: we should be able to eliminate these fns, replacing with the call to `.pdas` directly
2489+
auctionConfigAddress = (id: number): PublicKey => this.pdas.auctionConfig(id);
2490+
cctpMintRecipientAddress = (): PublicKey =>
2491+
this.pdas.cctpMintRecipient(this.custodianAddress());
2492+
routerEndpointAddress = (chain: ChainId): PublicKey => this.pdas.routerEndpoint(chain);
2493+
eventAuthorityAddress = (): PublicKey => this.pdas.eventAuthority();
2494+
auctionAddress = (vaaHash: VaaHash): PublicKey => this.pdas.auction(vaaHash);
2495+
custodianAddress = (): PublicKey => this.pdas.custodian();
2496+
fastFillAddress = (
2497+
sourceChain: ChainId,
2498+
orderSender: Array<number>,
2499+
sequence: Uint64,
2500+
): PublicKey => this.pdas.fastFill(sourceChain, orderSender, sequence);
2501+
coreMessageAddress = (auction: PublicKey): PublicKey => this.pdas.coreMessage(auction);
2502+
cctpMessageAddress = (auction: PublicKey): PublicKey => this.pdas.cctpMessage(auction);
2503+
preparedOrderResponseAddress = (fastVaaHash: VaaHash): PublicKey =>
2504+
this.pdas.preparedOrderResponse(fastVaaHash);
2505+
preparedCustodyTokenAddress = (preparedOrderResponse: PublicKey): PublicKey =>
2506+
this.pdas.preparedCustodyToken(preparedOrderResponse);
2507+
auctionCustodyTokenAddress = (auction: PublicKey): PublicKey =>
2508+
this.pdas.auctionCustodyToken(auction);
2509+
localCustodyTokenAddress = (sourceChain: ChainId): PublicKey =>
2510+
this.pdas.localCustodyToken(sourceChain);
2511+
fastFillSequencerAddress = (sourceChain: ChainId, sender: Array<number>): PublicKey =>
2512+
this.pdas.fastFillSequencer(sourceChain, sender);
2513+
reservedFastFillSequenceAddress = (fastVaaHash: VaaHash): PublicKey =>
2514+
this.pdas.reservedFastFillSequenceAddress(fastVaaHash);
2515+
transferAuthorityAddress = (auction: PublicKey, offerPrice: Uint64): PublicKey =>
2516+
this.pdas.transferAuthority(auction, offerPrice);
2517+
auctionHistoryAddress = (id: Uint64): PublicKey => this.pdas.auctionHistory(id);
25612518
}
25622519

25632520
export function testnet(): ProgramId {

0 commit comments

Comments
 (0)