Skip to content

Commit

Permalink
add getMessageFee to core protocol, add handlers for some Solana simu…
Browse files Browse the repository at this point in the history
…late errors
  • Loading branch information
barnjamin committed Jan 3, 2024
1 parent 33195de commit 4511053
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 12 deletions.
1 change: 1 addition & 0 deletions core/definitions/src/protocols/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export interface WormholeCore<
P extends Platform,
C extends PlatformToChains<P>,
> {
getMessageFee(): Promise<bigint>;
publishMessage(
sender: AccountAddress<C>,
message: string | Uint8Array,
Expand Down
3 changes: 3 additions & 0 deletions platforms/aptos/protocols/core/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ export class AptosWormholeCore<N extends Network, C extends AptosChains>
throw new Error(`CoreBridge contract Address for chain ${chain} not found`);
this.coreBridge = coreBridgeAddress;
}
getMessageFee(): Promise<bigint> {
throw new Error("Method not implemented.");
}

static async fromRpc<N extends Network>(
connection: AptosClient,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ export class CosmwasmWormholeCore<N extends Network, C extends CosmwasmChains>
this.coreAddress = coreAddress;
}

getMessageFee(): Promise<bigint> {
throw new Error("Method not implemented.");
}

static async fromRpc<N extends Network>(
rpc: CosmWasmClient,
config: ChainsConfig<N, CosmwasmPlatformType>,
Expand Down
4 changes: 2 additions & 2 deletions platforms/cosmwasm/protocols/core/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { registerProtocol } from "@wormhole-foundation/connect-sdk";
import { _platform } from "@wormhole-foundation/connect-sdk-cosmwasm";
import { CosmwasmWormholeCore } from "./wormholeCore";
import { CosmwasmWormholeCore } from "./core";

declare global {
namespace WormholeNamespace {
Expand All @@ -12,4 +12,4 @@ declare global {

registerProtocol(_platform, "WormholeCore", CosmwasmWormholeCore);

export * from "./wormholeCore";
export * from "./core";
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ export class EvmWormholeCore<
);
}

async getMessageFee(): Promise<bigint> {
return await this.core.messageFee.staticCall();
}

static async fromRpc<N extends Network>(
provider: Provider,
config: ChainsConfig<N, EvmPlatformType>,
Expand Down
4 changes: 2 additions & 2 deletions platforms/evm/protocols/core/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { registerProtocol } from '@wormhole-foundation/connect-sdk';
import { _platform } from '@wormhole-foundation/connect-sdk-evm';
import { EvmWormholeCore } from './wormholeCore';
import { EvmWormholeCore } from './core';

declare global {
namespace WormholeNamespace {
Expand All @@ -13,4 +13,4 @@ declare global {
registerProtocol(_platform, 'WormholeCore', EvmWormholeCore);

export * as ethers_contracts from './ethers-contracts';
export * from './wormholeCore';
export * from './core';
9 changes: 9 additions & 0 deletions platforms/solana/protocols/core/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
createReadOnlyWormholeProgramInterface,
createVerifySignaturesInstructions,
derivePostedVaaKey,
getWormholeBridgeData,
} from './utils';

const SOLANA_SEQ_LOG = 'Program log: Sequence: ';
Expand Down Expand Up @@ -86,6 +87,14 @@ export class SolanaWormholeCore<N extends Network, C extends SolanaChains>
);
}

async getMessageFee(): Promise<bigint> {
const bd = await getWormholeBridgeData(
this.connection,
this.coreBridge.programId,
);
return bd.config.fee;
}

async *publishMessage(
sender: AnySolanaAddress,
message: Uint8Array,
Expand Down
68 changes: 60 additions & 8 deletions platforms/solana/src/platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import {
ParsedAccountData,
PublicKey,
SendOptions,
SendTransactionError,
TransactionExpiredBlockheightExceededError,
} from '@solana/web3.js';
import { SolanaAddress, SolanaZeroAddress } from './address';
import {
Expand Down Expand Up @@ -160,6 +162,52 @@ export class SolanaPlatform<N extends Network> extends PlatformContext<
return balancesArr.reduce((obj, item) => Object.assign(obj, item), {});
}

// Handles retrying a Transaction if the error is deemed to be
// recoverable. Currently handles:
// - Blockhash not found (blockhash too new for the node we submitted to)
// - Not enough bytes (storage account not seen yet)

private static async sendWithRetry(
rpc: Connection,
stxns: SignedTx,
opts: SendOptions,
retries: number = 3,
): Promise<string> {
// Shouldnt get hit but just in case
if (!retries) throw new Error('Too many retries');

try {
const txid = await rpc.sendRawTransaction(stxns.tx, opts);
return txid;
} catch (e) {
retries -= 1;
if (!retries) throw e;

// Would require re-signing, for now bail
if (e instanceof TransactionExpiredBlockheightExceededError) throw e;

// Only handle SendTransactionError
if (!(e instanceof SendTransactionError)) throw e;
const emsg = e.message;

// Only handle simulation errors
if (!emsg.includes('Transaction simulation failed')) throw e;

// Blockhash not found _yet_
if (emsg.includes('Blockhash not found'))
return this.sendWithRetry(rpc, stxns, opts, retries);

// Find the log message with the error details
const loggedErr = e.logs.find((log) =>
log.startsWith('Program log: Error: '),
);

// Probably caused by storage account not seen yet
if (loggedErr && loggedErr.includes('Not enough bytes'))
return this.sendWithRetry(rpc, stxns, opts, retries);
}
}

static async sendWait(
chain: Chain,
rpc: Connection,
Expand All @@ -168,14 +216,16 @@ export class SolanaPlatform<N extends Network> extends PlatformContext<
): Promise<TxHash[]> {
const { blockhash, lastValidBlockHeight } = await this.latestBlock(rpc);

// Set the commitment level to match the rpc commitment level
// otherwise, it defaults to finalized
if (!opts) opts = { preflightCommitment: rpc.commitment };

const txhashes = await Promise.all(
stxns.map((stxn) => {
return rpc.sendRawTransaction(stxn, opts);
}),
stxns.map((stxn) =>
this.sendWithRetry(
rpc,
stxn,
// Set the commitment level to match the rpc commitment level
// otherwise, it defaults to finalized
opts ?? { preflightCommitment: rpc.commitment },
),
),
);

await Promise.all(
Expand All @@ -198,11 +248,13 @@ export class SolanaPlatform<N extends Network> extends PlatformContext<
rpc: Connection,
commitment?: Commitment,
): Promise<{ blockhash: string; lastValidBlockHeight: number }> {
// Use finalized to prevent blockhash not found errors
// Note: this may mean we have less time to submit transactions?
return rpc.getLatestBlockhash(commitment ?? 'finalized');
}

static async getLatestBlock(rpc: Connection): Promise<number> {
const { lastValidBlockHeight } = await this.latestBlock(rpc);
const { lastValidBlockHeight } = await this.latestBlock(rpc, 'confirmed');
return lastValidBlockHeight;
}

Expand Down

0 comments on commit 4511053

Please sign in to comment.