Skip to content

Commit

Permalink
Merge branch 'development' into esm-bundle
Browse files Browse the repository at this point in the history
  • Loading branch information
DaniSomoza committed Jan 15, 2025
2 parents 323c1b4 + fe00fb1 commit 0557993
Show file tree
Hide file tree
Showing 36 changed files with 769 additions and 103 deletions.
2 changes: 1 addition & 1 deletion LICENSE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2021-2024 Safe Ecosystem Foundation
Copyright (c) 2021-2025 Safe Ecosystem Foundation

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

Expand Down
2 changes: 1 addition & 1 deletion guides/integrating-the-safe-core-sdk.md
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ type SafeMultisigTransactionResponse = {
transactionHash?: string
confirmationType?: string
signature: string
signatureType?: string
signatureType?: SignatureType
},
// ...
]
Expand Down
8 changes: 4 additions & 4 deletions packages/api-kit/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@safe-global/api-kit",
"version": "2.5.4",
"version": "2.5.6",
"description": "SDK that facilitates the interaction with the Safe Transaction Service API",
"types": "dist/src/index.d.ts",
"main": "dist/cjs/index.cjs",
Expand Down Expand Up @@ -48,7 +48,7 @@
],
"homepage": "https://github.com/safe-global/safe-core-sdk#readme",
"devDependencies": {
"@safe-global/relay-kit": "^3.2.4",
"@safe-global/relay-kit": "^3.3.1",
"@safe-global/testing-kit": "^0.1.1",
"@types/chai": "^4.3.19",
"@types/chai-as-promised": "^7.1.8",
Expand All @@ -67,8 +67,8 @@
"web3": "^4.12.1"
},
"dependencies": {
"@safe-global/protocol-kit": "^5.0.4",
"@safe-global/types-kit": "^1.0.0",
"@safe-global/protocol-kit": "^5.1.1",
"@safe-global/types-kit": "^1.0.1",
"node-fetch": "^2.7.0",
"viem": "^2.21.8"
}
Expand Down
14 changes: 11 additions & 3 deletions packages/api-kit/src/SafeApiKit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,20 +115,28 @@ class SafeApiKit {
/**
* Decodes the specified Safe transaction data.
*
* @param data - The Safe transaction data
* @param data - The Safe transaction data. '0x' prefixed hexadecimal string.
* @param to - The address of the receiving contract. If provided, the decoded data will be more accurate, as in case of an ABI collision the Safe Transaction Service would know which ABI to use
* @returns The transaction data decoded
* @throws "Invalid data"
* @throws "Not Found"
* @throws "Ensure this field has at least 1 hexadecimal chars (not counting 0x)."
*/
async decodeData(data: string): Promise<any> {
async decodeData(data: string, to?: string): Promise<any> {

Check warning on line 125 in packages/api-kit/src/SafeApiKit.ts

View workflow job for this annotation

GitHub Actions / eslint

Unexpected any. Specify a different type
if (data === '') {
throw new Error('Invalid data')
}

const dataDecoderRequest: { data: string; to?: string } = { data }

if (to) {
dataDecoderRequest.to = to
}

return sendRequest({
url: `${this.#txServiceBaseUrl}/v1/data-decoder/`,
method: HttpMethod.Post,
body: { data }
body: dataDecoderRequest
})
}

Expand Down
5 changes: 3 additions & 2 deletions packages/api-kit/src/types/safeTransactionServiceTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import {
SafeTransactionData,
UserOperation,
SafeOperationResponse,
ListResponse
ListResponse,
SignatureType
} from '@safe-global/types-kit'

export type ListOptions = {
Expand Down Expand Up @@ -231,7 +232,7 @@ export type SafeMessageConfirmation = {
readonly modified: string
readonly owner: string
readonly signature: string
readonly signatureType: string
readonly signatureType: SignatureType
}

export type SafeMessage = {
Expand Down
18 changes: 18 additions & 0 deletions packages/api-kit/tests/e2e/decodeData.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,22 @@ describe('decodeData', () => {
})
)
})

it('should decode the data and allow to specify the receiving contract', async () => {
const data = '0x610b592500000000000000000000000090F8bf6A479f320ead074411a4B0e7944Ea8c9C1'
const to = '0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1'
const decodedData = await safeApiKit.decodeData(data, to)
chai.expect(JSON.stringify(decodedData)).to.be.equal(
JSON.stringify({
method: 'enableModule',
parameters: [
{
name: 'module',
type: 'address',
value: '0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1'
}
]
})
)
})
})
11 changes: 7 additions & 4 deletions packages/protocol-kit/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@safe-global/protocol-kit",
"version": "5.0.4",
"version": "5.1.1",
"description": "SDK that facilitates the interaction with Safe Smart Accounts",
"types": "dist/src/index.d.ts",
"main": "dist/cjs/src/index.cjs",
Expand Down Expand Up @@ -82,12 +82,15 @@
"web3": "^4.12.1"
},
"dependencies": {
"@noble/hashes": "^1.3.3",
"@safe-global/safe-deployments": "^1.37.14",
"@safe-global/safe-deployments": "^1.37.22",
"@safe-global/safe-modules-deployments": "^2.2.4",
"@safe-global/types-kit": "^1.0.0",
"@safe-global/types-kit": "^1.0.1",
"abitype": "^1.0.2",
"semver": "^7.6.3",
"viem": "^2.21.8"
},
"optionalDependencies": {
"@noble/curves": "^1.6.0",
"@peculiar/asn1-schema": "^2.3.13"
}
}
91 changes: 85 additions & 6 deletions packages/protocol-kit/src/Safe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
getChainSpecificDefaultSaltNonce,
getPredictedSafeAddressInitCode,
predictSafeAddress,
toTxResult,
validateSafeAccountConfig,
validateSafeDeploymentConfig
} from './contracts/utils'
Expand All @@ -42,7 +43,8 @@ import {
SigningMethodType,
SwapOwnerTxParams,
SafeModulesPaginated,
RemovePasskeyOwnerTxParams
RemovePasskeyOwnerTxParams,
PasskeyArgType
} from './types'
import {
EthSafeSignature,
Expand All @@ -59,7 +61,8 @@ import {
generateSignature,
preimageSafeMessageHash,
preimageSafeTransactionHash,
adjustVInSignature
adjustVInSignature,
extractPasskeyData
} from './utils'
import EthSafeTransaction from './utils/transactions/SafeTransaction'
import { SafeTransactionOptionalProps } from './utils/transactions/types'
Expand All @@ -81,9 +84,11 @@ import SafeMessage from './utils/messages/SafeMessage'
import semverSatisfies from 'semver/functions/satisfies'
import SafeProvider from './SafeProvider'
import { asHash, asHex } from './utils/types'
import { Hash, Hex } from 'viem'
import { Hash, Hex, SendTransactionParameters } from 'viem'
import getPasskeyOwnerAddress from './utils/passkeys/getPasskeyOwnerAddress'
import createPasskeyDeploymentTransaction from './utils/passkeys/createPasskeyDeploymentTransaction'
import generateOnChainIdentifier from './utils/on-chain-tracking/generateOnChainIdentifier'
import getProtocolKitVersion from './utils/getProtocolKitVersion'

const EQ_OR_GT_1_4_1 = '>=1.4.1'
const EQ_OR_GT_1_3_0 = '>=1.3.0'
Expand All @@ -100,6 +105,9 @@ class Safe {
#MAGIC_VALUE = '0x1626ba7e'
#MAGIC_VALUE_BYTES = '0x20c13b0b'

// on-chain Analytics
#onchainIdentifier: string = ''

/**
* Creates an instance of the Safe Core SDK.
* @param config - Ethers Safe configuration
Expand All @@ -124,7 +132,17 @@ class Safe {
* @throws "MultiSendCallOnly contract is not deployed on the current network"
*/
async #initializeProtocolKit(config: SafeConfig) {
const { provider, signer, isL1SafeSingleton, contractNetworks } = config
const { provider, signer, isL1SafeSingleton, contractNetworks, onchainAnalytics } = config

if (onchainAnalytics?.project) {
const { project, platform } = onchainAnalytics
this.#onchainIdentifier = generateOnChainIdentifier({
project,
platform,
tool: 'protocol-kit',
toolVersion: getProtocolKitVersion()
})
}

this.#safeProvider = await SafeProvider.init({
provider,
Expand Down Expand Up @@ -1338,6 +1356,32 @@ class Safe {

const signerAddress = await this.#safeProvider.getSignerAddress()

if (this.#onchainIdentifier) {
const encodedTransaction = await this.getEncodedTransaction(signedSafeTransaction)

const transaction = {
to: await this.getAddress(),
value: 0n,
data: encodedTransaction + this.#onchainIdentifier
}

const signer = await this.#safeProvider.getExternalSigner()

if (!signer) {
throw new Error('A signer must be set')
}

const hash = await signer.sendTransaction({
...transaction,
account: signer.account,
...options
} as SendTransactionParameters)

const provider = this.#safeProvider.getExternalProvider()

return toTxResult(provider, hash, options)
}

const txResponse = await this.#contractManager.safeContract.execTransaction(
signedSafeTransaction,
{
Expand Down Expand Up @@ -1464,6 +1508,14 @@ class Safe {
// we create the deployment transaction
const safeDeploymentTransaction = await this.createSafeDeploymentTransaction()

// remove the onchain idendifier if it is included
if (safeDeploymentTransaction.data.endsWith(this.#onchainIdentifier)) {
safeDeploymentTransaction.data = safeDeploymentTransaction.data.replace(
this.#onchainIdentifier,
''
)
}

// First transaction of the batch: The Safe deployment Transaction
const safeDeploymentBatchTransaction = {
to: safeDeploymentTransaction.to,
Expand All @@ -1484,7 +1536,11 @@ class Safe {
const transactions = [safeDeploymentBatchTransaction, safeBatchTransaction]

// this is the transaction with the batch
const safeDeploymentBatch = await this.createTransactionBatch(transactions, transactionOptions)
const safeDeploymentBatch = await this.createTransactionBatch(
transactions,
transactionOptions,
!!this.#onchainIdentifier // include the on chain identifier
)

return safeDeploymentBatch
}
Expand Down Expand Up @@ -1559,6 +1615,10 @@ class Safe {
])
}

if (this.#onchainIdentifier) {
safeDeployTransactionData.data += this.#onchainIdentifier
}

return safeDeployTransactionData
}

Expand All @@ -1570,12 +1630,14 @@ class Safe {
* @function createTransactionBatch
* @param {MetaTransactionData[]} transactions - An array of MetaTransactionData objects to be batched together.
* @param {TransactionOption} [transactionOptions] - Optional TransactionOption object to specify additional options for the transaction batch.
* @param {boolean} [includeOnchainIdentifier=false] - A flag indicating whether to append the onchain identifier to the data field of the resulting transaction.
* @returns {Promise<Transaction>} A Promise that resolves with the created transaction batch.
*
*/
async createTransactionBatch(
transactions: MetaTransactionData[],
transactionOptions?: TransactionOptions
transactionOptions?: TransactionOptions,
includeOnchainIdentifier: boolean = false
): Promise<Transaction> {
// we use the MultiSend contract to create the batch, see: https://github.com/safe-global/safe-contracts/blob/main/contracts/libraries/MultiSendCallOnly.sol
const multiSendCallOnlyContract = this.#contractManager.multiSendCallOnlyContract
Expand All @@ -1592,6 +1654,10 @@ class Safe {
data: batchData
}

if (includeOnchainIdentifier) {
transactionBatch.data += this.#onchainIdentifier
}

return transactionBatch
}

Expand Down Expand Up @@ -1698,6 +1764,19 @@ class Safe {
}): ContractInfo | undefined => {
return getContractInfo(contractAddress)
}

getOnchainIdentifier(): string {
return this.#onchainIdentifier
}

/**
* This method creates a signer to be used with the init method
* @param {Credential} credential - The credential to be used to create the signer. Can be generated in the web with navigator.credentials.create
* @returns {PasskeyArgType} - The signer to be used with the init method
*/
static createPasskeySigner = async (credential: Credential): Promise<PasskeyArgType> => {
return extractPasskeyData(credential)
}
}

export default Safe
4 changes: 2 additions & 2 deletions packages/protocol-kit/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ import {
estimateTxGas,
estimateSafeTxGas,
estimateSafeDeploymentGas,
extractPasskeyCoordinates,
extractPasskeyData,
validateEthereumAddress,
validateEip3770Address
Expand Down Expand Up @@ -67,21 +66,22 @@ import {
} from './utils/eip-712'
import { createPasskeyClient } from './utils/passkeys/PasskeyClient'
import getPasskeyOwnerAddress from './utils/passkeys/getPasskeyOwnerAddress'
import generateOnChainIdentifier from './utils/on-chain-tracking/generateOnChainIdentifier'

export {
estimateTxBaseGas,
estimateTxGas,
estimateSafeTxGas,
estimateSafeDeploymentGas,
extractPasskeyData,
extractPasskeyCoordinates,
ContractManager,
CreateCallBaseContract,
createERC20TokenTransferTransaction,
DEFAULT_SAFE_VERSION,
EthSafeSignature,
MultiSendCallOnlyBaseContract,
MultiSendBaseContract,
generateOnChainIdentifier,
PREDETERMINED_SALT_NONCE,
SafeBaseContract,
SafeProxyFactoryBaseContract,
Expand Down
5 changes: 4 additions & 1 deletion packages/protocol-kit/src/types/passkeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ export type PasskeyCoordinates = {
y: string
}

export type GetPasskeyCredentialFn = (options?: CredentialRequestOptions) => Promise<Credential>

export type PasskeyArgType = {
rawId: string // required to sign data
coordinates: PasskeyCoordinates // required to sign data
customVerifierAddress?: string // optional
customVerifierAddress?: string
getFn?: GetPasskeyCredentialFn
}
Loading

0 comments on commit 0557993

Please sign in to comment.