diff --git a/.github/workflows/api-kit-e2e-test.yml b/.github/workflows/api-kit-e2e-test.yml index 204279753..e62111161 100644 --- a/.github/workflows/api-kit-e2e-test.yml +++ b/.github/workflows/api-kit-e2e-test.yml @@ -25,6 +25,8 @@ jobs: run: yarn build - name: Test + env: + API_KEY: ${{ secrets.API_KEY }} run: | cd packages/api-kit yarn test:ci:ethers diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml index 1de37d672..f16d688b2 100644 --- a/.github/workflows/cla.yml +++ b/.github/workflows/cla.yml @@ -23,7 +23,7 @@ jobs: # branch should not be protected branch: 'main' # user names of users allowed to contribute without CLA - allowlist: germartinez,rmeissner,Uxio0,dasanra,luarx,DaniSomoza,yagopv,JagoFigueroa,bot* + allowlist: rmeissner,Uxio0,dasanra,luarx,yagopv,bot* # the followings are the optional inputs - If the optional inputs are not given, then default values will be taken # enter the remote organization name where the signatures should be stored (Default is storing the signatures in the same repository) diff --git a/guides/integrating-the-safe-core-sdk.md b/guides/integrating-the-safe-core-sdk.md index 6ca51a3f4..ecf651079 100644 --- a/guides/integrating-the-safe-core-sdk.md +++ b/guides/integrating-the-safe-core-sdk.md @@ -264,25 +264,39 @@ type SafeMultisigTransactionResponse = { data?: string operation: number gasToken: string - safeTxGas: number - baseGas: number + safeTxGas: string + baseGas: string gasPrice: string refundReceiver?: string - nonce: number - executionDate: string + nonce: string + executionDate: string | null submissionDate: string modified: string blockNumber?: number - transactionHash: string + transactionHash: string | null safeTxHash: string - executor?: string + executor: string | null + proposer: string | null + proposedByDelegate: string | null isExecuted: boolean - isSuccessful?: boolean - ethGasPrice?: string - gasUsed?: number - fee?: string + isSuccessful: boolean | null + ethGasPrice: string | null + maxFeePerGas: string | null + maxPriorityFeePerGas: string | null + gasUsed: number | null + fee: string | null origin: string - dataDecoded?: string + dataDecoded?: { + method: string + parameters: [ + { + name: string + type: string + value: string + valueDecoded?: ValueDecoded[] + } + ] + } confirmationsRequired: number confirmations?: [ { diff --git a/packages/api-kit/.env-sample b/packages/api-kit/.env-sample new file mode 100644 index 000000000..bf4be2592 --- /dev/null +++ b/packages/api-kit/.env-sample @@ -0,0 +1,2 @@ +# Transaction Service API Key +API_KEY= \ No newline at end of file diff --git a/packages/api-kit/package.json b/packages/api-kit/package.json index 419790951..0d38fb7e3 100644 --- a/packages/api-kit/package.json +++ b/packages/api-kit/package.json @@ -1,6 +1,6 @@ { "name": "@safe-global/api-kit", - "version": "3.0.2", + "version": "4.0.0", "description": "SDK that facilitates the interaction with the Safe Transaction Service API", "types": "dist/src/index.d.ts", "main": "dist/cjs/index.cjs", @@ -46,7 +46,7 @@ ], "homepage": "https://github.com/safe-global/safe-core-sdk#readme", "devDependencies": { - "@safe-global/relay-kit": "^4.0.4", + "@safe-global/relay-kit": "^4.0.5", "@safe-global/testing-kit": "^0.2.1", "@types/chai": "^4.3.20", "@types/chai-as-promised": "^7.1.8", @@ -65,8 +65,8 @@ "tsconfig-paths": "^4.2.0" }, "dependencies": { - "@safe-global/protocol-kit": "^6.0.5", - "@safe-global/types-kit": "^2.0.1", + "@safe-global/protocol-kit": "^6.1.0", + "@safe-global/types-kit": "^3.0.0", "node-fetch": "^2.7.0", "viem": "^2.21.8" } diff --git a/packages/api-kit/src/SafeApiKit.ts b/packages/api-kit/src/SafeApiKit.ts index 88b5cdb74..b51ca2ed5 100644 --- a/packages/api-kit/src/SafeApiKit.ts +++ b/packages/api-kit/src/SafeApiKit.ts @@ -37,7 +37,7 @@ import { TokenInfoResponse, TransferListResponse } from '@safe-global/api-kit/types/safeTransactionServiceTypes' -import { HttpMethod, sendRequest } from '@safe-global/api-kit/utils/httpRequests' +import { HttpMethod, HttpRequest, sendRequest } from '@safe-global/api-kit/utils/httpRequests' import { signDelegate } from '@safe-global/api-kit/utils/signDelegate' import { validateEip3770Address, validateEthereumAddress } from '@safe-global/protocol-kit' import { @@ -50,7 +50,7 @@ import { SafeOperationResponse, UserOperationV06 } from '@safe-global/types-kit' -import { TRANSACTION_SERVICE_URLS } from './utils/config' +import { getTransactionServiceUrl } from './utils/config' import { isEmptyData } from './utils' import { getAddSafeOperationProps, isSafeOperation } from './utils/safeOperation' import { QUERY_PARAMS_MAP } from './utils/queryParamsMap' @@ -60,19 +60,43 @@ export interface SafeApiKitConfig { chainId: bigint /** txServiceUrl - Safe Transaction Service URL */ txServiceUrl?: string + /** + * apiKey - The API key to access the Safe Transaction Service. + * - Required if txServiceUrl is undefined + * - Required if txServiceUrl contains "safe.global" or "5afe.dev" + * - Optional otherwise + */ + apiKey?: string } class SafeApiKit { #chainId: bigint + #apiKey?: string #txServiceBaseUrl: string - constructor({ chainId, txServiceUrl }: SafeApiKitConfig) { + constructor({ chainId, txServiceUrl, apiKey }: SafeApiKitConfig) { this.#chainId = chainId if (txServiceUrl) { + // If txServiceUrl contains safe.global or 5afe.dev, apiKey is mandatory + if ( + (txServiceUrl.includes('api.safe.global') || txServiceUrl.includes('api.5afe.dev')) && + !apiKey + ) { + throw new Error( + 'apiKey is mandatory when using api.safe.global or api.5afe.dev domains. Please obtain your API key at https://developer.safe.global.' + ) + } this.#txServiceBaseUrl = txServiceUrl } else { - const url = TRANSACTION_SERVICE_URLS[chainId.toString()] + // If txServiceUrl is not defined, apiKey is mandatory + if (!apiKey) { + throw new Error( + 'apiKey is mandatory when txServiceUrl is not defined. Please obtain your API key at https://developer.safe.global.' + ) + } + + const url = getTransactionServiceUrl(chainId) if (!url) { throw new TypeError( `There is no transaction service available for chainId ${chainId}. Please set the txServiceUrl property to use a custom transaction service.` @@ -81,6 +105,8 @@ class SafeApiKit { this.#txServiceBaseUrl = url } + + this.#apiKey = apiKey } #isValidAddress(address: string) { @@ -119,13 +145,17 @@ class SafeApiKit { }) } + async #api(request: HttpRequest): Promise { + return sendRequest(request, this.#apiKey) + } + /** * Returns the information and configuration of the service. * * @returns The information and configuration of the service */ async getServiceInfo(): Promise { - return sendRequest({ + return this.#api({ url: `${this.#txServiceBaseUrl}/v1/about`, method: HttpMethod.Get }) @@ -137,7 +167,7 @@ class SafeApiKit { * @returns The list of Safe singletons */ async getServiceSingletonsInfo(): Promise { - return sendRequest({ + return this.#api({ url: `${this.#txServiceBaseUrl}/v1/about/singletons`, method: HttpMethod.Get }) @@ -164,7 +194,7 @@ class SafeApiKit { dataDecoderRequest.to = to } - return sendRequest({ + return this.#api({ url: `${this.#txServiceBaseUrl}/v1/data-decoder/`, method: HttpMethod.Post, body: dataDecoderRequest @@ -210,7 +240,7 @@ class SafeApiKit { url.searchParams.set('offset', offset.toString()) } - return sendRequest({ + return this.#api({ url: url.toString(), method: HttpMethod.Get }) @@ -256,7 +286,7 @@ class SafeApiKit { label, signature } - return sendRequest({ + return this.#api({ url: `${this.#txServiceBaseUrl}/v2/delegates/`, method: HttpMethod.Post, body @@ -289,7 +319,7 @@ class SafeApiKit { const { address: delegator } = this.#getEip3770Address(delegatorAddress) const signature = await signDelegate(signer, delegate, this.#chainId) - return sendRequest({ + return this.#api({ url: `${this.#txServiceBaseUrl}/v2/delegates/${delegate}`, method: HttpMethod.Delete, body: { @@ -309,7 +339,7 @@ class SafeApiKit { throw new Error('Invalid messageHash') } - return sendRequest({ + return this.#api({ url: `${this.#txServiceBaseUrl}/v1/messages/${messageHash}/`, method: HttpMethod.Get }) @@ -334,7 +364,7 @@ class SafeApiKit { // Check if options are given and add query parameters this.#addUrlQueryParams(url, options) - return sendRequest({ + return this.#api({ url: url.toString(), method: HttpMethod.Get }) @@ -351,7 +381,7 @@ class SafeApiKit { throw new Error('Invalid safeAddress') } - return sendRequest({ + return this.#api({ url: `${this.#txServiceBaseUrl}/v1/safes/${safeAddress}/messages/`, method: HttpMethod.Post, body: addMessageOptions @@ -368,7 +398,7 @@ class SafeApiKit { throw new Error('Invalid messageHash or signature') } - return sendRequest({ + return this.#api({ url: `${this.#txServiceBaseUrl}/v1/messages/${messageHash}/signatures/`, method: HttpMethod.Post, body: { @@ -390,7 +420,7 @@ class SafeApiKit { throw new Error('Invalid owner address') } const { address } = this.#getEip3770Address(ownerAddress) - return sendRequest({ + return this.#api({ url: `${this.#txServiceBaseUrl}/v1/owners/${address}/safes/`, method: HttpMethod.Get }) @@ -409,7 +439,7 @@ class SafeApiKit { throw new Error('Invalid module address') } const { address } = this.#getEip3770Address(moduleAddress) - return sendRequest({ + return this.#api({ url: `${this.#txServiceBaseUrl}/v1/modules/${address}/safes/`, method: HttpMethod.Get }) @@ -427,8 +457,8 @@ class SafeApiKit { if (safeTxHash === '') { throw new Error('Invalid safeTxHash') } - return sendRequest({ - url: `${this.#txServiceBaseUrl}/v1/multisig-transactions/${safeTxHash}/`, + return this.#api({ + url: `${this.#txServiceBaseUrl}/v2/multisig-transactions/${safeTxHash}/`, method: HttpMethod.Get }) } @@ -446,7 +476,7 @@ class SafeApiKit { if (safeTxHash === '') { throw new Error('Invalid safeTxHash') } - return sendRequest({ + return this.#api({ url: `${this.#txServiceBaseUrl}/v1/multisig-transactions/${safeTxHash}/confirmations/`, method: HttpMethod.Get }) @@ -470,7 +500,7 @@ class SafeApiKit { if (signature === '') { throw new Error('Invalid signature') } - return sendRequest({ + return this.#api({ url: `${this.#txServiceBaseUrl}/v1/multisig-transactions/${safeTxHash}/confirmations/`, method: HttpMethod.Post, body: { @@ -492,7 +522,7 @@ class SafeApiKit { throw new Error('Invalid Safe address') } const { address } = this.#getEip3770Address(safeAddress) - return sendRequest({ + return this.#api({ url: `${this.#txServiceBaseUrl}/v1/safes/${address}/`, method: HttpMethod.Get }).then((response: any) => { @@ -521,7 +551,7 @@ class SafeApiKit { throw new Error('Invalid Safe address') } const { address } = this.#getEip3770Address(safeAddress) - return sendRequest({ + return this.#api({ url: `${this.#txServiceBaseUrl}/v1/safes/${address}/creation/`, method: HttpMethod.Get }).then((response: any) => { @@ -554,7 +584,7 @@ class SafeApiKit { throw new Error('Invalid Safe address') } const { address } = this.#getEip3770Address(safeAddress) - return sendRequest({ + return this.#api({ url: `${this.#txServiceBaseUrl}/v1/safes/${address}/multisig-transactions/estimations/`, method: HttpMethod.Post, body: safeTransaction @@ -587,8 +617,8 @@ class SafeApiKit { if (safeTxHash === '') { throw new Error('Invalid safeTxHash') } - return sendRequest({ - url: `${this.#txServiceBaseUrl}/v1/safes/${safe}/multisig-transactions/`, + return this.#api({ + url: `${this.#txServiceBaseUrl}/v2/safes/${safe}/multisig-transactions/`, method: HttpMethod.Post, body: { ...safeTransactionData, @@ -622,7 +652,7 @@ class SafeApiKit { // Check if options are given and add query parameters this.#addUrlQueryParams(url, options) - return sendRequest({ + return this.#api({ url: url.toString(), method: HttpMethod.Get }) @@ -651,7 +681,7 @@ class SafeApiKit { // Check if options are given and add query parameters this.#addUrlQueryParams(url, options) - return sendRequest({ + return this.#api({ url: url.toString(), method: HttpMethod.Get }) @@ -675,12 +705,12 @@ class SafeApiKit { } const { address } = this.#getEip3770Address(safeAddress) - const url = new URL(`${this.#txServiceBaseUrl}/v1/safes/${address}/multisig-transactions/`) + const url = new URL(`${this.#txServiceBaseUrl}/v2/safes/${address}/multisig-transactions/`) // Check if options are given and add query parameters this.#addUrlQueryParams(url, options) - return sendRequest({ + return this.#api({ url: url.toString(), method: HttpMethod.Get }) @@ -709,7 +739,7 @@ class SafeApiKit { const nonce = currentNonce ? currentNonce : (await this.getSafeInfo(address)).nonce const url = new URL( - `${this.#txServiceBaseUrl}/v1/safes/${address}/multisig-transactions/?executed=false&nonce__gte=${nonce}` + `${this.#txServiceBaseUrl}/v2/safes/${address}/multisig-transactions/?executed=false&nonce__gte=${nonce}` ) if (hasConfirmations) { @@ -728,7 +758,7 @@ class SafeApiKit { url.searchParams.set('offset', offset.toString()) } - return sendRequest({ + return this.#api({ url: url.toString(), method: HttpMethod.Get }) @@ -752,12 +782,12 @@ class SafeApiKit { throw new Error('Invalid Safe address') } const { address } = this.#getEip3770Address(safeAddress) - const url = new URL(`${this.#txServiceBaseUrl}/v1/safes/${address}/all-transactions/`) + const url = new URL(`${this.#txServiceBaseUrl}/v2/safes/${address}/all-transactions/`) // Check if options are given and add query parameters this.#addUrlQueryParams(url, options) - return sendRequest({ + return this.#api({ url: url.toString(), method: HttpMethod.Get }) @@ -802,7 +832,7 @@ class SafeApiKit { // Check if options are given and add query parameters this.#addUrlQueryParams(url, options) - return sendRequest({ + return this.#api({ url: url.toString(), method: HttpMethod.Get }) @@ -821,7 +851,7 @@ class SafeApiKit { throw new Error('Invalid token address') } const { address } = this.#getEip3770Address(tokenAddress) - return sendRequest({ + return this.#api({ url: `${this.#txServiceBaseUrl}/v1/tokens/${address}/`, method: HttpMethod.Get }) @@ -850,7 +880,7 @@ class SafeApiKit { // Check if options are given and add query parameters this.#addUrlQueryParams(url, options) - return sendRequest({ + return this.#api({ url: url.toString(), method: HttpMethod.Get }) @@ -886,7 +916,7 @@ class SafeApiKit { throw new Error('SafeOperation hash must not be empty') } - return sendRequest({ + return this.#api({ url: `${this.#txServiceBaseUrl}/v1/safe-operations/${safeOperationHash}/`, method: HttpMethod.Get }) @@ -947,7 +977,7 @@ class SafeApiKit { const userOperationV06 = userOperation as UserOperationV06 - return sendRequest({ + return this.#api({ url: `${this.#txServiceBaseUrl}/v1/safes/${safeAddress}/safe-operations/`, method: HttpMethod.Post, body: { @@ -1000,7 +1030,7 @@ class SafeApiKit { url.searchParams.set('offset', offset.toString()) } - return sendRequest({ + return this.#api({ url: url.toString(), method: HttpMethod.Get }) @@ -1024,7 +1054,7 @@ class SafeApiKit { if (!signature) { throw new Error('Invalid signature') } - return sendRequest({ + return this.#api({ url: `${this.#txServiceBaseUrl}/v1/safe-operations/${safeOperationHash}/confirmations/`, method: HttpMethod.Post, body: { signature } diff --git a/packages/api-kit/src/utils/config.ts b/packages/api-kit/src/utils/config.ts index 7310f6a8a..ffce90d8a 100644 --- a/packages/api-kit/src/utils/config.ts +++ b/packages/api-kit/src/utils/config.ts @@ -1,23 +1,47 @@ -export const TRANSACTION_SERVICE_URLS: Record = { - '1': 'https://safe-transaction-mainnet.safe.global/api', - '10': 'https://safe-transaction-optimism.safe.global/api', - '56': 'https://safe-transaction-bsc.safe.global/api', - '100': 'https://safe-transaction-gnosis-chain.safe.global/api', - '130': 'https://safe-transaction-unichain.safe.global/api', - '137': 'https://safe-transaction-polygon.safe.global/api', - '196': 'https://safe-transaction-xlayer.safe.global/api', - '324': 'https://safe-transaction-zksync.safe.global/api', - '480': 'https://safe-transaction-worldchain.safe.global/api', - '1101': 'https://safe-transaction-zkevm.safe.global/api', - '5000': 'https://safe-transaction-mantle.safe.global/api', - '8453': 'https://safe-transaction-base.safe.global/api', - '42161': 'https://safe-transaction-arbitrum.safe.global/api', - '42220': 'https://safe-transaction-celo.safe.global/api', - '43114': 'https://safe-transaction-avalanche.safe.global/api', - '59144': 'https://safe-transaction-linea.safe.global/api', - '81457': 'https://safe-transaction-blast.safe.global/api', - '84532': 'https://safe-transaction-base-sepolia.safe.global/api', - '534352': 'https://safe-transaction-scroll.safe.global/api', - '11155111': 'https://safe-transaction-sepolia.safe.global/api', - '1313161554': 'https://safe-transaction-aurora.safe.global/api' +const TRANSACTION_SERVICE_URL = 'https://api.safe.global/tx-service' + +type NetworkShortName = { + shortName: string + chainId: bigint +} + +export const networks: NetworkShortName[] = [ + { chainId: 1n, shortName: 'eth' }, + { chainId: 10n, shortName: 'oeth' }, + { chainId: 56n, shortName: 'bnb' }, + { chainId: 100n, shortName: 'gno' }, + { chainId: 130n, shortName: 'unichain' }, + { chainId: 137n, shortName: 'pol' }, + { chainId: 146n, shortName: 'sonic' }, + { chainId: 196n, shortName: 'okb' }, + { chainId: 232n, shortName: 'lens' }, + { chainId: 324n, shortName: 'zksync' }, + { chainId: 480n, shortName: 'wc' }, + { chainId: 1101n, shortName: 'zkevm' }, + { chainId: 5000n, shortName: 'mantle' }, + { chainId: 8453n, shortName: 'base' }, + { chainId: 10200n, shortName: 'chi' }, + { chainId: 42161n, shortName: 'arb1' }, + { chainId: 43111n, shortName: 'hemi' }, + { chainId: 57073n, shortName: 'ink' }, + { chainId: 80094n, shortName: 'berachain' }, + { chainId: 59144n, shortName: 'linea' }, + { chainId: 42220n, shortName: 'celo' }, + { chainId: 43114n, shortName: 'avax' }, + { chainId: 84532n, shortName: 'basesep' }, + { chainId: 534352n, shortName: 'scr' }, + { chainId: 11155111n, shortName: 'sep' }, + { chainId: 1313161554n, shortName: 'aurora' } +] + +export const getNetworkShortName = (chainId: bigint): string => { + const network = networks.find((n) => n.chainId === chainId) + if (!network) { + throw new Error(`Network with chainId ${chainId} not found`) + } + return network.shortName +} + +export const getTransactionServiceUrl = (chainId: bigint) => { + return `${TRANSACTION_SERVICE_URL}/${getNetworkShortName(chainId)}/api` } diff --git a/packages/api-kit/src/utils/httpRequests.ts b/packages/api-kit/src/utils/httpRequests.ts index 506ca8973..972d19fd7 100644 --- a/packages/api-kit/src/utils/httpRequests.ts +++ b/packages/api-kit/src/utils/httpRequests.ts @@ -4,23 +4,32 @@ export enum HttpMethod { Delete = 'delete' } -interface HttpRequest { +export interface HttpRequest { url: string method: HttpMethod body?: any } -export async function sendRequest({ url, method, body }: HttpRequest): Promise { +export async function sendRequest( + { url, method, body }: HttpRequest, + apiKey?: string +): Promise { const fetch = await (typeof window === 'undefined' ? import('node-fetch').then((m) => m.default) : Promise.resolve(window.fetch)) + const headers: Record = { + Accept: 'application/json', + 'Content-Type': 'application/json' + } + + if (apiKey) { + headers['Authorization'] = `Bearer ${apiKey}` + } + const response = await fetch(url, { method, - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json' - }, + headers, body: JSON.stringify(body) }) diff --git a/packages/api-kit/tests/e2e/addSafeDelegate.test.ts b/packages/api-kit/tests/e2e/addSafeDelegate.test.ts index 957625c9c..156f62994 100644 --- a/packages/api-kit/tests/e2e/addSafeDelegate.test.ts +++ b/packages/api-kit/tests/e2e/addSafeDelegate.test.ts @@ -19,7 +19,7 @@ let delegatorAddress: Address describe('addSafeDelegate', () => { before(() => { - safeApiKit = getApiKit('https://safe-transaction-sepolia.staging.5afe.dev/api') + safeApiKit = getApiKit() signer = createWalletClient({ chain: sepolia, transport: http(), diff --git a/packages/api-kit/tests/e2e/addSafeOperation.test.ts b/packages/api-kit/tests/e2e/addSafeOperation.test.ts index e99b1cd55..f715875fa 100644 --- a/packages/api-kit/tests/e2e/addSafeOperation.test.ts +++ b/packages/api-kit/tests/e2e/addSafeOperation.test.ts @@ -13,7 +13,6 @@ const SIGNER_PK = '0x83a415ca62e11f5fa5567e98450d0f82ae19ff36ef876c10a8d448c788a const SAFE_ADDRESS = '0x60C4Ab82D06Fd7dFE9517e17736C2Dcc77443EF0' // 1/2 Safe (v1.4.1) with signer above being an owner + 4337 module enabled const PAYMASTER_TOKEN_ADDRESS = '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238' const BUNDLER_URL = 'https://api.pimlico.io/v2/sepolia/rpc?apikey=pim_Vjs7ohRqWdvsjUegngf9Bg' -const TX_SERVICE_URL = 'https://safe-transaction-sepolia.staging.5afe.dev/api' let safeApiKit: SafeApiKit let protocolKit: Safe @@ -30,8 +29,7 @@ describe('addSafeOperation', () => { before(async () => { ;({ safeApiKit, protocolKit } = await getKits({ safeAddress: SAFE_ADDRESS, - signer: SIGNER_PK, - txServiceUrl: TX_SERVICE_URL + signer: SIGNER_PK })) safe4337Pack = await Safe4337Pack.init({ diff --git a/packages/api-kit/tests/e2e/confirmSafeOperation.test.ts b/packages/api-kit/tests/e2e/confirmSafeOperation.test.ts index cf7750993..9e6895e2b 100644 --- a/packages/api-kit/tests/e2e/confirmSafeOperation.test.ts +++ b/packages/api-kit/tests/e2e/confirmSafeOperation.test.ts @@ -11,7 +11,6 @@ chai.use(chaiAsPromised) const PRIVATE_KEY_1 = '0x83a415ca62e11f5fa5567e98450d0f82ae19ff36ef876c10a8d448c788a53676' // 0x56e2C102c664De6DfD7315d12c0178b61D16F171 const PRIVATE_KEY_2 = '0xb88ad5789871315d0dab6fc5961d6714f24f35a6393f13a6f426dfecfc00ab44' // 0x9cCBDE03eDd71074ea9c49e413FA9CDfF16D263B const SAFE_ADDRESS = '0x60C4Ab82D06Fd7dFE9517e17736C2Dcc77443EF0' // 4337 enabled 1/2 Safe (v1.4.1) owned by PRIVATE_KEY_1 + PRIVATE_KEY_2 -const TX_SERVICE_URL = 'https://safe-transaction-sepolia.staging.5afe.dev/api' const BUNDLER_URL = 'https://api.pimlico.io/v2/sepolia/rpc?apikey=pim_Vjs7ohRqWdvsjUegngf9Bg' const PAYMASTER_TOKEN_ADDRESS = '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238' @@ -62,7 +61,7 @@ describe('confirmSafeOperation', () => { before(async () => { safe4337Pack = await getSafe4337Pack({ signer: PRIVATE_KEY_1 }) - safeApiKit = getApiKit(TX_SERVICE_URL) + safeApiKit = getApiKit() // Submit a new Safe operation to the transaction service safeOperation = await addSafeOperation() diff --git a/packages/api-kit/tests/e2e/getPendingSafeOperations.test.ts b/packages/api-kit/tests/e2e/getPendingSafeOperations.test.ts index 93f229fe4..e50b4f36d 100644 --- a/packages/api-kit/tests/e2e/getPendingSafeOperations.test.ts +++ b/packages/api-kit/tests/e2e/getPendingSafeOperations.test.ts @@ -6,13 +6,12 @@ import { getApiKit } from '../utils/setupKits' chai.use(chaiAsPromised) const SAFE_ADDRESS = '0x60C4Ab82D06Fd7dFE9517e17736C2Dcc77443EF0' // v1.4.1 -const TX_SERVICE_URL = 'https://safe-transaction-sepolia.staging.5afe.dev/api' let safeApiKit: SafeApiKit describe('getPendingSafeOperations', () => { before(async () => { - safeApiKit = getApiKit(TX_SERVICE_URL) + safeApiKit = getApiKit() }) describe('should fail', () => { diff --git a/packages/api-kit/tests/e2e/getSafeDelegates.test.ts b/packages/api-kit/tests/e2e/getSafeDelegates.test.ts index 5c541c979..900a849e1 100644 --- a/packages/api-kit/tests/e2e/getSafeDelegates.test.ts +++ b/packages/api-kit/tests/e2e/getSafeDelegates.test.ts @@ -23,7 +23,7 @@ describe('getSafeDelegates', () => { const safeAddress = API_TESTING_SAFE.address before(() => { - safeApiKit = getApiKit('https://safe-transaction-sepolia.staging.5afe.dev/api') + safeApiKit = getApiKit() signer = createWalletClient({ chain: sepolia, transport: http(), diff --git a/packages/api-kit/tests/e2e/getSafeOperation.test.ts b/packages/api-kit/tests/e2e/getSafeOperation.test.ts index 03c5df50d..0d20ce2e0 100644 --- a/packages/api-kit/tests/e2e/getSafeOperation.test.ts +++ b/packages/api-kit/tests/e2e/getSafeOperation.test.ts @@ -6,13 +6,12 @@ import { getApiKit } from '../utils/setupKits' chai.use(chaiAsPromised) const SAFE_ADDRESS = '0x60C4Ab82D06Fd7dFE9517e17736C2Dcc77443EF0' // v1.4.1 -const TX_SERVICE_URL = 'https://safe-transaction-sepolia.staging.5afe.dev/api' let safeApiKit: SafeApiKit describe('getSafeOperation', () => { before(async () => { - safeApiKit = getApiKit(TX_SERVICE_URL) + safeApiKit = getApiKit() }) describe('should fail', () => { diff --git a/packages/api-kit/tests/e2e/getSafeOperationConfirmations.test.ts b/packages/api-kit/tests/e2e/getSafeOperationConfirmations.test.ts index 5ea3aa12b..091de44b0 100644 --- a/packages/api-kit/tests/e2e/getSafeOperationConfirmations.test.ts +++ b/packages/api-kit/tests/e2e/getSafeOperationConfirmations.test.ts @@ -8,30 +8,29 @@ chai.use(chaiAsPromised) let safeApiKit: SafeApiKit -const TX_SERVICE_URL = 'https://safe-transaction-sepolia.staging.5afe.dev/api' -const SAFE_OPERATION_HASH = '0x375d3bd580600ce04d7d2c1d8d88d85f27b9c7d14d7b544f2ee585d672f2b449' +const SAFE_OPERATION_HASH = '0x9d473e819a15ef68d253a31d56e8da9c982d8d589171c0640d673e3ebf4efff7' const EXPECTED_SAFE_OPERATION_CONFIRMATIONS = [ { - created: '2024-06-19T10:45:22.906337Z', - modified: '2024-06-19T10:45:22.906337Z', + created: '2025-02-05T09:57:34.085839Z', + modified: '2025-02-05T09:57:34.085839Z', owner: '0x56e2C102c664De6DfD7315d12c0178b61D16F171', signature: - '0x3c4a706f78e269e20e046f06a153ff06842045bf2c9e7b28aa9f4e93b530c8aa67b8c0871ae008987ad9d1abbe1aa48efbae3b425c791ba03ed1a7542e07c9ce1b', + '0x564109d2728c7e902ee3b7296b1473e1b9778a2d46ea6f2b5496474553d17b2c4d4a5fc9991ac6bb8cc786f029762624348bde1d11d76579e4d28874eaf461ba1b', signatureType: 'EOA' }, { - created: '2024-06-19T10:45:25.895780Z', - modified: '2024-06-19T10:45:25.895780Z', + created: '2025-02-05T09:57:35.660515Z', + modified: '2025-02-05T09:57:35.660515Z', owner: '0x9cCBDE03eDd71074ea9c49e413FA9CDfF16D263B', signature: - '0x619619f4f29578bc9cc4396ceb4b8ee6cc8722bc011f6cf3ace3c903b29162a07e2ddb3af4beccecd8ce68252af177a29f613d1d27c7961b0fc61cf09a0347fa1b', + '0x8147447f780d306ad3436212f11e8c5f174e9fbbd519e6258d88db87369f385127af10385e68bbe9dc9d4544bbad32a0327632a8414bd8d1d0f691ed844c142f1c', signatureType: 'EOA' } ] describe('getSafeOperationConfirmations', () => { before(() => { - safeApiKit = getApiKit(TX_SERVICE_URL) + safeApiKit = getApiKit() }) it('should fail if safeOperationHash is empty', async () => { diff --git a/packages/api-kit/tests/e2e/getSafeOperationsByAddress.test.ts b/packages/api-kit/tests/e2e/getSafeOperationsByAddress.test.ts index 8d365c0af..5c796ff93 100644 --- a/packages/api-kit/tests/e2e/getSafeOperationsByAddress.test.ts +++ b/packages/api-kit/tests/e2e/getSafeOperationsByAddress.test.ts @@ -7,13 +7,12 @@ import { getApiKit } from '../utils/setupKits' chai.use(chaiAsPromised) const SAFE_ADDRESS = '0x60C4Ab82D06Fd7dFE9517e17736C2Dcc77443EF0' // v1.4.1 -const TX_SERVICE_URL = 'https://safe-transaction-sepolia.staging.5afe.dev/api' let safeApiKit: SafeApiKit describe('getSafeOperationsByAddress', () => { before(async () => { - safeApiKit = getApiKit(TX_SERVICE_URL) + safeApiKit = getApiKit() }) let safeOperations: SafeOperationResponse[] diff --git a/packages/api-kit/tests/e2e/getServiceInfo.test.ts b/packages/api-kit/tests/e2e/getServiceInfo.test.ts index 2b4c42140..a3f550461 100644 --- a/packages/api-kit/tests/e2e/getServiceInfo.test.ts +++ b/packages/api-kit/tests/e2e/getServiceInfo.test.ts @@ -1,16 +1,28 @@ import { expect } from 'chai' -import SafeApiKit from '@safe-global/api-kit/index' +import { getTransactionServiceUrl, networks } from '@safe-global/api-kit/utils/config' import { getApiKit } from '../utils/setupKits' -let safeApiKit: SafeApiKit - describe('getServiceInfo', () => { - before(async () => { - safeApiKit = getApiKit() - }) - it('should return the Safe service info', async () => { + const safeApiKit = getApiKit() const serviceInfo = await safeApiKit.getServiceInfo() expect(serviceInfo.api_version).to.be.equal('v1') }) + + describe('Network tests', () => { + beforeEach(function (done) { + setTimeout(done, 250) // 250 ms delay to avoid rate limiting issues + }) + + networks.forEach((network) => { + it(`should return correct network info for chainId ${network.chainId} (${getTransactionServiceUrl(network.chainId)})`, async function () { + this.timeout(10000) + const safeApiKit = getApiKit(undefined, network.chainId) + const serviceInfo = await safeApiKit.getServiceInfo() + + expect(serviceInfo).to.have.property('version').to.be.a('string').not.empty + expect(serviceInfo).to.have.property('name').to.be.a('string').not.empty + }) + }) + }) }) diff --git a/packages/api-kit/tests/e2e/getTransaction.test.ts b/packages/api-kit/tests/e2e/getTransaction.test.ts index 8e18393fc..c4be3fb25 100644 --- a/packages/api-kit/tests/e2e/getTransaction.test.ts +++ b/packages/api-kit/tests/e2e/getTransaction.test.ts @@ -30,8 +30,8 @@ describe('getTransaction', () => { const transaction = await safeApiKit.getTransaction(safeTxHash) chai.expect(transaction.safeTxHash).to.be.equal(safeTxHash) chai.expect(transaction.safe).to.be.eq(API_TESTING_SAFE.address) - chai.expect(transaction.nonce).to.be.a('number') - chai.expect(transaction.safeTxGas).to.be.a('number') - chai.expect(transaction.baseGas).to.be.a('number') + chai.expect(transaction.nonce).to.be.a('string') + chai.expect(transaction.safeTxGas).to.be.a('string') + chai.expect(transaction.baseGas).to.be.a('string') }) }) diff --git a/packages/api-kit/tests/e2e/removeSafeDelegate.test.ts b/packages/api-kit/tests/e2e/removeSafeDelegate.test.ts index 33fa203dd..acf06fb60 100644 --- a/packages/api-kit/tests/e2e/removeSafeDelegate.test.ts +++ b/packages/api-kit/tests/e2e/removeSafeDelegate.test.ts @@ -17,7 +17,7 @@ let delegatorAddress: Address describe('removeSafeDelegate', () => { before(() => { - safeApiKit = getApiKit('https://safe-transaction-sepolia.staging.5afe.dev/api') + safeApiKit = getApiKit() signer = createWalletClient({ chain: sepolia, transport: http(), diff --git a/packages/api-kit/tests/endpoint/SafeApiKit.test.ts b/packages/api-kit/tests/endpoint/SafeApiKit.test.ts new file mode 100644 index 000000000..242037005 --- /dev/null +++ b/packages/api-kit/tests/endpoint/SafeApiKit.test.ts @@ -0,0 +1,93 @@ +import chai from 'chai' +import chaiAsPromised from 'chai-as-promised' +import SafeApiKit from '@safe-global/api-kit/index' +import config from '../utils/config' + +chai.use(chaiAsPromised) +const { expect } = chai + +describe('SafeApiKit', () => { + const chainId = config.CHAIN_ID + + describe('txServiceUrl with api.safe.global domain', () => { + it('should throw an error when txServiceUrl contains safe.global and apiKey is not provided', () => { + expect(() => { + new SafeApiKit({ + chainId, + txServiceUrl: 'https://api.safe.global/tx-service' + }) + }).to.throw('apiKey is mandatory when using api.safe.global or api.5afe.dev domains') + }) + + it('should instantiate successfully when txServiceUrl contains api.safe.global and apiKey is provided', () => { + expect(() => { + new SafeApiKit({ + chainId, + txServiceUrl: 'https://api.safe.global/tx-service', + apiKey: 'valid-api-key' + }) + }).not.to.throw() + }) + }) + + describe('txServiceUrl with api.5afe.dev domain', () => { + it('should throw an error when txServiceUrl contains api.5afe.dev and apiKey is not provided', () => { + expect(() => { + new SafeApiKit({ + chainId, + txServiceUrl: 'https://api.5afe.dev/tx-service' + }) + }).to.throw('apiKey is mandatory when using api.safe.global or api.5afe.dev domains') + }) + + it('should instantiate successfully when txServiceUrl contains api.5afe.dev and apiKey is provided', () => { + expect(() => { + new SafeApiKit({ + chainId, + txServiceUrl: 'https://api.5afe.dev/tx-service', + apiKey: 'valid-api-key' + }) + }).not.to.throw() + }) + }) + + describe('txServiceUrl with other domains', () => { + it('should instantiate successfully when txServiceUrl is for a custom domain and apiKey is not provided', () => { + expect(() => { + new SafeApiKit({ + chainId, + txServiceUrl: 'https://my-custom-service.example.com' + }) + }).not.to.throw() + }) + + it('should instantiate successfully when txServiceUrl is for a custom domain and apiKey is provided', () => { + expect(() => { + new SafeApiKit({ + chainId, + txServiceUrl: 'https://my-custom-service.example.com', + apiKey: 'valid-api-key' + }) + }).not.to.throw() + }) + }) + + describe('no txServiceUrl provided', () => { + it('should throw an error when txServiceUrl is not provided and apiKey is not provided', () => { + expect(() => { + new SafeApiKit({ + chainId + }) + }).to.throw('apiKey is mandatory when txServiceUrl is not defined') + }) + + it('should instantiate successfully when txServiceUrl is not provided and apiKey is provided', () => { + expect(() => { + new SafeApiKit({ + chainId, + apiKey: 'valid-api-key' + }) + }).not.to.throw() + }) + }) +}) diff --git a/packages/api-kit/tests/endpoint/index.test.ts b/packages/api-kit/tests/endpoint/index.test.ts index d8f8371f3..3c5e1f9a2 100644 --- a/packages/api-kit/tests/endpoint/index.test.ts +++ b/packages/api-kit/tests/endpoint/index.test.ts @@ -17,6 +17,7 @@ import { UserOperation } from '@safe-global/types-kit' import { signDelegate } from '@safe-global/api-kit/utils/signDelegate' import config from '../utils/config' import { getApiKit, getKits } from '../utils/setupKits' +import { getTransactionServiceUrl } from '@safe-global/api-kit/utils/config' chai.use(chaiAsPromised) chai.use(sinonChai) @@ -34,7 +35,7 @@ const tokenAddress = '0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14' const eip3770TokenAddress = `${config.EIP_3770_PREFIX}:${tokenAddress}` const safeTxHash = '0x317834aea988fd3cfa54fd8b2be2c96b4fd70a14d8c9470a7110576b01e6480a' const safeOpHash = '0x8b1840745ec0a6288e868c6e285dadcfebd49e846d307610a9ccd97f445ace93' -const txServiceBaseUrl = 'https://safe-transaction-sepolia.safe.global/api' +const txServiceBaseUrl = getTransactionServiceUrl(11155111n) const signer = createWalletClient({ transport: http(), @@ -140,7 +141,7 @@ describe('Endpoint tests', () => { .expect(safeApiKit.getTransaction(safeTxHash)) .to.be.eventually.deep.equals({ data: { success: true } }) chai.expect(fetchData).to.have.been.calledWith({ - url: `${txServiceBaseUrl}/v1/multisig-transactions/${safeTxHash}/`, + url: `${txServiceBaseUrl}/v2/multisig-transactions/${safeTxHash}/`, method: 'get' }) }) @@ -396,7 +397,7 @@ describe('Endpoint tests', () => { ) .to.be.eventually.deep.equals({ data: { success: true } }) chai.expect(fetchData).to.have.been.calledWith({ - url: `${txServiceBaseUrl}/v1/safes/${safeAddress}/multisig-transactions/`, + url: `${txServiceBaseUrl}/v2/safes/${safeAddress}/multisig-transactions/`, method: 'post', body: { ...safeTransactionData, @@ -444,7 +445,7 @@ describe('Endpoint tests', () => { ) .to.be.eventually.deep.equals({ data: { success: true } }) chai.expect(fetchData).to.have.been.calledWith({ - url: `${txServiceBaseUrl}/v1/safes/${safeAddress}/multisig-transactions/`, + url: `${txServiceBaseUrl}/v2/safes/${safeAddress}/multisig-transactions/`, method: 'post', body: { ...safeTransactionData, @@ -512,7 +513,7 @@ describe('Endpoint tests', () => { .expect(safeApiKit.getMultisigTransactions(safeAddress)) .to.be.eventually.deep.equals({ data: { success: true } }) chai.expect(fetchData).to.have.been.calledWith({ - url: `${txServiceBaseUrl}/v1/safes/${safeAddress}/multisig-transactions/`, + url: `${txServiceBaseUrl}/v2/safes/${safeAddress}/multisig-transactions/`, method: 'get' }) }) @@ -522,7 +523,7 @@ describe('Endpoint tests', () => { .expect(safeApiKit.getMultisigTransactions(eip3770SafeAddress)) .to.be.eventually.deep.equals({ data: { success: true } }) chai.expect(fetchData).to.have.been.calledWith({ - url: `${txServiceBaseUrl}/v1/safes/${safeAddress}/multisig-transactions/`, + url: `${txServiceBaseUrl}/v2/safes/${safeAddress}/multisig-transactions/`, method: 'get' }) }) @@ -532,7 +533,7 @@ describe('Endpoint tests', () => { .expect(safeApiKit.getMultisigTransactions(safeAddress, { executed: true })) .to.be.eventually.deep.equals({ data: { success: true } }) chai.expect(fetchData).to.have.been.calledWith({ - url: `${txServiceBaseUrl}/v1/safes/${safeAddress}/multisig-transactions/?executed=true`, + url: `${txServiceBaseUrl}/v2/safes/${safeAddress}/multisig-transactions/?executed=true`, method: 'get' }) }) @@ -547,7 +548,7 @@ describe('Endpoint tests', () => { ) .to.be.eventually.deep.equals({ data: { success: true } }) chai.expect(fetchData).to.have.been.calledWith({ - url: `${txServiceBaseUrl}/v1/safes/${safeAddress}/multisig-transactions/?has_confirmations=true&nonce__gte=1`, + url: `${txServiceBaseUrl}/v2/safes/${safeAddress}/multisig-transactions/?has_confirmations=true&nonce__gte=1`, method: 'get' }) }) @@ -558,7 +559,7 @@ describe('Endpoint tests', () => { .expect(safeApiKit.getPendingTransactions(safeAddress, { currentNonce })) .to.be.eventually.deep.equals({ data: { success: true } }) chai.expect(fetchData).to.have.been.calledWith({ - url: `${txServiceBaseUrl}/v1/safes/${safeAddress}/multisig-transactions/?executed=false&nonce__gte=${currentNonce}`, + url: `${txServiceBaseUrl}/v2/safes/${safeAddress}/multisig-transactions/?executed=false&nonce__gte=${currentNonce}`, method: 'get' }) }) @@ -569,7 +570,7 @@ describe('Endpoint tests', () => { .expect(safeApiKit.getPendingTransactions(eip3770SafeAddress, { currentNonce })) .to.be.eventually.deep.equals({ data: { success: true } }) chai.expect(fetchData).to.have.been.calledWith({ - url: `${txServiceBaseUrl}/v1/safes/${safeAddress}/multisig-transactions/?executed=false&nonce__gte=${currentNonce}`, + url: `${txServiceBaseUrl}/v2/safes/${safeAddress}/multisig-transactions/?executed=false&nonce__gte=${currentNonce}`, method: 'get' }) }) @@ -579,7 +580,7 @@ describe('Endpoint tests', () => { .expect(safeApiKit.getAllTransactions(safeAddress)) .to.be.eventually.deep.equals({ data: { success: true } }) chai.expect(fetchData).to.have.been.calledWith({ - url: `${txServiceBaseUrl}/v1/safes/${safeAddress}/all-transactions/`, + url: `${txServiceBaseUrl}/v2/safes/${safeAddress}/all-transactions/`, method: 'get' }) }) @@ -589,7 +590,7 @@ describe('Endpoint tests', () => { .expect(safeApiKit.getAllTransactions(eip3770SafeAddress)) .to.be.eventually.deep.equals({ data: { success: true } }) chai.expect(fetchData).to.have.been.calledWith({ - url: `${txServiceBaseUrl}/v1/safes/${safeAddress}/all-transactions/`, + url: `${txServiceBaseUrl}/v2/safes/${safeAddress}/all-transactions/`, method: 'get' }) }) diff --git a/packages/api-kit/tests/utils/setupKits.ts b/packages/api-kit/tests/utils/setupKits.ts index 09b3437d6..ff730b4f6 100644 --- a/packages/api-kit/tests/utils/setupKits.ts +++ b/packages/api-kit/tests/utils/setupKits.ts @@ -1,4 +1,5 @@ import hre from 'hardhat' +import dotenv from 'dotenv' import { BrowserProvider } from 'ethers' import { custom, createWalletClient } from 'viem' @@ -6,6 +7,9 @@ import Safe, { SafeProviderConfig, Eip1193Provider } from '@safe-global/protocol import SafeApiKit from '@safe-global/api-kit/index' import config from './config' +import path from 'path' + +dotenv.config({ path: path.join(__dirname, '../../.env') }) type GetKits = { protocolKit: Safe @@ -15,6 +19,7 @@ type GetKits = { type GetKitsOptions = { signer?: SafeProviderConfig['signer'] txServiceUrl?: string + chainId?: bigint safeAddress: string } @@ -53,8 +58,15 @@ export async function getProtocolKit({ return protocolKit } -export function getApiKit(txServiceUrl?: GetKitsOptions['txServiceUrl']): SafeApiKit { - const safeApiKit = new SafeApiKit({ chainId: config.CHAIN_ID, txServiceUrl }) +export function getApiKit( + txServiceUrl?: GetKitsOptions['txServiceUrl'], + chainId?: GetKitsOptions['chainId'] +): SafeApiKit { + const safeApiKit = new SafeApiKit({ + chainId: chainId || config.CHAIN_ID, + apiKey: process.env.API_KEY || 'jwt-token', + txServiceUrl + }) return safeApiKit } diff --git a/packages/protocol-kit/package.json b/packages/protocol-kit/package.json index ff3a1703a..a58b2ae67 100644 --- a/packages/protocol-kit/package.json +++ b/packages/protocol-kit/package.json @@ -1,6 +1,6 @@ { "name": "@safe-global/protocol-kit", - "version": "6.0.5", + "version": "6.1.0", "description": "SDK that facilitates the interaction with Safe Smart Accounts", "types": "dist/src/index.d.ts", "main": "dist/cjs/src/index.cjs", @@ -79,9 +79,9 @@ "tsconfig-paths": "^4.2.0" }, "dependencies": { - "@safe-global/safe-deployments": "^1.37.34", - "@safe-global/safe-modules-deployments": "^2.2.9", - "@safe-global/types-kit": "^2.0.1", + "@safe-global/safe-deployments": "^1.37.35", + "@safe-global/safe-modules-deployments": "^2.2.10", + "@safe-global/types-kit": "^3.0.0", "abitype": "^1.0.2", "semver": "^7.7.1", "viem": "^2.21.8" diff --git a/packages/protocol-kit/src/Safe.ts b/packages/protocol-kit/src/Safe.ts index f979cdb1e..6311c4c0e 100644 --- a/packages/protocol-kit/src/Safe.ts +++ b/packages/protocol-kit/src/Safe.ts @@ -215,13 +215,21 @@ class Safe { * @throws "MultiSendCallOnly contract is not deployed on the current network" */ async connect(config: ConnectSafeConfig): Promise { - const { provider, signer, safeAddress, predictedSafe, isL1SafeSingleton, contractNetworks } = - config + const { + provider, + signer, + safeAddress, + predictedSafe, + isL1SafeSingleton, + contractNetworks, + onchainAnalytics + } = config const configProps: SafeConfigProps = { provider: provider || this.#safeProvider.provider, signer, isL1SafeSingleton: isL1SafeSingleton || this.#contractManager.isL1SafeSingleton, - contractNetworks: contractNetworks || this.#contractManager.contractNetworks + contractNetworks: contractNetworks || this.#contractManager.contractNetworks, + onchainAnalytics } // A new existing Safe is connected to the Signer @@ -779,7 +787,7 @@ class Safe { * Adds the signature of the current signer to the Safe transaction object. * * @param safeTransaction - The Safe transaction to be signed - * @param signingMethod - Method followed to sign a transaction. Optional. Default value is "eth_sign" + * @param signingMethod - Method followed to sign a transaction. Optional. Default value is "eth_signTypedData_v4" * @param preimageSafeAddress - If the preimage is required, the address of the Safe that will be used to calculate the preimage * This field is mandatory for 1.3.0 and 1.4.1 contract versions Because the safe uses the old EIP-1271 interface which uses `bytes` instead of `bytes32` for the message * we need to use the pre-image of the message to calculate the message hash @@ -1264,12 +1272,14 @@ class Safe { operation: serviceTransactionResponse.operation } const options = { + // TODO remove toString() in next major v7.0 safeTxGas: serviceTransactionResponse.safeTxGas.toString(), + // TODO remove toString() in next major v7.0 baseGas: serviceTransactionResponse.baseGas.toString(), gasPrice: serviceTransactionResponse.gasPrice, gasToken: serviceTransactionResponse.gasToken, refundReceiver: serviceTransactionResponse.refundReceiver, - nonce: serviceTransactionResponse.nonce + nonce: parseInt(serviceTransactionResponse.nonce, 10) } const safeTransaction = await this.createTransaction({ transactions: [safeTransactionData], diff --git a/packages/protocol-kit/src/utils/eip-3770/config.ts b/packages/protocol-kit/src/utils/eip-3770/config.ts index ed43cc6c4..0fe72a216 100644 --- a/packages/protocol-kit/src/utils/eip-3770/config.ts +++ b/packages/protocol-kit/src/utils/eip-3770/config.ts @@ -103,6 +103,7 @@ export const networks: NetworkShortName[] = [ { chainId: 690n, shortName: 'redstone' }, { chainId: 747n, shortName: 'flow-mainnet' }, { chainId: 787n, shortName: 'aca' }, + { chainId: 842n, shortName: 'taratest' }, { chainId: 919n, shortName: 'modesep' }, { chainId: 938n, shortName: 'haust' }, { chainId: 943n, shortName: 't4pls' }, @@ -359,6 +360,7 @@ export const networks: NetworkShortName[] = [ { chainId: 200810n, shortName: 'btrt' }, { chainId: 200901n, shortName: 'btr' }, { chainId: 205205n, shortName: 'auroria' }, + { chainId: 210425n, shortName: 'platon' }, { chainId: 314159n, shortName: 'filecoin-calibration' }, { chainId: 325000n, shortName: 'CampV2' }, { chainId: 328527n, shortName: 'nal' }, @@ -391,6 +393,7 @@ export const networks: NetworkShortName[] = [ { chainId: 978657n, shortName: 'treasure-ruby' }, { chainId: 984122n, shortName: 'forma' }, { chainId: 1501869n, shortName: 'water9' }, + { chainId: 2206132n, shortName: 'platondev2' }, { chainId: 2632500n, shortName: 'coti' }, { chainId: 3441006n, shortName: 'mantaSepoliaTestnet' }, { chainId: 4457845n, shortName: 'zero-sepolia' }, diff --git a/packages/protocol-kit/src/utils/getProtocolKitVersion.ts b/packages/protocol-kit/src/utils/getProtocolKitVersion.ts index fc85a23ad..05c68a6c4 100644 --- a/packages/protocol-kit/src/utils/getProtocolKitVersion.ts +++ b/packages/protocol-kit/src/utils/getProtocolKitVersion.ts @@ -1 +1 @@ -export const getProtocolKitVersion = () => '6.0.5' +export const getProtocolKitVersion = () => '6.1.0' diff --git a/packages/protocol-kit/tests/e2e/offChainSignatures.test.ts b/packages/protocol-kit/tests/e2e/offChainSignatures.test.ts index cccceaab6..d24ae72ff 100644 --- a/packages/protocol-kit/tests/e2e/offChainSignatures.test.ts +++ b/packages/protocol-kit/tests/e2e/offChainSignatures.test.ts @@ -283,11 +283,11 @@ describe('Off-chain signatures', () => { data: '0x', operation: 1, gasToken: '0x3333333333333333333333333333333333333333', - safeTxGas: 666, - baseGas: 111, + safeTxGas: '666', + baseGas: '111', gasPrice: '222', refundReceiver: '0x4444444444444444444444444444444444444444', - nonce: await safeSdk.getNonce(), + nonce: (await safeSdk.getNonce()).toString(), executionDate: '', submissionDate: '', modified: '', @@ -298,12 +298,14 @@ describe('Off-chain signatures', () => { isExecuted: false, isSuccessful: false, ethGasPrice: '', + maxFeePerGas: null, + maxPriorityFeePerGas: null, gasUsed: 12345, fee: '12345', origin: '', - dataDecoded: '', confirmationsRequired: 2, proposer: '0x', + proposedByDelegate: null, confirmations: [ { owner: '0x1111111111111111111111111111111111111111', diff --git a/packages/relay-kit/package.json b/packages/relay-kit/package.json index 79fcb278b..1fd8b822f 100644 --- a/packages/relay-kit/package.json +++ b/packages/relay-kit/package.json @@ -1,6 +1,6 @@ { "name": "@safe-global/relay-kit", - "version": "4.0.4", + "version": "4.0.5", "description": "SDK for Safe Smart Accounts with support for ERC-4337 and Relay", "types": "dist/src/index.d.ts", "main": "dist/cjs/src/index.cjs", @@ -56,9 +56,9 @@ }, "dependencies": { "@gelatonetwork/relay-sdk": "^5.6.0", - "@safe-global/protocol-kit": "^6.0.5", - "@safe-global/safe-modules-deployments": "^2.2.9", - "@safe-global/types-kit": "^2.0.1", + "@safe-global/protocol-kit": "^6.1.0", + "@safe-global/safe-modules-deployments": "^2.2.10", + "@safe-global/types-kit": "^3.0.0", "semver": "^7.7.1", "viem": "^2.21.8" } diff --git a/packages/relay-kit/src/packs/safe-4337/utils/getRelayKitVersion.ts b/packages/relay-kit/src/packs/safe-4337/utils/getRelayKitVersion.ts index a10bfe9e4..ae5e67375 100644 --- a/packages/relay-kit/src/packs/safe-4337/utils/getRelayKitVersion.ts +++ b/packages/relay-kit/src/packs/safe-4337/utils/getRelayKitVersion.ts @@ -1 +1 @@ -export const getRelayKitVersion = () => '4.0.4' +export const getRelayKitVersion = () => '4.0.5' diff --git a/packages/sdk-starter-kit/package.json b/packages/sdk-starter-kit/package.json index 1235d3ed6..126e465d4 100644 --- a/packages/sdk-starter-kit/package.json +++ b/packages/sdk-starter-kit/package.json @@ -1,6 +1,6 @@ { "name": "@safe-global/sdk-starter-kit", - "version": "2.0.3", + "version": "3.0.0", "description": "SDK that provides the basic tools to interact with the Safe Smart Account.", "types": "dist/src/index.d.ts", "main": "dist/cjs/index.cjs", @@ -45,10 +45,10 @@ "access": "public" }, "dependencies": { - "@safe-global/api-kit": "^3.0.2", - "@safe-global/protocol-kit": "^6.0.5", - "@safe-global/relay-kit": "^4.0.4", - "@safe-global/types-kit": "^2.0.1", + "@safe-global/api-kit": "^4.0.0", + "@safe-global/protocol-kit": "^6.1.0", + "@safe-global/relay-kit": "^4.0.5", + "@safe-global/types-kit": "^3.0.0", "viem": "^2.21.8" } } diff --git a/packages/sdk-starter-kit/src/SafeClient.test.ts b/packages/sdk-starter-kit/src/SafeClient.test.ts index c7657660d..89e8abad6 100644 --- a/packages/sdk-starter-kit/src/SafeClient.test.ts +++ b/packages/sdk-starter-kit/src/SafeClient.test.ts @@ -1,6 +1,5 @@ import Safe, * as protocolKitModule from '@safe-global/protocol-kit' import SafeApiKit from '@safe-global/api-kit' - import * as utils from './utils' import { SafeClient } from './SafeClient' import { MESSAGES, SafeClientTxStatus } from './constants' @@ -46,7 +45,10 @@ describe('SafeClient', () => { beforeEach(() => { protocolKit = new Safe() - apiKit = new SafeApiKit({ chainId: 1n }) as jest.Mocked + apiKit = new SafeApiKit({ + chainId: 1n, + apiKey: 'apiKey' + }) as jest.Mocked safeClient = new SafeClient(protocolKit, apiKit) diff --git a/packages/sdk-starter-kit/src/extensions/messages/SafeMessageClient.test.ts b/packages/sdk-starter-kit/src/extensions/messages/SafeMessageClient.test.ts index cd405f55d..1342202cf 100644 --- a/packages/sdk-starter-kit/src/extensions/messages/SafeMessageClient.test.ts +++ b/packages/sdk-starter-kit/src/extensions/messages/SafeMessageClient.test.ts @@ -33,7 +33,10 @@ describe('SafeClient', () => { beforeEach(() => { protocolKit = new Safe() - apiKit = new SafeApiKit({ chainId: 1n }) as jest.Mocked + apiKit = new SafeApiKit({ + chainId: 1n, + apiKey: 'apiKey' + }) as jest.Mocked safeMessageClient = new SafeMessageClient(protocolKit, apiKit) apiKit.getMessage = jest.fn().mockResolvedValue(MESSAGE_RESPONSE) diff --git a/packages/sdk-starter-kit/src/extensions/messages/offChainMessages.test.ts b/packages/sdk-starter-kit/src/extensions/messages/offChainMessages.test.ts index ee40c1b27..f0f61875a 100644 --- a/packages/sdk-starter-kit/src/extensions/messages/offChainMessages.test.ts +++ b/packages/sdk-starter-kit/src/extensions/messages/offChainMessages.test.ts @@ -21,7 +21,10 @@ describe('onChainMessages', () => { beforeEach(() => { protocolKit = new Safe() - apiKit = new SafeApiKit({ chainId: 1n }) as jest.Mocked + apiKit = new SafeApiKit({ + chainId: 1n, + apiKey: 'apiKey' + }) as jest.Mocked safeClient = new SafeClient(protocolKit, apiKit) }) diff --git a/packages/sdk-starter-kit/src/extensions/messages/onChainMessages.test.ts b/packages/sdk-starter-kit/src/extensions/messages/onChainMessages.test.ts index 46b22d50c..8c1a4a3b9 100644 --- a/packages/sdk-starter-kit/src/extensions/messages/onChainMessages.test.ts +++ b/packages/sdk-starter-kit/src/extensions/messages/onChainMessages.test.ts @@ -22,7 +22,10 @@ describe('onChainMessages', () => { beforeEach(() => { protocolKit = new Safe() - apiKit = new SafeApiKit({ chainId: 1n }) as jest.Mocked + apiKit = new SafeApiKit({ + chainId: 1n, + apiKey: 'apiKey' + }) as jest.Mocked safeClient = new SafeClient(protocolKit, apiKit) }) diff --git a/packages/sdk-starter-kit/src/extensions/safe-operations/SafeOperationClient.test.ts b/packages/sdk-starter-kit/src/extensions/safe-operations/SafeOperationClient.test.ts index ad0440b8e..3ad03e761 100644 --- a/packages/sdk-starter-kit/src/extensions/safe-operations/SafeOperationClient.test.ts +++ b/packages/sdk-starter-kit/src/extensions/safe-operations/SafeOperationClient.test.ts @@ -64,7 +64,10 @@ describe('SafeOperationClient', () => { const bundlerClientMock = { send: jest.fn().mockResolvedValue('1') } as any protocolKit = new Safe() - apiKit = new SafeApiKit({ chainId: 1n }) as jest.Mocked + apiKit = new SafeApiKit({ + chainId: 1n, + apiKey: 'apiKey' + }) as jest.Mocked safe4337Pack = new Safe4337Pack({ protocolKit, bundlerClient: bundlerClientMock, diff --git a/packages/sdk-starter-kit/src/extensions/safe-operations/safeOperations.test.ts b/packages/sdk-starter-kit/src/extensions/safe-operations/safeOperations.test.ts index f57072da8..67d83771d 100644 --- a/packages/sdk-starter-kit/src/extensions/safe-operations/safeOperations.test.ts +++ b/packages/sdk-starter-kit/src/extensions/safe-operations/safeOperations.test.ts @@ -29,7 +29,10 @@ describe('safeOperations', () => { beforeEach(() => { protocolKit = new Safe() - apiKit = new SafeApiKit({ chainId: 1n }) as jest.Mocked + apiKit = new SafeApiKit({ + chainId: 1n, + apiKey: 'apiKey' + }) as jest.Mocked safeClient = new SafeClient(protocolKit, apiKit) }) diff --git a/packages/sdk-starter-kit/src/index.test.ts b/packages/sdk-starter-kit/src/index.test.ts index 45b82d586..13837155d 100644 --- a/packages/sdk-starter-kit/src/index.test.ts +++ b/packages/sdk-starter-kit/src/index.test.ts @@ -6,12 +6,14 @@ const SAFE_OWNERS = [ '0x9cCBDE03eDd71074ea9c49e413FA9CDfF16D263B', '0x56e2C102c664De6DfD7315d12c0178b61D16F171' ] +const API_KEY = 'apiKey' describe('createSafeClient', () => { it('should create a Safe client instance', async () => { const safeClient = await createSafeClient({ provider: RPC_URL, - safeAddress: SAFE_ADDRESS + safeAddress: SAFE_ADDRESS, + apiKey: API_KEY }) const safeAddress = await safeClient.getAddress() @@ -26,7 +28,8 @@ describe('createSafeClient', () => { it('should allow to extend the client several times and accumulating methods', async () => { const safeClient1 = await createSafeClient({ provider: RPC_URL, - safeAddress: SAFE_ADDRESS + safeAddress: SAFE_ADDRESS, + apiKey: API_KEY }) const safeClient2 = safeClient1.extend(offChainMessages()) diff --git a/packages/sdk-starter-kit/src/index.ts b/packages/sdk-starter-kit/src/index.ts index 28f4cbd50..22567a7e0 100644 --- a/packages/sdk-starter-kit/src/index.ts +++ b/packages/sdk-starter-kit/src/index.ts @@ -98,7 +98,11 @@ async function getApiKitInstance( ): Promise { const chainId = await protocolKit.getChainId() - return new SafeApiKit({ chainId, txServiceUrl: config.txServiceUrl }) + return new SafeApiKit({ + chainId, + apiKey: config.apiKey, + txServiceUrl: config.txServiceUrl + }) } export * from './types' diff --git a/packages/sdk-starter-kit/src/types.ts b/packages/sdk-starter-kit/src/types.ts index 839a8ac55..4d0509674 100644 --- a/packages/sdk-starter-kit/src/types.ts +++ b/packages/sdk-starter-kit/src/types.ts @@ -59,6 +59,7 @@ export type PredictedSafeConfig = { export type SdkStarterKitRootConfig = { provider: SafeProvider['provider'] signer?: SafeProvider['signer'] + apiKey?: string txServiceUrl?: string } diff --git a/packages/testing-kit/package.json b/packages/testing-kit/package.json index 85dac065b..9906aa119 100644 --- a/packages/testing-kit/package.json +++ b/packages/testing-kit/package.json @@ -49,7 +49,7 @@ "@openzeppelin/contracts": "^2.5.1", "@safe-global/safe-contracts-v1.4.1": "npm:@safe-global/safe-contracts@1.4.1", "@safe-global/safe-passkey": "0.2.0", - "@safe-global/types-kit": "^2.0.1", + "@safe-global/types-kit": "^3.0.0", "@types/semver": "^7.7.0", "hardhat": "^2.22.19", "hardhat-deploy": "^0.12.4", diff --git a/packages/types-kit/package.json b/packages/types-kit/package.json index 82ae083f9..fa8b4d275 100644 --- a/packages/types-kit/package.json +++ b/packages/types-kit/package.json @@ -1,6 +1,6 @@ { "name": "@safe-global/types-kit", - "version": "2.0.1", + "version": "3.0.0", "description": "Types for use with the Safe Core SDK packages", "types": "dist/src/index.d.ts", "main": "dist/cjs/index.cjs", diff --git a/packages/types-kit/src/types.ts b/packages/types-kit/src/types.ts index 7f4964632..d78e4227c 100644 --- a/packages/types-kit/src/types.ts +++ b/packages/types-kit/src/types.ts @@ -248,11 +248,11 @@ export type SafeMultisigTransactionResponse = { readonly data?: string readonly operation: number readonly gasToken: string - readonly safeTxGas: number - readonly baseGas: number + readonly safeTxGas: string + readonly baseGas: string readonly gasPrice: string readonly refundReceiver?: string - readonly nonce: number + readonly nonce: string readonly executionDate: string | null readonly submissionDate: string readonly modified: string diff --git a/playground/api-kit/.env-sample b/playground/api-kit/.env-sample new file mode 100644 index 000000000..6dae7996f --- /dev/null +++ b/playground/api-kit/.env-sample @@ -0,0 +1,12 @@ +# Set the chain ID where the playgrounds will be used +CHAIN_ID=11155111 +# Set the preferred RPC url +RPC_URL='https://sepolia.gateway.tenderly.co' +# API key +API_KEY= +# Private key of the account used to sign +SIGNER_ADDRESS_PRIVATE_KEY= +# Safe address to use with the playgrounds where Safe must already exist +SAFE_ADDRESS=0x... +# Safe tx hash to use with the playgrounds where the transaction is already created +SAFE_TX_HASH=0x... \ No newline at end of file diff --git a/playground/api-kit/confirm-transaction.ts b/playground/api-kit/confirm-transaction.ts index 0ff04eef2..4cceabb9e 100644 --- a/playground/api-kit/confirm-transaction.ts +++ b/playground/api-kit/confirm-transaction.ts @@ -1,40 +1,35 @@ +import * as dotenv from 'dotenv' import Safe from '@safe-global/protocol-kit' import SafeApiKit from '@safe-global/api-kit' -// This file can be used to play around with the Safe Core SDK - -interface Config { - CHAIN_ID: bigint - RPC_URL: string - SIGNER_ADDRESS_PRIVATE_KEY: string - SAFE_ADDRESS: string - SAFE_TX_HASH: string -} - -// Adjust the configuration with your own input parameters before running the script -const config: Config = { - CHAIN_ID: 11155111n, - RPC_URL: 'https://sepolia.gateway.tenderly.co', - SIGNER_ADDRESS_PRIVATE_KEY: '', - SAFE_ADDRESS: '', - SAFE_TX_HASH: '' -} +dotenv.config({ path: './playground/api-kit/.env' }) +// Load environment variables from ./.env file +// Follow .env-sample as an example to create your own file +const { + CHAIN_ID = 11155111, + RPC_URL = '', + API_KEY = '', + SIGNER_ADDRESS_PRIVATE_KEY = '', + SAFE_ADDRESS = '', + SAFE_TX_HASH = '' +} = process.env async function main() { // Create Safe instance const protocolKit = await Safe.init({ - provider: config.RPC_URL, - signer: config.SIGNER_ADDRESS_PRIVATE_KEY, - safeAddress: config.SAFE_ADDRESS + provider: RPC_URL, + signer: SIGNER_ADDRESS_PRIVATE_KEY, + safeAddress: SAFE_ADDRESS }) // Create Safe API Kit instance const apiKit = new SafeApiKit({ - chainId: config.CHAIN_ID + chainId: BigInt(CHAIN_ID), + apiKey: API_KEY }) // Get the transaction - const safeTransaction = await apiKit.getTransaction(config.SAFE_TX_HASH) + const safeTransaction = await apiKit.getTransaction(SAFE_TX_HASH || '') const safeTxHash = safeTransaction.safeTxHash const signature = await protocolKit.signHash(safeTxHash) @@ -42,7 +37,7 @@ async function main() { const signatureResponse = await apiKit.confirmTransaction(safeTxHash, signature.data) const signerAddress = await protocolKit.getSafeProvider().getSignerAddress() - console.log('Added a new signature to transaction with safeTxGas:', config.SAFE_TX_HASH) + console.log('Added a new signature to transaction with safeTxGas:', SAFE_TX_HASH) console.log('- Signer:', signerAddress) console.log('- Signer signature:', signatureResponse.signature) } diff --git a/playground/api-kit/execute-transaction.ts b/playground/api-kit/execute-transaction.ts index 498bca9cc..c250e680b 100644 --- a/playground/api-kit/execute-transaction.ts +++ b/playground/api-kit/execute-transaction.ts @@ -1,42 +1,37 @@ +import * as dotenv from 'dotenv' import { Hash } from 'viem' import { waitForTransactionReceipt } from 'viem/actions' import Safe from '@safe-global/protocol-kit' import SafeApiKit from '@safe-global/api-kit' -// This file can be used to play around with the Safe Core SDK - -interface Config { - CHAIN_ID: bigint - RPC_URL: string - SIGNER_ADDRESS_PRIVATE_KEY: string - SAFE_ADDRESS: string - SAFE_TX_HASH: string -} - -// Adjust the configuration with your own input parameters before running the script -const config: Config = { - CHAIN_ID: 11155111n, - RPC_URL: 'https://sepolia.gateway.tenderly.co', - SIGNER_ADDRESS_PRIVATE_KEY: '', - SAFE_ADDRESS: '', - SAFE_TX_HASH: '' -} +dotenv.config({ path: './playground/api-kit/.env' }) +// Load environment variables from ./.env file +// Follow .env-sample as an example to create your own file +const { + CHAIN_ID = 11155111, + RPC_URL = '', + API_KEY = '', + SIGNER_ADDRESS_PRIVATE_KEY = '', + SAFE_ADDRESS = '', + SAFE_TX_HASH = '' +} = process.env async function main() { // Create Safe instance const protocolKit = await Safe.init({ - provider: config.RPC_URL, - signer: config.SIGNER_ADDRESS_PRIVATE_KEY, - safeAddress: config.SAFE_ADDRESS + provider: RPC_URL, + signer: SIGNER_ADDRESS_PRIVATE_KEY, + safeAddress: SAFE_ADDRESS }) // Create Safe API Kit instance const apiKit = new SafeApiKit({ - chainId: config.CHAIN_ID + chainId: BigInt(CHAIN_ID), + apiKey: API_KEY || '' }) // Get the transaction - const safeTransaction = await apiKit.getTransaction(config.SAFE_TX_HASH) + const safeTransaction = await apiKit.getTransaction(SAFE_TX_HASH) const isTxExecutable = await protocolKit.isValidTransaction(safeTransaction) if (isTxExecutable) { diff --git a/playground/api-kit/propose-transaction.ts b/playground/api-kit/propose-transaction.ts index 2e3570d85..08fc4ce4f 100644 --- a/playground/api-kit/propose-transaction.ts +++ b/playground/api-kit/propose-transaction.ts @@ -1,40 +1,36 @@ +import * as dotenv from 'dotenv' import Safe from '@safe-global/protocol-kit' import SafeApiKit from '@safe-global/api-kit' import { OperationType, SafeTransactionDataPartial } from '@safe-global/types-kit' -// This file can be used to play around with the Safe Core SDK - -interface Config { - CHAIN_ID: bigint - RPC_URL: string - SIGNER_ADDRESS_PRIVATE_KEY: string - SAFE_ADDRESS: string -} - -// Adjust the configuration with your own input parameters before running the script -const config: Config = { - CHAIN_ID: 11155111n, - RPC_URL: 'https://sepolia.gateway.tenderly.co', - SIGNER_ADDRESS_PRIVATE_KEY: '', - SAFE_ADDRESS: '' -} +dotenv.config({ path: './playground/api-kit/.env' }) +// Load environment variables from ./.env file +// Follow .env-sample as an example to create your own file +const { + CHAIN_ID = 11155111, + RPC_URL = '', + API_KEY = '', + SIGNER_ADDRESS_PRIVATE_KEY = '', + SAFE_ADDRESS = '' +} = process.env async function main() { // Create Safe instance const protocolKit = await Safe.init({ - provider: config.RPC_URL, - signer: config.SIGNER_ADDRESS_PRIVATE_KEY, - safeAddress: config.SAFE_ADDRESS + provider: RPC_URL, + signer: SIGNER_ADDRESS_PRIVATE_KEY, + safeAddress: SAFE_ADDRESS }) // Create Safe API Kit instance const apiKit = new SafeApiKit({ - chainId: config.CHAIN_ID + chainId: BigInt(CHAIN_ID), + apiKey: API_KEY || '' }) // Create transaction const safeTransactionData: SafeTransactionDataPartial = { - to: config.SAFE_ADDRESS, + to: SAFE_ADDRESS, value: '1', // 1 wei data: '0x', operation: OperationType.Call @@ -49,14 +45,14 @@ async function main() { // Propose transaction to the service await apiKit.proposeTransaction({ - safeAddress: config.SAFE_ADDRESS, + safeAddress: SAFE_ADDRESS, safeTransactionData: safeTransaction.data, safeTxHash, senderAddress: signerAddress, senderSignature: signature.data }) - console.log('Proposed a transaction with Safe:', config.SAFE_ADDRESS) + console.log('Proposed a transaction with Safe:', SAFE_ADDRESS) console.log('- safeTxHash:', safeTxHash) console.log('- Sender:', signerAddress) console.log('- Sender signature:', signature.data) diff --git a/playground/relay-kit/.env-sample b/playground/relay-kit/.env-sample index 71c76387e..3002d9db9 100644 --- a/playground/relay-kit/.env-sample +++ b/playground/relay-kit/.env-sample @@ -12,4 +12,6 @@ CHAIN_ID=11155111 BUNDLER_URL= PAYMASTER_URL= # Set the sponsor policy in case you are using one to test the sponsor user operations related playgrounds -SPONSORSHIP_POLICY_ID= \ No newline at end of file +SPONSORSHIP_POLICY_ID= +# Api key to use Transaction Service +API_KEY= \ No newline at end of file diff --git a/playground/relay-kit/userop-api-kit-interoperability.ts b/playground/relay-kit/userop-api-kit-interoperability.ts index 39e0b7950..a3940923d 100644 --- a/playground/relay-kit/userop-api-kit-interoperability.ts +++ b/playground/relay-kit/userop-api-kit-interoperability.ts @@ -1,3 +1,4 @@ +import * as dotenv from 'dotenv' import { privateKeyToAddress } from 'viem/accounts' import SafeApiKit from '@safe-global/api-kit' import { Safe4337Pack } from '@safe-global/relay-kit' @@ -15,8 +16,15 @@ const RPC_URL = 'https://ethereum-sepolia-rpc.publicnode.com' const BUNDLER_URL = 'https://...' const PAYMASTER_URL = 'https://...' +dotenv.config({ path: './playground/relay-kit/.env' }) + +const { API_KEY } = process.env + async function main() { - const apiKit = new SafeApiKit({ chainId: BigInt(CHAIN_ID) }) + const apiKit = new SafeApiKit({ + chainId: BigInt(CHAIN_ID), + apiKey: API_KEY || '' + }) let safe4337Pack = await Safe4337Pack.init({ provider: RPC_URL, diff --git a/yarn.lock b/yarn.lock index 655f5e989..60d62b408 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1826,17 +1826,17 @@ resolved "https://registry.yarnpkg.com/@safe-global/safe-contracts/-/safe-contracts-1.4.1.tgz#82605342f3289dc6b99818f599a3409ec2cb3fdc" integrity sha512-fP1jewywSwsIniM04NsqPyVRFKPMAuirC3ftA/TA4X3Zc5EnwQp/UCJUU2PL/37/z/jMo8UUaJ+pnFNWmMU7dQ== -"@safe-global/safe-deployments@^1.37.34": - version "1.37.34" - resolved "https://registry.yarnpkg.com/@safe-global/safe-deployments/-/safe-deployments-1.37.34.tgz#e3061327c12d73869a48acddb4742b2c2cf7c2b0" - integrity sha512-ynBFymugc3sZJ63NZuUuRYjjp5LIVPJqt/kJV/lnB0nh0JeUjX+W7NPNTjO6dxxdIi5p547pEaLKYKnaS4pTCQ== +"@safe-global/safe-deployments@^1.37.35": + version "1.37.35" + resolved "https://registry.yarnpkg.com/@safe-global/safe-deployments/-/safe-deployments-1.37.35.tgz#d1516a4c571fc9409e601b58330f50d7c5526242" + integrity sha512-ICZ4VgvUwDlr0lu5ZfGwcj3igm3b2jopnMBw9UweT/a5ezGKu+3p8j2spxjUwhqo5eixIXH1R8yjHI7mp1LISQ== dependencies: semver "^7.6.2" -"@safe-global/safe-modules-deployments@^2.2.9": - version "2.2.9" - resolved "https://registry.yarnpkg.com/@safe-global/safe-modules-deployments/-/safe-modules-deployments-2.2.9.tgz#f1575863b5e62fcb3c6df4d1e480c3c9b249ba15" - integrity sha512-3brLLX4RkLlH2vB+1CgGo1iA73kckmPpgroTLaweZnysr1lKHqpqY0VAA3gmjHq1Qiq1Cx0fccDHk3c2rqRfsw== +"@safe-global/safe-modules-deployments@^2.2.10": + version "2.2.10" + resolved "https://registry.yarnpkg.com/@safe-global/safe-modules-deployments/-/safe-modules-deployments-2.2.10.tgz#b33a7b1e5279550664214b3c16930d106b6d409c" + integrity sha512-xEOOqSt4zqSPEU4VWgQZmjXbsgFtXOHsz34af8hx7WBLoVKUZ3umOuSjBYb0qBOY4FiQWkgGYsvJJRQjA1ESBA== "@safe-global/safe-passkey@0.2.0": version "0.2.0" @@ -8426,11 +8426,6 @@ undici-types@~6.19.2: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== -undici-types@~6.20.0: - version "6.20.0" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433" - integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg== - undici-types@~6.21.0: version "6.21.0" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb"