Skip to content

Commit

Permalink
solana/web3.js 1.67 is now more reliable with confirmations
Browse files Browse the repository at this point in the history
  • Loading branch information
archseer committed Nov 29, 2022
1 parent 551c510 commit 9a770db
Show file tree
Hide file tree
Showing 5 changed files with 22 additions and 144 deletions.
2 changes: 1 addition & 1 deletion gauntlet/packages/gauntlet-serum-multisig/package.json
Expand Up @@ -28,6 +28,6 @@
"@chainlink/gauntlet-core": "0.5.0",
"@chainlink/gauntlet-solana": "*",
"@project-serum/anchor": "^0.25.0",
"@solana/web3.js": "^1.50.1"
"@solana/web3.js": "^1.67.0"
}
}
4 changes: 2 additions & 2 deletions gauntlet/packages/gauntlet-solana-contracts/package.json
Expand Up @@ -30,10 +30,10 @@
"@chainlink/gauntlet-solana": "*",
"@ethersproject/keccak256": "^5.5.0",
"@solana/spl-token": "^0.2.0",
"@solana/web3.js": "^1.64.0",
"@solana/web3.js": "^1.67.0",
"protobufjs": "^6.11.2"
},
"overrides": {
"@solana/web3.js": "^1.64.0"
"@solana/web3.js": "^1.67.0"
}
}
4 changes: 2 additions & 2 deletions gauntlet/packages/gauntlet-solana/package.json
Expand Up @@ -30,9 +30,9 @@
"@ledgerhq/hw-transport-node-hid": "^6.20.0",
"@project-serum/anchor": "^0.25.0",
"@project-serum/borsh": "^0.2.2",
"@solana/web3.js": "^1.64.0"
"@solana/web3.js": "^1.67.0"
},
"overrides": {
"@solana/web3.js": "^1.64.0"
"@solana/web3.js": "^1.67.0"
}
}
148 changes: 13 additions & 135 deletions gauntlet/packages/gauntlet-solana/src/commands/internal/solana.ts
Expand Up @@ -7,13 +7,9 @@ import {
PublicKey,
TransactionSignature,
TransactionInstruction,
sendAndConfirmRawTransaction,
TransactionConfirmationStatus,
BlockhashWithExpiryBlockHeight,
RpcResponseAndContext,
SignatureStatus,
SimulatedTransactionResponse,
Commitment,
TransactionExpiredBlockheightExceededError,
} from '@solana/web3.js'
import { withProvider, withWallet, withNetwork } from '../middlewares'
import { TransactionResponse } from '../types'
Expand Down Expand Up @@ -41,17 +37,6 @@ export const getUnixTs = () => {
return new Date().getTime() / 1000
}

export class TimeoutError extends Error {
message: string
txid: string

constructor({ txid }) {
super()
this.message = `Timed out awaiting confirmation. Please confirm in the explorer: `
this.txid = txid
}
}

export class SolanaError extends Error {
message: string
txid: string
Expand Down Expand Up @@ -129,8 +114,7 @@ export default abstract class SolanaCommand extends WriteCommand<TransactionResp

// Based on mango-client-v3
// - https://github.com/blockworks-foundation/mango-client-v3/blob/d34d248a3c9a97d51d977139f61cd982278b7f01/src/client.ts#L356-L437
// - https://github.com/blockworks-foundation/mango-client-v3/blob/d34d248a3c9a97d51d977139f61cd982278b7f01/src/client.ts#L546-L668
// for more reliable transaction submission. Works around https://github.com/solana-labs/solana/issues/25955
// for more reliable transaction submission.
signAndSendRawTx = async (
rawTxs: TransactionInstruction[],
extraSigners?: Keypair[],
Expand Down Expand Up @@ -166,6 +150,7 @@ export default abstract class SolanaCommand extends WriteCommand<TransactionResp
skipPreflight: true,
})

// Send the transaction, periodically retrying for durability
console.log('Started awaiting confirmation for', txid, 'size:', rawTransaction.length)
const startTime = getUnixTs()
let timeout = 60_000
Expand All @@ -189,10 +174,17 @@ export default abstract class SolanaCommand extends WriteCommand<TransactionResp
})()

try {
await this.awaitTransactionSignatureConfirmation(txid, timeout, CONFIRM_LEVEL, currentBlockhash)
await this.provider.connection.confirmTransaction(
{
signature: txid,
...currentBlockhash,
},
CONFIRM_LEVEL,
)
} catch (err: any) {
if (err.timeout) {
throw new TimeoutError({ txid })
if (err instanceof TransactionExpiredBlockheightExceededError) {
console.log(`Timed out awaiting confirmation. Please confirm in the explorer: `, txid)
throw err
}
let simulateResult: SimulatedTransactionResponse | null = null
try {
Expand Down Expand Up @@ -223,120 +215,6 @@ export default abstract class SolanaCommand extends WriteCommand<TransactionResp
done = true
}
return txid

// TODO: skipPreflight: true since we previously simulated it?
// return await sendAndConfirmRawTransaction(this.provider.connection, rawTransaction) // TODO: { maxRetries: 5 }
}

async awaitTransactionSignatureConfirmation(
txid: TransactionSignature,
timeout: number,
confirmLevel: TransactionConfirmationStatus,
signedAtBlock?: BlockhashWithExpiryBlockHeight,
) {
const timeoutBlockHeight = signedAtBlock
? signedAtBlock.lastValidBlockHeight + MAXIMUM_NUMBER_OF_BLOCKS_FOR_TRANSACTION
: 0
let startTimeoutCheck = false
let done = false
const confirmLevels: (TransactionConfirmationStatus | null | undefined)[] = ['finalized']

if (confirmLevel === 'confirmed') {
confirmLevels.push('confirmed')
} else if (confirmLevel === 'processed') {
confirmLevels.push('confirmed')
confirmLevels.push('processed')
}
let subscriptionId: number | undefined

const result = await new Promise((resolve, reject) => {
;(async () => {
setTimeout(() => {
if (done) {
return
}
if (timeoutBlockHeight !== 0) {
startTimeoutCheck = true
} else {
done = true
console.log('Timed out for txid: ', txid)
reject({ timeout: true })
}
}, timeout)
try {
subscriptionId = this.provider.connection.onSignature(
txid,
(result, context) => {
subscriptionId = undefined
done = true
if (result.err) {
reject(result.err)
} else {
// this.lastSlot = context?.slot;
resolve(result)
}
},
confirmLevel,
)
} catch (e) {
done = true
console.log('WS error in setup', txid, e)
}
let retrySleep = 2000
while (!done) {
// eslint-disable-next-line no-loop-func
await sleep(retrySleep)
;(async () => {
try {
const promises: [Promise<RpcResponseAndContext<SignatureStatus | null>>, Promise<number>?] = [
this.provider.connection.getSignatureStatus(txid),
]
//if startTimeoutThreshold passed we start to check if
//current blocks are did not passed timeoutBlockHeight threshold
if (startTimeoutCheck) {
promises.push(this.provider.connection.getBlockHeight(confirmLevel))
}
const [signatureStatus, currentBlockHeight] = await Promise.all(promises)
if (typeof currentBlockHeight !== undefined && timeoutBlockHeight <= currentBlockHeight!) {
console.log('Timed out for txid: ', txid)
done = true
reject({ timeout: true })
}

const result = signatureStatus?.value
if (!done) {
if (!result) return
if (result.err) {
console.log('REST error for', txid, result)
done = true
reject(result.err)
} else if (result.confirmations && confirmLevels.includes(result.confirmationStatus)) {
// this.lastSlot = signatureStatuses?.context?.slot;
console.log('REST confirmed', txid, result)
done = true
resolve(result)
} else {
console.log('REST not confirmed', txid, result)
}
}
} catch (e) {
if (!done) {
console.log('REST connection error: txid', txid, e)
}
}
})()
}
})()
})

if (subscriptionId) {
this.provider.connection.removeSignatureListener(subscriptionId).catch((e) => {
console.log('WS error in cleanup', e)
})
}

done = true
return result
}

sendTxWithIDL = (sendAction: (...args: any) => Promise<TransactionSignature>, idl: Idl) => async (
Expand Down
8 changes: 4 additions & 4 deletions gauntlet/yarn.lock
Expand Up @@ -1065,10 +1065,10 @@
superstruct "^0.14.2"
tweetnacl "^1.0.0"

"@solana/web3.js@^1.50.1", "@solana/web3.js@^1.64.0":
version "1.66.2"
resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.66.2.tgz#80b43c5868b846124fe3ebac7d3943930c3fa60c"
integrity sha512-RyaHMR2jGmaesnYP045VLeBGfR/gAW3cvZHzMFGg7bkO+WOYOYp1nEllf0/la4U4qsYGKCsO9eEevR5fhHiVHg==
"@solana/web3.js@^1.67.0":
version "1.67.0"
resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.67.0.tgz#bd67742cd9c176889fd1954080173dd7d0c224a2"
integrity sha512-t6tfRbYyuoyuik4D4B8eK6hPm//+5l7k+00YppFTGoxcix8b2bpE7iANLmvw+u5iZNMwG3VFV20xRdrqz8zhvQ==
dependencies:
"@babel/runtime" "^7.12.5"
"@noble/ed25519" "^1.7.0"
Expand Down

0 comments on commit 9a770db

Please sign in to comment.