From c900da1877e58274c5c0b8d89d9f4869f7ee93d4 Mon Sep 17 00:00:00 2001 From: J M Rossy Date: Mon, 6 May 2024 16:44:46 -0400 Subject: [PATCH] Workaround TS bug in Safe protocol-lib (#3717) ### Description Replace SDK's `gnosisSafe.ts` utils file with a pure JS version to avoid TS checks on Safe lib which has an incorrectly placed TS file in it's lib dist folder. ### Drive-by changes Fix assert import ## Related issues https://github.com/safe-global/safe-core-sdk/issues/805 --- .changeset/tricky-walls-care.md | 5 +++ typescript/infra/scripts/safe-delegate.ts | 1 + .../infra/src/govern/HyperlaneAppGovernor.ts | 3 +- typescript/infra/src/govern/multisend.ts | 9 ++--- typescript/sdk/package.json | 4 +-- typescript/sdk/src/hook/read.ts | 2 +- typescript/sdk/src/index.ts | 33 +++++++++---------- .../ethersV5/EV5GnosisSafeTxSubmitter.ts | 25 ++++---------- .../EV5InterchainAccountTxTransformer.ts | 9 ++--- .../utils/{gnosisSafe.ts => gnosisSafe.js} | 30 +++++------------ 10 files changed, 49 insertions(+), 72 deletions(-) create mode 100644 .changeset/tricky-walls-care.md rename typescript/sdk/src/utils/{gnosisSafe.ts => gnosisSafe.js} (70%) diff --git a/.changeset/tricky-walls-care.md b/.changeset/tricky-walls-care.md new file mode 100644 index 0000000000..9033ccad33 --- /dev/null +++ b/.changeset/tricky-walls-care.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/sdk': patch +--- + +Workaround TS bug in Safe protocol-lib diff --git a/typescript/infra/scripts/safe-delegate.ts b/typescript/infra/scripts/safe-delegate.ts index 74c08d2101..f39b94ecef 100644 --- a/typescript/infra/scripts/safe-delegate.ts +++ b/typescript/infra/scripts/safe-delegate.ts @@ -4,6 +4,7 @@ import { LedgerSigner } from '@ethersproject/hardware-wallets'; import '@ethersproject/hardware-wallets/thirdparty'; import { AddSafeDelegateProps } from '@safe-global/api-kit'; +// @ts-ignore import { getSafeDelegates, getSafeService } from '@hyperlane-xyz/sdk'; import { getChains } from '../config/registry.js'; diff --git a/typescript/infra/src/govern/HyperlaneAppGovernor.ts b/typescript/infra/src/govern/HyperlaneAppGovernor.ts index 78256d0909..1da898ab13 100644 --- a/typescript/infra/src/govern/HyperlaneAppGovernor.ts +++ b/typescript/infra/src/govern/HyperlaneAppGovernor.ts @@ -11,8 +11,9 @@ import { InterchainAccount, OwnableConfig, OwnerViolation, - canProposeSafeTransactions, } from '@hyperlane-xyz/sdk'; +// @ts-ignore +import { canProposeSafeTransactions } from '@hyperlane-xyz/sdk'; import { Address, CallData, diff --git a/typescript/infra/src/govern/multisend.ts b/typescript/infra/src/govern/multisend.ts index 3d173cb2b0..348762cfaa 100644 --- a/typescript/infra/src/govern/multisend.ts +++ b/typescript/infra/src/govern/multisend.ts @@ -1,9 +1,6 @@ -import { - ChainName, - MultiProvider, - getSafe, - getSafeService, -} from '@hyperlane-xyz/sdk'; +import { ChainName, MultiProvider } from '@hyperlane-xyz/sdk'; +// @ts-ignore +import { getSafe, getSafeService } from '@hyperlane-xyz/sdk'; import { CallData } from '@hyperlane-xyz/utils'; export abstract class MultiSend { diff --git a/typescript/sdk/package.json b/typescript/sdk/package.json index a54369eb98..6dff23176b 100644 --- a/typescript/sdk/package.json +++ b/typescript/sdk/package.json @@ -66,8 +66,8 @@ ], "license": "Apache-2.0", "scripts": { - "build": "yarn build:fixSafeGlobalLib; tsc", - "build:fixSafeGlobalLib": "rm -rf ../../node_modules/@safe-global/protocol-kit/dist/typechain/src/web3-v1/**/Multi_send.ts", + "build": "tsc && yarn copy-js", + "copy-js": "cp ./src/utils/*.js ./dist/utils", "dev": "tsc --watch", "check": "tsc --noEmit", "clean": "rm -rf ./dist ./cache", diff --git a/typescript/sdk/src/hook/read.ts b/typescript/sdk/src/hook/read.ts index 2f49bf9451..304a7a54cc 100644 --- a/typescript/sdk/src/hook/read.ts +++ b/typescript/sdk/src/hook/read.ts @@ -1,4 +1,3 @@ -import { assert } from 'console'; import { ethers, providers } from 'ethers'; import { @@ -18,6 +17,7 @@ import { import { Address, WithAddress, + assert, concurrentMap, eqAddress, rootLogger, diff --git a/typescript/sdk/src/index.ts b/typescript/sdk/src/index.ts index 0fc7ad3edb..d0e2c66c94 100644 --- a/typescript/sdk/src/index.ts +++ b/typescript/sdk/src/index.ts @@ -236,17 +236,6 @@ export { InterchainQueryDeployer, } from './middleware/query/InterchainQueryDeployer.js'; export { interchainQueryFactories } from './middleware/query/contracts.js'; -export { TxSubmitterBuilder } from './providers/transactions/submitter/builder/TxSubmitterBuilder.js'; -export { EV5GnosisSafeTxSubmitter } from './providers/transactions/submitter/ethersV5/EV5GnosisSafeTxSubmitter.js'; -export { EV5ImpersonatedAccountTxSubmitter } from './providers/transactions/submitter/ethersV5/EV5ImpersonatedAccountTxSubmitter.js'; -export { EV5JsonRpcTxSubmitter } from './providers/transactions/submitter/ethersV5/EV5JsonRpcTxSubmitter.js'; -export { EV5TxSubmitterInterface } from './providers/transactions/submitter/ethersV5/EV5TxSubmitterInterface.js'; -export { TxSubmitterInterface } from './providers/transactions/submitter/TxSubmitterInterface.js'; -export { TxSubmitterType } from './providers/transactions/submitter/TxSubmitterTypes.js'; -export { EV5InterchainAccountTxTransformer } from './providers/transactions/transformer/ethersV5/EV5InterchainAccountTxTransformer.js'; -export { EV5TxTransformerInterface } from './providers/transactions/transformer/ethersV5/EV5TxTransformerInterface.js'; -export { TxTransformerInterface } from './providers/transactions/transformer/TxTransformerInterface.js'; -export { TxTransformerType } from './providers/transactions/transformer/TxTransformerTypes.js'; export { MultiProtocolProvider, MultiProtocolProviderOptions, @@ -314,6 +303,17 @@ export { defaultViemProviderBuilder, protocolToDefaultProviderBuilder, } from './providers/providerBuilders.js'; +export { TxSubmitterInterface } from './providers/transactions/submitter/TxSubmitterInterface.js'; +export { TxSubmitterType } from './providers/transactions/submitter/TxSubmitterTypes.js'; +export { TxSubmitterBuilder } from './providers/transactions/submitter/builder/TxSubmitterBuilder.js'; +export { EV5GnosisSafeTxSubmitter } from './providers/transactions/submitter/ethersV5/EV5GnosisSafeTxSubmitter.js'; +export { EV5ImpersonatedAccountTxSubmitter } from './providers/transactions/submitter/ethersV5/EV5ImpersonatedAccountTxSubmitter.js'; +export { EV5JsonRpcTxSubmitter } from './providers/transactions/submitter/ethersV5/EV5JsonRpcTxSubmitter.js'; +export { EV5TxSubmitterInterface } from './providers/transactions/submitter/ethersV5/EV5TxSubmitterInterface.js'; +export { TxTransformerInterface } from './providers/transactions/transformer/TxTransformerInterface.js'; +export { TxTransformerType } from './providers/transactions/transformer/TxTransformerTypes.js'; +export { EV5InterchainAccountTxTransformer } from './providers/transactions/transformer/ethersV5/EV5InterchainAccountTxTransformer.js'; +export { EV5TxTransformerInterface } from './providers/transactions/transformer/ethersV5/EV5TxTransformerInterface.js'; export { GasRouterDeployer } from './router/GasRouterDeployer.js'; export { HyperlaneRouterChecker } from './router/HyperlaneRouterChecker.js'; export { HyperlaneRouterDeployer } from './router/HyperlaneRouterDeployer.js'; @@ -453,12 +453,7 @@ export { setFork, stopImpersonatingAccount, } from './utils/fork.js'; -export { - getSafeService, - getSafe, - getSafeDelegates, - canProposeSafeTransactions, -} from './utils/gnosisSafe.js'; + export { multisigIsmVerificationCost } from './utils/ism.js'; export { SealevelAccountDataWrapper, @@ -483,3 +478,7 @@ export { TokenRouterConfigSchema as tokenRouterConfigSchema, } from './token/schemas.js'; export { TokenRouterConfig, WarpRouteDeployConfig } from './token/types.js'; + +// prettier-ignore +// @ts-ignore +export { canProposeSafeTransactions, getSafe, getSafeDelegates, getSafeService } from './utils/gnosisSafe.js'; diff --git a/typescript/sdk/src/providers/transactions/submitter/ethersV5/EV5GnosisSafeTxSubmitter.ts b/typescript/sdk/src/providers/transactions/submitter/ethersV5/EV5GnosisSafeTxSubmitter.ts index de7d3b18ad..9975f560d3 100644 --- a/typescript/sdk/src/providers/transactions/submitter/ethersV5/EV5GnosisSafeTxSubmitter.ts +++ b/typescript/sdk/src/providers/transactions/submitter/ethersV5/EV5GnosisSafeTxSubmitter.ts @@ -1,16 +1,10 @@ -import SafeApiKit from '@safe-global/api-kit'; -import Safe, { EthSafeSignature } from '@safe-global/protocol-kit'; -import { - MetaTransactionData, - SafeTransactionData, -} from '@safe-global/safe-core-sdk-types'; -import assert from 'assert'; import { PopulatedTransaction } from 'ethers'; import { Logger } from 'pino'; -import { Address, rootLogger } from '@hyperlane-xyz/utils'; +import { Address, assert, rootLogger } from '@hyperlane-xyz/utils'; import { ChainName } from '../../../../types.js'; +// @ts-ignore import { getSafe, getSafeService } from '../../../../utils/gnosisSafe.js'; import { MultiProvider } from '../../../MultiProvider.js'; import { TxSubmitterType } from '../TxSubmitterTypes.js'; @@ -36,19 +30,16 @@ export class EV5GnosisSafeTxSubmitter implements EV5TxSubmitterInterface { ) {} public async submit(...txs: PopulatedTransaction[]): Promise { - const safe: Safe.default = await getSafe( + const safe = await getSafe( this.chain, this.multiProvider, this.props.safeAddress, ); - const safeService: SafeApiKit.default = getSafeService( - this.chain, - this.multiProvider, - ); + const safeService = await getSafeService(this.chain, this.multiProvider); const nextNonce: number = await safeService.getNextNonce( this.props.safeAddress, ); - const safeTransactionBatch: MetaTransactionData[] = txs.map( + const safeTransactionBatch: any[] = txs.map( ({ to, data, value }: PopulatedTransaction) => { assert( to && data, @@ -61,14 +52,12 @@ export class EV5GnosisSafeTxSubmitter implements EV5TxSubmitterInterface { safeTransactionData: safeTransactionBatch, options: { nonce: nextNonce }, }); - const safeTransactionData: SafeTransactionData = safeTransaction.data; + const safeTransactionData: any = safeTransaction.data; const safeTxHash: string = await safe.getTransactionHash(safeTransaction); const senderAddress: Address = await this.multiProvider.getSignerAddress( this.chain, ); - const safeSignature: EthSafeSignature = await safe.signTransactionHash( - safeTxHash, - ); + const safeSignature: any = await safe.signTransactionHash(safeTxHash); const senderSignature: string = safeSignature.data; this.logger.debug( diff --git a/typescript/sdk/src/providers/transactions/transformer/ethersV5/EV5InterchainAccountTxTransformer.ts b/typescript/sdk/src/providers/transactions/transformer/ethersV5/EV5InterchainAccountTxTransformer.ts index 2f5e138bc3..2e2ddfb520 100644 --- a/typescript/sdk/src/providers/transactions/transformer/ethersV5/EV5InterchainAccountTxTransformer.ts +++ b/typescript/sdk/src/providers/transactions/transformer/ethersV5/EV5InterchainAccountTxTransformer.ts @@ -1,8 +1,7 @@ -import assert from 'assert'; import { PopulatedTransaction } from 'ethers'; import { Logger } from 'pino'; -import { CallData, rootLogger } from '@hyperlane-xyz/utils'; +import { CallData, assert, rootLogger } from '@hyperlane-xyz/utils'; import { InterchainAccount } from '../../../../middleware/account/InterchainAccount.js'; import { AccountConfig } from '../../../../middleware/account/types.js'; @@ -43,10 +42,8 @@ export class EV5InterchainAccountTxTransformer const innerCalls: CallData[] = txs.map( ({ to, data, value }: PopulatedTransaction) => { - assert( - to && data, - 'Invalid PopulatedTransaction: Missing required field to or data.', - ); + assert(to, 'Invalid PopulatedTransaction: Missing to field'); + assert(data, 'Invalid PopulatedTransaction: Missing data field'); return { to, data, value }; }, ); diff --git a/typescript/sdk/src/utils/gnosisSafe.ts b/typescript/sdk/src/utils/gnosisSafe.js similarity index 70% rename from typescript/sdk/src/utils/gnosisSafe.ts rename to typescript/sdk/src/utils/gnosisSafe.js index 803c53f655..892785ec5d 100644 --- a/typescript/sdk/src/utils/gnosisSafe.ts +++ b/typescript/sdk/src/utils/gnosisSafe.js @@ -1,14 +1,9 @@ +// This file is JS because of https://github.com/safe-global/safe-core-sdk/issues/805 import SafeApiKit from '@safe-global/api-kit'; import Safe, { EthersAdapter } from '@safe-global/protocol-kit'; import { ethers } from 'ethers'; -import { MultiProvider } from '../providers/MultiProvider.js'; -import { ChainName } from '../types.js'; - -export function getSafeService( - chain: ChainName, - multiProvider: MultiProvider, -): SafeApiKit.default { +export function getSafeService(chain, multiProvider) { const signer = multiProvider.getSigner(chain); const ethAdapter = new EthersAdapter({ ethers, signerOrProvider: signer }); const txServiceUrl = @@ -18,11 +13,7 @@ export function getSafeService( return new SafeApiKit.default({ txServiceUrl, ethAdapter }); } -export function getSafe( - chain: ChainName, - multiProvider: MultiProvider, - safeAddress: string, -): Promise { +export function getSafe(chain, multiProvider, safeAddress) { const signer = multiProvider.getSigner(chain); const ethAdapter = new EthersAdapter({ ethers, signerOrProvider: signer }); return Safe.default.create({ @@ -31,20 +22,17 @@ export function getSafe( }); } -export async function getSafeDelegates( - service: SafeApiKit.default, - safeAddress: string, -) { +export async function getSafeDelegates(service, safeAddress) { const delegateResponse = await service.getSafeDelegates({ safeAddress }); return delegateResponse.results.map((r) => r.delegate); } export async function canProposeSafeTransactions( - proposer: string, - chain: ChainName, - multiProvider: MultiProvider, - safeAddress: string, -): Promise { + proposer, + chain, + multiProvider, + safeAddress, +) { let safeService; try { safeService = getSafeService(chain, multiProvider);