From 5dedb2e554c6ab9695caa81beabc41ee6fe9f9ba Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Thu, 4 Jan 2024 08:03:19 -0500 Subject: [PATCH] set stricter tsconfig, fix what broke --- connect/src/protocols/cctpTransfer.ts | 34 +++++------- core/definitions/src/address.ts | 8 +-- platforms/algorand/protocols/core/src/core.ts | 54 +++++++++---------- .../algorand/protocols/core/src/storage.ts | 10 ++-- .../protocols/tokenBridge/src/tokenBridge.ts | 53 ++++++++---------- platforms/cosmwasm/src/platform.ts | 2 +- .../evm/protocols/cctp/src/circleBridge.ts | 4 +- .../solana/protocols/cctp/src/circleBridge.ts | 6 ++- platforms/solana/src/testing/sendSigner.ts | 2 +- tsconfig.json | 4 +- turbo.json | 1 + 11 files changed, 81 insertions(+), 97 deletions(-) diff --git a/connect/src/protocols/cctpTransfer.ts b/connect/src/protocols/cctpTransfer.ts index c203190ee9..764a49af4c 100644 --- a/connect/src/protocols/cctpTransfer.ts +++ b/connect/src/protocols/cctpTransfer.ts @@ -293,12 +293,12 @@ export class CircleTransfer for (const idx in attestations) { const ca = attestations[idx]!; - if (ca.attestation.attestation) continue; // already got it + if (ca.attestation?.attestation) continue; // already got it const attestation = await this.wh.getCircleAttestation(ca.id.hash, timeout); if (attestation === null) throw new Error("No attestation available after timeout exhausted"); - attestations[idx].attestation.attestation = attestation; + attestations[idx]!.attestation!.attestation = attestation; } this.attestations = attestations; @@ -355,24 +355,20 @@ export class CircleTransfer if (!this.attestations) throw new Error("No Circle Attestations found"); - const circleAttestations = this.attestations.filter((a) => - isCircleMessageId(a.id), - ) as AttestationReceipt<"CircleBridge">[]; - + const circleAttestations = this.attestations.filter((a) => isCircleMessageId(a.id)); if (circleAttestations.length > 1) throw new Error(`Expected a single circle attestation, found ${circleAttestations.length}`); - const toChain = this.wh.getChain(this.transfer.to.chain); - - const { - id, - attestation: { message, attestation }, - } = circleAttestations[0]!; - + const { id, attestation } = circleAttestations[0]! as AttestationReceipt<"CircleBridge">; if (!attestation) throw new Error(`No Circle Attestation for ${id.hash}`); + const { message, attestation: signatures } = attestation; + if (!signatures) throw new Error(`No Circle Attestation for ${id.hash}`); + + const toChain = this.wh.getChain(this.transfer.to.chain); const tb = await toChain.getCircleBridge(); - const xfer = tb.redeem(this.transfer.to.address, message, attestation); + + const xfer = tb.redeem(this.transfer.to.address, message, signatures!); const txids = await signSendWait(toChain, xfer, signer); this.txids?.push(...txids); @@ -490,14 +486,8 @@ export class CircleTransfer receipt = { ...receipt, state: TransferState.SourceInitiated, originTxs }; } - const att = xfer.attestations.filter((a) => - isWormholeMessageId(a.id), - ) as AttestationReceipt<"AutomaticCircleBridge">[]; - - const ctt = xfer.attestations.filter((a) => - isCircleMessageId(a.id), - ) as AttestationReceipt<"CircleBridge">[]; - + const att = xfer.attestations?.filter((a) => isWormholeMessageId(a.id)) ?? []; + const ctt = xfer.attestations?.filter((a) => isCircleMessageId(a.id)) ?? []; const attestation = att.length > 0 ? att[0]! : ctt.length > 0 ? ctt[0]! : undefined; if (attestation && attestation.attestation) { receipt = { ...receipt, state: TransferState.Attested, attestation: attestation }; diff --git a/core/definitions/src/address.ts b/core/definitions/src/address.ts index 8aa8f66742..ed9d19c8d6 100644 --- a/core/definitions/src/address.ts +++ b/core/definitions/src/address.ts @@ -73,7 +73,7 @@ export function registerNative

(platform: P, ctr: NativeAddre } export function nativeIsRegistered(chain: C): boolean { - const platform: Platform = chainToPlatform.get(chain); + const platform: Platform = chainToPlatform.get(chain)!; return nativeFactory.has(platform); } @@ -81,7 +81,7 @@ export function toNative( chain: C, ua: UniversalAddress | string | Uint8Array, ): NativeAddress { - const platform: Platform = chainToPlatform.get(chain); + const platform: Platform = chainToPlatform.get(chain)!; const nativeCtr = nativeFactory.get(platform); if (!nativeCtr) throw new Error(`No native address type registered for platform ${platform}`); return new nativeCtr(ua) as unknown as NativeAddress; @@ -91,6 +91,6 @@ export function toUniversal( chain: C, address: string | Uint8Array, ): UniversalAddress { - const platform: Platform = chainToPlatform.get(chain); - return new UniversalAddress(address, platformToAddressFormat.get(platform)); + const platform: Platform = chainToPlatform.get(chain)!; + return new UniversalAddress(address, platformToAddressFormat.get(platform)!); } diff --git a/platforms/algorand/protocols/core/src/core.ts b/platforms/algorand/protocols/core/src/core.ts index d77028cdd9..48fd120cf0 100644 --- a/platforms/algorand/protocols/core/src/core.ts +++ b/platforms/algorand/protocols/core/src/core.ts @@ -124,10 +124,7 @@ export class AlgorandWormholeCore const storage = StorageLogicSig.forEmitter(this.coreAppId, _sender.toUint8Array()); - const { - accounts: [storageAddress], - txs, - } = await AlgorandWormholeCore.maybeCreateStorageTx( + const { accounts, txs } = await AlgorandWormholeCore.maybeCreateStorageTx( this.connection, address, this.coreAppId, @@ -143,7 +140,7 @@ export class AlgorandWormholeCore from: address, appIndex: safeBigIntToNumber(this.coreAppId), appArgs: [AlgorandWormholeCore.publishMessage, message, encoding.bignum.toBytes(0n, 8)], - accounts: [storageAddress], + accounts: accounts, onComplete: OnApplicationComplete.NoOpOC, suggestedParams, }); @@ -162,7 +159,7 @@ export class AlgorandWormholeCore .getApplicationByID(safeBigIntToNumber(this.coreAppId)) .do(); const appInfo = modelsv2.Application.from_obj_for_encoding(applInfoResp); - const val = appInfo.params.globalState.find((kv) => kv.key === AlgorandWormholeCore.feeKey); + const val = appInfo.params.globalState?.find((kv) => kv.key === AlgorandWormholeCore.feeKey); return val ? BigInt(val.value.uint) : 0n; } @@ -180,20 +177,20 @@ export class AlgorandWormholeCore } // Expect target is core app - if (BigInt(ptr.txn.txn.apid) !== this.coreAppId) return msgs; + if (BigInt(ptr.txn.txn.apid ?? 0) !== this.coreAppId) return msgs; // Expect logs if (!ptr.logs || ptr.logs.length === 0) return msgs; // Expect publish messeage as first arg - const args = ptr.txn.txn.apaa; + const args = ptr.txn.txn.apaa ?? []; if ( args.length !== 3 || - !encoding.bytes.equals(new Uint8Array(args[0]), AlgorandWormholeCore.publishMessage) + !encoding.bytes.equals(new Uint8Array(args[0]!), AlgorandWormholeCore.publishMessage) ) return msgs; - const sequence = encoding.bignum.decode(ptr.logs[0]); + const sequence = encoding.bignum.decode(ptr.logs[0]!); const emitter = new AlgorandAddress(ptr.txn.txn.snd).toUniversalAddress(); msgs.push({ chain: this.chain, emitter, sequence }); @@ -233,7 +230,7 @@ export class AlgorandWormholeCore suggestedParams, }); seedTxn.fee = seedTxn.fee * 2; - txs.push({ tx: seedTxn, signer: null }); + txs.push({ tx: seedTxn }); // Opt in to the app and rekey to the app address that is using // this as storage @@ -285,10 +282,7 @@ export class AlgorandWormholeCore sequence: vaa.sequence, emitter: vaa.emitterAddress, }); - const { - accounts: [seqAddr], - txs: seqOptInTxs, - } = await AlgorandWormholeCore.maybeCreateStorageTx( + const { accounts: seqAddr, txs: seqOptInTxs } = await AlgorandWormholeCore.maybeCreateStorageTx( client, senderAddr, appid, @@ -299,22 +293,24 @@ export class AlgorandWormholeCore // Get storage account for Guardian set const gsStorage = StorageLogicSig.forGuardianSet(coreId, vaa.guardianSet); - const { - accounts: [guardianAddr], - txs: guardianOptInTxs, - } = await AlgorandWormholeCore.maybeCreateStorageTx( - client, - senderAddr, - coreId, - gsStorage, - suggestedParams, - ); + const { accounts: guardianAddr, txs: guardianOptInTxs } = + await AlgorandWormholeCore.maybeCreateStorageTx( + client, + senderAddr, + coreId, + gsStorage, + suggestedParams, + ); txs.push(...guardianOptInTxs); - let accts: string[] = [seqAddr, guardianAddr]; + let accts: string[] = [...seqAddr, ...guardianAddr]; // Get the Guardian keys - const keys: Uint8Array = await StorageLogicSig.decodeLocalState(client, coreId, guardianAddr); + const keys: Uint8Array = await StorageLogicSig.decodeLocalState( + client, + coreId, + guardianAddr[0]!, + ); // We don't pass the entire payload in but instead just pass it pre-digested. This gets around size // limitations with lsigs AND reduces the cost of the entire operation on a congested network by reducing the @@ -343,7 +339,7 @@ export class AlgorandWormholeCore for (let i = 0; i < sigs.length; i++) { // The first byte of the sig is the relative index of that signature in the signatures array // Use that index to get the appropriate Guardian key - const sig = sigs[i * SIG_LEN]; + const sig = sigs[i * SIG_LEN]!; const key = keys.slice( sig.guardianIndex * GuardianKeyLen + 1, (sig.guardianIndex + 1) * GuardianKeyLen + 1, @@ -386,7 +382,7 @@ export class AlgorandWormholeCore suggestedParams, }); appTxn.fee = appTxn.fee * (2 + numTxns); // Was 1 - txs.push({ tx: appTxn, signer: null }); + txs.push({ tx: appTxn }); return { accounts: accts, txs }; } diff --git a/platforms/algorand/protocols/core/src/storage.ts b/platforms/algorand/protocols/core/src/storage.ts index 29dd2f0cdd..73cda234dd 100644 --- a/platforms/algorand/protocols/core/src/storage.ts +++ b/platforms/algorand/protocols/core/src/storage.ts @@ -155,13 +155,13 @@ export const StorageLogicSig = { * @returns Promise with Uint8Array of data squirreled away */ decodeLocalState: async (client: Algodv2, appId: bigint, address: string) => { - let appState: modelsv2.ApplicationLocalState | undefined; + let appState: modelsv2.ApplicationLocalState; try { const ai = await client .accountApplicationInformation(address, safeBigIntToNumber(appId)) .do(); const acctAppInfo = modelsv2.AccountApplicationResponse.from_obj_for_encoding(ai); - appState = acctAppInfo.appLocalState; + appState = acctAppInfo.appLocalState!; } catch (e) { return new Uint8Array(); } @@ -173,19 +173,19 @@ export const StorageLogicSig = { // so first put them in a map by numeric key // then iterate over keys to concat them in the right order let vals = new Map(); - for (const kv of appState.keyValue) { + for (const kv of appState!.keyValue!) { if (kv.key === metaKey) continue; // Take the first byte off the key to be the // numeric index - const key = encoding.b64.decode(kv.key)[0]; + const key = encoding.b64.decode(kv.key)[0]!; const value = encoding.b64.decode(kv.value.bytes); vals.set(key, value); } const byteArrays: Uint8Array[] = []; for (let i = 0; i < MAX_KEYS; i++) { - if (vals.has(i)) byteArrays.push(vals.get(i)); + if (vals.has(i)) byteArrays.push(vals.get(i)!); } return encoding.bytes.concat(...byteArrays); diff --git a/platforms/algorand/protocols/tokenBridge/src/tokenBridge.ts b/platforms/algorand/protocols/tokenBridge/src/tokenBridge.ts index 2e35a74841..a3522992db 100644 --- a/platforms/algorand/protocols/tokenBridge/src/tokenBridge.ts +++ b/platforms/algorand/protocols/tokenBridge/src/tokenBridge.ts @@ -12,7 +12,6 @@ import { TokenBridge, TokenId, UniversalAddress, - UnsignedTransaction, encoding, serialize, toChain, @@ -196,10 +195,7 @@ export class AlgorandTokenBridge // Creates a Token Attestation VAA containing metadata about // the token that may be submitted to a Token Bridge on another chain // to allow it to create a wrapped version of the token - async *createAttestation( - token: AnyAlgorandAddress, - payer: AnyAlgorandAddress, - ): AsyncGenerator> { + async *createAttestation(token: TokenAddress, payer?: AnyAlgorandAddress) { if (!payer) throw new Error("Payer required to create attestation"); const senderAddr = new AlgorandAddress(payer).toString(); @@ -261,7 +257,7 @@ export class AlgorandTokenBridge appArgs: [AlgorandTokenBridge.noop], suggestedParams, }); - txs.push({ tx: firstTxn, signer: null }); + txs.push({ tx: firstTxn }); const mfee = await this.coreBridge.getMessageFee(); if (mfee > BigInt(0)) { @@ -271,10 +267,10 @@ export class AlgorandTokenBridge to: this.tokenBridgeAddress, amount: mfee, }); - txs.push({ tx: feeTxn, signer: null }); + txs.push({ tx: feeTxn }); } - let accts: string[] = [emitterAddr, creatorAddr, this.coreAppAddress]; + let accts: string[] = [emitterAddr!, creatorAddr, this.coreAppAddress]; if (creatorAcctInfo) { accts.push(creatorAcctInfo.address); @@ -295,7 +291,7 @@ export class AlgorandTokenBridge } else { appTxn.fee *= 2; } - txs.push({ tx: appTxn, signer: null }); + txs.push({ tx: appTxn }); for (const utxn of txs) { yield this.createUnsignedTx(utxn, "TokenBridge.createAttestation", true); @@ -306,9 +302,10 @@ export class AlgorandTokenBridge // to create the wrapped token represented by the data in the VAA async *submitAttestation( vaa: TokenBridge.AttestVAA, - sender: AnyAlgorandAddress, + sender?: AnyAlgorandAddress, suggestedParams?: SuggestedParams, ): AsyncGenerator> { + if (!sender) throw new Error("Sender required to submit attestation"); if (!suggestedParams) suggestedParams = await this.connection.getTransactionParams().do(); const senderAddr = sender.toString(); @@ -374,7 +371,7 @@ export class AlgorandTokenBridge }), }); - txs[txs.length - 1].tx.fee = txs[txs.length - 1].tx.fee * 2; + txs[txs.length - 1]!.tx.fee = txs[txs.length - 1]!.tx.fee * 2; for (const utxn of txs) { yield this.createUnsignedTx(utxn, "TokenBridge.submitAttestation", true); @@ -441,7 +438,6 @@ export class AlgorandTokenBridge amount: msgFee, suggestedParams, }), - signer: null, }); if (!wormhole) { @@ -459,7 +455,7 @@ export class AlgorandTokenBridge nativeStorageAccount, suggestedParams, ); - creator = address; + creator = address!; txs.push(...txs); } @@ -474,7 +470,7 @@ export class AlgorandTokenBridge amount: 100000, suggestedParams, }); - txs.unshift({ tx: payTxn, signer: null }); + txs.unshift({ tx: payTxn }); // The tokenid app needs to do the optin since it has signature authority let txn = makeApplicationCallTxnFromObject({ from: senderAddr, @@ -486,7 +482,7 @@ export class AlgorandTokenBridge suggestedParams, }); txn.fee *= 2; - txs.unshift({ tx: txn, signer: null }); + txs.unshift({ tx: txn }); } const t = makeApplicationCallTxnFromObject({ @@ -496,7 +492,7 @@ export class AlgorandTokenBridge appArgs: [AlgorandTokenBridge.noop], suggestedParams, }); - txs.push({ tx: t, signer: null }); + txs.push({ tx: t }); let accounts: string[] = []; if (assetId === 0) { @@ -506,8 +502,8 @@ export class AlgorandTokenBridge amount: qty, suggestedParams, }); - txs.push({ tx: t, signer: null }); - accounts = [emitterAddr, creator, creator]; + txs.push({ tx: t }); + accounts = [emitterAddr!, creator, creator]; } else { const t = makeAssetTransferTxnWithSuggestedParamsFromObject({ from: senderAddr, @@ -516,10 +512,10 @@ export class AlgorandTokenBridge assetIndex: assetId, suggestedParams, }); - txs.push({ tx: t, signer: null }); + txs.push({ tx: t }); accounts = creatorAcct?.address - ? [emitterAddr, creator, creatorAcct.address] - : [emitterAddr, creator]; + ? [emitterAddr!, creator, creatorAcct.address] + : [emitterAddr!, creator]; } const args = [ @@ -544,7 +540,7 @@ export class AlgorandTokenBridge suggestedParams, }); acTxn.fee *= 2; - txs.push({ tx: acTxn, signer: null }); + txs.push({ tx: acTxn }); for (const utxn of txs) { yield this.createUnsignedTx(utxn, "TokenBridge.transfer", true); @@ -621,7 +617,6 @@ export class AlgorandTokenBridge suggestedParams, to: senderAddr, }), - signer: null, }); } } @@ -638,24 +633,23 @@ export class AlgorandTokenBridge txs.push({ tx: makeApplicationCallTxnFromObject(appCallObj), - signer: null, }); // We need to cover the inner transactions - txs[txs.length - 1].tx.fee = - txs[txs.length - 1].tx.fee * + txs[txs.length - 1]!.tx.fee = + txs[txs.length - 1]!.tx.fee * (vaa.payloadName === "Transfer" && vaa.payload.fee !== undefined && vaa.payload.fee === 0n ? 2 : 3); if (vaa.payloadName === "TransferWithPayload") { - txs[txs.length - 1].tx.appForeignApps = [appId]; + txs[txs.length - 1]!.tx.appForeignApps = [appId]; txs.push({ tx: makeApplicationCallTxnFromObject({ appArgs: [ TransferMethodSelector.getSelector(), - (TransferMethodSelector.args[0].type as ABIType).encode(serialize(vaa)), + (TransferMethodSelector.args[0]!.type as ABIType).encode(serialize(vaa)), ], appIndex: appId, foreignAssets: foreignAssets, @@ -663,7 +657,6 @@ export class AlgorandTokenBridge onComplete: OnApplicationComplete.NoOpOC, suggestedParams, }), - signer: null, }); } @@ -683,7 +676,7 @@ export class AlgorandTokenBridge try { const acctInfoResp = await client.accountAssetInformation(address, asset).do(); const acctInfo = modelsv2.AccountAssetResponse.from_obj_for_encoding(acctInfoResp); - return acctInfo.assetHolding.amount > 0; + return (acctInfo.assetHolding?.amount ?? 0) > 0; } catch {} return false; } diff --git a/platforms/cosmwasm/src/platform.ts b/platforms/cosmwasm/src/platform.ts index 96876bc858..c2cfec5d6b 100644 --- a/platforms/cosmwasm/src/platform.ts +++ b/platforms/cosmwasm/src/platform.ts @@ -145,7 +145,7 @@ export class CosmwasmPlatform extends PlatformContext(network: N, chain: C): string { - return chainToNativeDenoms.get(network, chain); + return chainToNativeDenoms.get(network, chain)!; } static async sendWait(chain: Chain, rpc: CosmWasmClient, stxns: SignedTx[]): Promise { diff --git a/platforms/evm/protocols/cctp/src/circleBridge.ts b/platforms/evm/protocols/cctp/src/circleBridge.ts index 1fe8abdd7b..a2b6222c23 100644 --- a/platforms/evm/protocols/cctp/src/circleBridge.ts +++ b/platforms/evm/protocols/cctp/src/circleBridge.ts @@ -123,7 +123,7 @@ export class EvmCircleBridge .toUniversalAddress() .toUint8Array(); - const tokenAddr = circle.usdcContract.get(this.network, this.chain); + const tokenAddr = circle.usdcContract.get(this.network, this.chain)!; const tokenContract = EvmPlatform.getTokenImplementation( this.provider, @@ -149,7 +149,7 @@ export class EvmCircleBridge const txReq = await this.tokenMessenger.depositForBurn.populateTransaction( amount, - circle.circleChainId(recipient.chain as circle.CircleChain), + circle.circleChainId.get(recipient.chain), recipientAddress, tokenAddr, ); diff --git a/platforms/solana/protocols/cctp/src/circleBridge.ts b/platforms/solana/protocols/cctp/src/circleBridge.ts index 837388413b..e22f7ed676 100644 --- a/platforms/solana/protocols/cctp/src/circleBridge.ts +++ b/platforms/solana/protocols/cctp/src/circleBridge.ts @@ -188,7 +188,11 @@ export class SolanaCircleBridge const messageLogs = [ ...messageTransmitterParser.parseLogs(tx.meta.logMessages || []), ]; - const message = new Uint8Array(messageLogs[0].data['message'] as Buffer); + + if (messageLogs.length === 0) + throw new Error('No CircleTransferMessage found'); + + const message = new Uint8Array(messageLogs[0]!.data['message'] as Buffer); const [msg, hash] = CircleBridge.deserialize(message); const { payload: body } = msg; diff --git a/platforms/solana/src/testing/sendSigner.ts b/platforms/solana/src/testing/sendSigner.ts index 19188e9c62..79f356d3df 100644 --- a/platforms/solana/src/testing/sendSigner.ts +++ b/platforms/solana/src/testing/sendSigner.ts @@ -64,7 +64,7 @@ export class SolanaSendSigner< if (e.message.includes('Blockhash not found')) return true; // Find the log message with the error details - const loggedErr = e.logs.find((log) => + const loggedErr = e.logs?.find((log) => log.startsWith('Program log: Error: '), ); diff --git a/tsconfig.json b/tsconfig.json index 80d8fba510..e494c82f0d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,8 +16,8 @@ // Strict Checks "alwaysStrict": true, "noImplicitAny": true, - "strictNullChecks": false, - "strictPropertyInitialization": false, + "strictNullChecks": true, + "strictPropertyInitialization": true, "useUnknownInCatchVariables": true, "strictFunctionTypes": true, "noImplicitThis": true, diff --git a/turbo.json b/turbo.json index 5b7642082d..8635a96e63 100644 --- a/turbo.json +++ b/turbo.json @@ -1,5 +1,6 @@ { "$schema": "https://turbo.build/schema.json", + "globalDependencies": ["tsconfig.json"], "pipeline": { "build": { "dependsOn": ["^build"],