Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SDK: Implement EVM protocols, refine interfaces #186

Open
wants to merge 11 commits into
base: sdk/evm-protocol-def
Choose a base branch
from
3 changes: 2 additions & 1 deletion evm/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ ts/lib
ts/src/types
dist
ts-types
*.tsbuildinfo
*.tsbuildinfo
selectors.txt
6 changes: 3 additions & 3 deletions evm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@
}
},
"dependencies": {
"@wormhole-foundation/sdk-base": "^0.7.0-beta.6",
"@wormhole-foundation/sdk-definitions": "^0.7.0-beta.6",
"@wormhole-foundation/sdk-evm": "^0.7.0-beta.6",
"@wormhole-foundation/sdk-base": "^0.7.1-beta.2",
"@wormhole-foundation/sdk-definitions": "^0.7.1-beta.2",
"@wormhole-foundation/sdk-evm": "^0.7.1-beta.2",
"@wormhole-foundation/example-liquidity-layer-definitions": "0.0.1",
"ethers": "^6.5.1"
},
Expand Down
18 changes: 9 additions & 9 deletions evm/ts/src/MatchingEngine/evm.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ChainId, asChainId } from "@wormhole-foundation/sdk-base";
import { ethers } from "ethers";
import { RouterEndpoint, LiveAuctionData, MatchingEngine, RedeemParameters } from ".";
import { AbstractMatchingEngine, LiveAuctionData, RedeemParameters, RouterEndpoint } from ".";
import { LiquidityLayerTransactionResult } from "..";
import {
IMatchingEngine,
Expand All @@ -11,7 +11,7 @@ import {
IWormhole__factory,
} from "../types";

export class EvmMatchingEngine implements MatchingEngine<ethers.ContractTransaction> {
export class MatchingEngine implements AbstractMatchingEngine<ethers.ContractTransaction> {
contract: IMatchingEngine;
circle: ITokenMessenger;

Expand Down Expand Up @@ -39,39 +39,39 @@ export class EvmMatchingEngine implements MatchingEngine<ethers.ContractTransact
return this.connection;
}

connect(connection: ethers.Provider): EvmMatchingEngine {
return new EvmMatchingEngine(connection, this.address, this.circleBridge);
connect(connection: ethers.Provider): MatchingEngine {
return new MatchingEngine(connection, this.address, this.circleBridge);
}

async addRouterEndpoint(
async addRouterEndpointTx(
chain: number,
endpoint: RouterEndpoint,
domain: number,
): Promise<ethers.ContractTransaction> {
return this.contract.addRouterEndpoint.populateTransaction(chain, endpoint, domain);
}

async placeInitialBid(
async placeInitialBidTx(
fastTransferVaa: Buffer | Uint8Array,
feeBid: bigint | ethers.BigNumberish,
): Promise<ethers.ContractTransaction> {
return this.contract.placeInitialBid.populateTransaction(fastTransferVaa, feeBid);
}

async improveBid(
async improveBidTx(
auctionId: Buffer | Uint8Array,
feeBid: bigint | ethers.BigNumberish,
): Promise<ethers.ContractTransaction> {
return this.contract.improveBid.populateTransaction(auctionId, feeBid);
}

async executeFastOrder(
async executeFastOrderTx(
fastTransferVaa: Buffer | Uint8Array,
): Promise<ethers.ContractTransaction> {
return this.contract.executeFastOrder.populateTransaction(fastTransferVaa);
}

async executeSlowOrderAndRedeem(
async executeSlowOrderAndRedeemTx(
fastTransferVaa: Buffer | Uint8Array,
params: RedeemParameters,
): Promise<ethers.ContractTransaction> {
Expand Down
12 changes: 6 additions & 6 deletions evm/ts/src/MatchingEngine/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ export type RouterEndpoint = {
mintRecipient: string | Buffer | Uint8Array;
};

export abstract class MatchingEngine<PreparedTransactionType extends PreparedInstruction> {
export abstract class AbstractMatchingEngine<PreparedTransactionType extends PreparedInstruction> {
abstract get address(): string;

abstract addRouterEndpoint(
abstract addRouterEndpointTx(
chain: number,
endpoint: RouterEndpoint,
domain: number,
Expand All @@ -37,21 +37,21 @@ export abstract class MatchingEngine<PreparedTransactionType extends PreparedIns

abstract auctionStatus(auctionId: Buffer | Uint8Array): Promise<bigint>;

abstract placeInitialBid(
abstract placeInitialBidTx(
fastTransferVaa: Buffer | Uint8Array,
feeBid: bigint,
): Promise<PreparedTransactionType>;

abstract improveBid(
abstract improveBidTx(
auctionId: Buffer | Uint8Array,
feeBid: bigint,
): Promise<PreparedTransactionType>;

abstract executeFastOrder(
abstract executeFastOrderTx(
fastTransferVaa: Buffer | Uint8Array,
): Promise<PreparedTransactionType>;

abstract executeSlowOrderAndRedeem(
abstract executeSlowOrderAndRedeemTx(
fastTransferVaa: Buffer | Uint8Array,
params: RedeemParameters,
): Promise<PreparedTransactionType>;
Expand Down
4 changes: 2 additions & 2 deletions evm/ts/src/TokenRouter/evm.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ChainId, asChainId } from "@wormhole-foundation/sdk-base";
import { ethers } from "ethers";
import { Endpoint, OrderResponse, TokenRouter, FastTransferParameters } from ".";
import { Endpoint, OrderResponse, AbstractTokenRouter, FastTransferParameters } from ".";
import { LiquidityLayerTransactionResult } from "..";
import {
ITokenRouter,
Expand All @@ -11,7 +11,7 @@ import {
ITokenMessenger,
} from "../types";

export class EvmTokenRouter implements TokenRouter<ethers.ContractTransaction> {
export class TokenRouter implements AbstractTokenRouter<ethers.ContractTransaction> {
contract: ITokenRouter;
circle: ITokenMessenger;

Expand Down
31 changes: 30 additions & 1 deletion evm/ts/src/TokenRouter/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { encoding } from "@wormhole-foundation/sdk-base";
import { CircleBridge, VAA, deserialize, serialize } from "@wormhole-foundation/sdk-definitions";
import { LiquidityLayerTransactionResult, PreparedInstruction } from "..";
import {
FastTransfer,
MatchingEngine,
} from "@wormhole-foundation/example-liquidity-layer-definitions";
export * from "./evm";

export type FastTransferParameters = {
Expand All @@ -14,12 +20,35 @@ export type OrderResponse = {
circleAttestation: Buffer | Uint8Array;
};

export function encodeOrderResponse(response: FastTransfer.OrderResponse): OrderResponse {
return FastTransfer.isFastFill(response)
? {
encodedWormholeMessage: serialize(response.vaa),
circleAttestation: new Uint8Array(),
circleBridgeMessage: new Uint8Array(),
}
: {
encodedWormholeMessage: serialize(response.vaa),
circleAttestation: encoding.hex.decode(response.cctp!.attestation!),
circleBridgeMessage: CircleBridge.serialize(response.cctp!.message),
};
}
export function decodedOrderResponse(response: OrderResponse): FastTransfer.OrderResponse {
if (response.circleAttestation.length > 0) {
const [message] = CircleBridge.deserialize(response.circleBridgeMessage);
const attestation = encoding.hex.encode(response.circleAttestation, true);
const vaa = deserialize("FastTransfer:CctpDeposit", response.encodedWormholeMessage);
return { vaa, cctp: { message, attestation } };
}
return { vaa: deserialize("FastTransfer:FastFill", response.encodedWormholeMessage) };
}

export type Endpoint = {
router: string | Buffer | Uint8Array;
mintRecipient: string | Buffer | Uint8Array;
};

export abstract class TokenRouter<PreparedTransactionType extends PreparedInstruction> {
export abstract class AbstractTokenRouter<PreparedTransactionType extends PreparedInstruction> {
abstract get address(): string;

abstract placeMarketOrderTx(
Expand Down
1 change: 1 addition & 0 deletions evm/ts/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export * from "./TokenRouter";
export * from "./error";
export * from "./messages";
export * from "./utils";
export * from "./protocol";

export * as ethers_types from "./types";

Expand Down
1 change: 1 addition & 0 deletions evm/ts/src/protocol/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./tokenRouter";
export * from "./matchingEngine";
156 changes: 156 additions & 0 deletions evm/ts/src/protocol/matchingEngine.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import {
FastTransfer,
MatchingEngine,
} from "@wormhole-foundation/example-liquidity-layer-definitions";
import { Chain, Network } from "@wormhole-foundation/sdk-base";
import {
AccountAddress,
CircleBridge,
Contracts,
UnsignedTransaction,
serialize,
} from "@wormhole-foundation/sdk-definitions";
import {
AnyEvmAddress,
EvmAddress,
EvmChains,
EvmUnsignedTransaction,
} from "@wormhole-foundation/sdk-evm";
import { ethers } from "ethers";
import { MatchingEngine as _MatchingEngine } from "../MatchingEngine";
import { IUSDC__factory } from "../types";

export class EvmMatchingEngine<N extends Network, C extends EvmChains>
extends _MatchingEngine
implements MatchingEngine<N, C>
{
constructor(
readonly network: N,
readonly chain: C,
provider: ethers.Provider,
readonly contracts: Contracts & MatchingEngine.Addresses,
) {
super(provider, contracts.matchingEngine, contracts.cctp.tokenMessenger);
}
async *registerRouter<RC extends Chain>(
sender: AnyEvmAddress,
chain: RC,
cctpDomain: number,
router: AccountAddress<RC>,
tokenAccount?: AccountAddress<C> | undefined,
) {
throw new Error("Method not implemented.");
}
async *updateRouter<RC extends Chain>(
sender: AnyEvmAddress,
chain: RC,
cctpDomain: number,
router: AccountAddress<RC>,
tokenAccount?: AccountAddress<C> | undefined,
) {
throw new Error("Method not implemented.");
}
async *disableRouter<RC extends Chain>(sender: AnyEvmAddress, chain: RC) {
throw new Error("Method not implemented.");
}
async *setPause(sender: AnyEvmAddress, pause: boolean) {
throw new Error("Method not implemented.");
}
async *setConfiguration(config: {
enabled: boolean;
maxAmount: bigint;
baseFee: bigint;
initAuctionFee: bigint;
}) {
throw new Error("Method not implemented.");
}

async *approveAllowance(sender: AnyEvmAddress, amount: bigint) {
const from = new EvmAddress(sender).unwrap();
const tokenContract = IUSDC__factory.connect(this.contracts.cctp.usdcMint, this.provider);

const allowed = await tokenContract.allowance(from, this.address);
if (amount > allowed) {
const txReq = await tokenContract.approve.populateTransaction(this.address, amount);
yield this.createUnsignedTx(
{ ...txReq, from },
"MatchingEngine.approveAllowance",
false,
);
}
}

async *placeInitialOffer(sender: AnyEvmAddress, order: FastTransfer.Order, offerPrice: bigint) {
const from = new EvmAddress(sender).unwrap();

const { amountIn, maxFee } = order.payload;
yield* this.approveAllowance(sender, amountIn + maxFee);

const txReq = await this.connect(this.provider).placeInitialBidTx(
serialize(order),
offerPrice,
);

yield this.createUnsignedTx({ ...txReq, from }, "MatchingEngine.placeInitialOffer");
}
async *improveOffer(sender: AnyEvmAddress, order: FastTransfer.Order, offer: bigint) {
const from = new EvmAddress(sender).unwrap();

const auctionId = FastTransfer.auctionId(order);

// TODO: is this the correct amount to request for allowance here
const { amount, securityDeposit } = await this.liveAuctionInfo(auctionId);
yield* this.approveAllowance(sender, amount + securityDeposit);

const txReq = await this.improveBidTx(auctionId, offer);
yield this.createUnsignedTx({ ...txReq, from }, "MatchingEngine.improveOffer");
}

async *executeFastOrder(sender: AnyEvmAddress, vaa: FastTransfer.Order) {
const from = new EvmAddress(sender).unwrap();
const txReq = await this.executeFastOrderTx(serialize(vaa));
yield this.createUnsignedTx({ ...txReq, from }, "MatchingEngine.executeFastOrder");
}

async *prepareOrderResponse(
sender: AnyEvmAddress,
order: FastTransfer.Order,
response: FastTransfer.OrderResponse,
) {
throw new Error("Method not implemented.");
}

async *settleOrder(
sender: AnyEvmAddress,
order: FastTransfer.Order,
response: FastTransfer.OrderResponse,
) {
const from = new EvmAddress(sender).unwrap();

const fastVaaBytes = serialize(order);

const txReq = await (FastTransfer.isFastFill(response)
? this.executeFastOrderTx(fastVaaBytes)
: this.executeSlowOrderAndRedeemTx(fastVaaBytes, {
encodedWormholeMessage: serialize(response.vaa),
circleBridgeMessage: CircleBridge.serialize(response.cctp!.message),
circleAttestation: response.cctp!.attestation!,
}));

yield this.createUnsignedTx({ ...txReq, from }, "MatchingEngine.settleOrder");
}

private createUnsignedTx(
txReq: ethers.TransactionRequest,
description: string,
parallelizable: boolean = false,
): UnsignedTransaction<N, C> {
return new EvmUnsignedTransaction(
txReq,
this.network,
this.chain,
description,
parallelizable,
);
}
}
Loading
Loading