|
| 1 | +import { Rpc, SolanaRpcApi } from "@solana/kit"; |
| 2 | +import { Deposit, FillStatus, FillWithBlock, RelayData } from "../../interfaces"; |
| 3 | +import { BigNumber, isUnsafeDepositId } from "../../utils"; |
| 4 | + |
| 5 | +type Provider = Rpc<SolanaRpcApi>; |
| 6 | + |
| 7 | +/** |
| 8 | + * @param spokePool SpokePool Contract instance. |
| 9 | + * @param deposit V3Deopsit instance. |
| 10 | + * @param repaymentChainId Optional repaymentChainId (defaults to destinationChainId). |
| 11 | + * @returns An Ethers UnsignedTransaction instance. |
| 12 | + */ |
| 13 | +export function populateV3Relay( |
| 14 | + _spokePool: unknown, |
| 15 | + _deposit: Omit<Deposit, "messageHash">, |
| 16 | + _relayer: string, |
| 17 | + _repaymentChainId = _deposit.destinationChainId |
| 18 | +): Promise<unknown> { |
| 19 | + throw new Error("populateV3Relay: not implemented"); |
| 20 | +} |
| 21 | + |
| 22 | +/** |
| 23 | + * Retrieves the time from the SpokePool contract at a particular block. |
| 24 | + * @returns The time at the specified block tag. |
| 25 | + */ |
| 26 | +export function getTimeAt(_spokePool: unknown, _blockNumber: number): Promise<number> { |
| 27 | + throw new Error("getTimeAt: not implemented"); |
| 28 | +} |
| 29 | + |
| 30 | +/** |
| 31 | + * Retrieves the chain time at a particular block. |
| 32 | + * @note This should be the same as getTimeAt() but can differ in test. These two functions should be consolidated. |
| 33 | + * @returns The chain time at the specified block tag. |
| 34 | + */ |
| 35 | +export async function getTimestampForBlock(provider: Provider, blockNumber: number): Promise<number> { |
| 36 | + const block = await provider.getBlock(BigInt(blockNumber)).send(); |
| 37 | + let timestamp: number; |
| 38 | + if (!block?.blockTime) { |
| 39 | + console.error(`Unable to resolve svm block ${blockNumber}`); |
| 40 | + timestamp = 0; // @todo: How to handle this? |
| 41 | + } else { |
| 42 | + timestamp = Number(block.blockTime); // Unix timestamps fit within number. |
| 43 | + } |
| 44 | + |
| 45 | + return timestamp; |
| 46 | +} |
| 47 | + |
| 48 | +/** |
| 49 | + * Return maximum of fill deadline buffer at start and end of block range. |
| 50 | + * @param spokePool SpokePool contract instance |
| 51 | + * @param startBlock start block |
| 52 | + * @param endBlock end block |
| 53 | + * @returns maximum of fill deadline buffer at start and end block |
| 54 | + */ |
| 55 | +export function getMaxFillDeadlineInRange( |
| 56 | + _spokePool: unknown, |
| 57 | + _startBlock: number, |
| 58 | + _endBlock: number |
| 59 | +): Promise<number> { |
| 60 | + throw new Error("getMaxFillDeadlineInRange: not implemented"); |
| 61 | +} |
| 62 | + |
| 63 | +/** |
| 64 | + * Finds the deposit id at a specific block number. |
| 65 | + * @param blockTag The block number to search for the deposit ID at. |
| 66 | + * @returns The deposit ID. |
| 67 | + */ |
| 68 | +export function getDepositIdAtBlock(_contract: unknown, _blockTag: number): Promise<BigNumber> { |
| 69 | + throw new Error("getDepositIdAtBlock: not implemented"); |
| 70 | +} |
| 71 | + |
| 72 | +/** |
| 73 | + * xxx todo |
| 74 | + */ |
| 75 | +export async function getSlotForBlock( |
| 76 | + provider: Provider, |
| 77 | + blockNumber: bigint, |
| 78 | + lowSlot: bigint, |
| 79 | + _highSlot?: bigint |
| 80 | +): Promise<bigint | undefined> { |
| 81 | + // @todo: Factor getBlock out to SlotFinder ?? |
| 82 | + const getBlockNumber = async (slot: bigint): Promise<bigint> => { |
| 83 | + const block = await provider |
| 84 | + .getBlock(slot, { transactionDetails: "none", maxSupportedTransactionVersion: 0 }) |
| 85 | + .send(); |
| 86 | + return block?.blockHeight ?? BigInt(0); // @xxx Handle undefined here! |
| 87 | + }; |
| 88 | + |
| 89 | + let highSlot = _highSlot ?? (await provider.getSlot().send()); |
| 90 | + const [blockLow = 0, blockHigh = 1_000_000_000] = await Promise.all([ |
| 91 | + getBlockNumber(lowSlot), |
| 92 | + getBlockNumber(highSlot), |
| 93 | + ]); |
| 94 | + |
| 95 | + if (blockLow > blockNumber || blockHigh < blockNumber) { |
| 96 | + return undefined; // blockNumber did not occur within the specified block range. |
| 97 | + } |
| 98 | + |
| 99 | + // Find the lowest slot number where blockHeight is greater than the requested blockNumber. |
| 100 | + do { |
| 101 | + const midSlot = (highSlot + lowSlot) / BigInt(2); |
| 102 | + const midBlock = await getBlockNumber(midSlot); |
| 103 | + |
| 104 | + if (midBlock < blockNumber) { |
| 105 | + lowSlot = midSlot + BigInt(1); |
| 106 | + } else if (midBlock > blockNumber) { |
| 107 | + highSlot = midSlot + BigInt(1); // blockNumber occurred at or earlier than midBlock. |
| 108 | + } else { |
| 109 | + return midSlot; |
| 110 | + } |
| 111 | + } while (lowSlot <= highSlot); |
| 112 | + |
| 113 | + return undefined; |
| 114 | +} |
| 115 | + |
| 116 | +export function findDepositBlock( |
| 117 | + _spokePool: unknown, |
| 118 | + depositId: BigNumber, |
| 119 | + _lowBlock: number, |
| 120 | + _highBlock?: number |
| 121 | +): Promise<number | undefined> { |
| 122 | + // We can only perform this search when we have a safe deposit ID. |
| 123 | + if (isUnsafeDepositId(depositId)) { |
| 124 | + throw new Error(`Cannot binary search for depositId ${depositId}`); |
| 125 | + } |
| 126 | + throw new Error("findDepositBlock: not implemented"); |
| 127 | +} |
| 128 | + |
| 129 | +/** |
| 130 | + * Find the amount filled for a deposit at a particular block. |
| 131 | + * @param spokePool SpokePool contract instance. |
| 132 | + * @param relayData Deposit information that is used to complete a fill. |
| 133 | + * @param blockTag Block tag (numeric or "latest") to query at. |
| 134 | + * @returns The amount filled for the specified deposit at the requested block (or latest). |
| 135 | + */ |
| 136 | +export function relayFillStatus( |
| 137 | + _spokePool: unknown, |
| 138 | + _relayData: RelayData, |
| 139 | + _blockTag?: number | "latest", |
| 140 | + _destinationChainId?: number |
| 141 | +): Promise<FillStatus> { |
| 142 | + throw new Error("relayFillStatus: not implemented"); |
| 143 | +} |
| 144 | + |
| 145 | +export function fillStatusArray( |
| 146 | + _spokePool: unknown, |
| 147 | + _relayData: RelayData[], |
| 148 | + _blockTag = "processed" |
| 149 | +): Promise<(FillStatus | undefined)[]> { |
| 150 | + throw new Error("fillStatusArray: not implemented"); |
| 151 | +} |
| 152 | + |
| 153 | +/** |
| 154 | + * Find the block at which a fill was completed. |
| 155 | + * @todo After SpokePool upgrade, this function can be simplified to use the FillStatus enum. |
| 156 | + * @param spokePool SpokePool contract instance. |
| 157 | + * @param relayData Deposit information that is used to complete a fill. |
| 158 | + * @param lowBlockNumber The lower bound of the search. Must be bounded by SpokePool deployment. |
| 159 | + * @param highBlocknumber Optional upper bound for the search. |
| 160 | + * @returns The block number at which the relay was completed, or undefined. |
| 161 | + */ |
| 162 | +export function findFillBlock( |
| 163 | + _spokePool: unknown, |
| 164 | + _relayData: RelayData, |
| 165 | + _lowBlockNumber: number, |
| 166 | + _highBlockNumber?: number |
| 167 | +): Promise<number | undefined> { |
| 168 | + throw new Error("fillStatusArray: not implemented"); |
| 169 | +} |
| 170 | + |
| 171 | +export function findFillEvent( |
| 172 | + _spokePool: unknown, |
| 173 | + _relayData: RelayData, |
| 174 | + _lowBlockNumber: number, |
| 175 | + _highBlockNumber?: number |
| 176 | +): Promise<FillWithBlock | undefined> { |
| 177 | + throw new Error("fillStatusArray: not implemented"); |
| 178 | +} |
0 commit comments