Skip to content

Commit

Permalink
Merge branch 'main' into feat/story-odyssey
Browse files Browse the repository at this point in the history
  • Loading branch information
leonimella authored Nov 6, 2024
2 parents 9e52384 + 014f365 commit a66cdfa
Show file tree
Hide file tree
Showing 23 changed files with 293 additions and 59 deletions.
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.1",
"version": "2.5.4",
"description": "SDK that facilitates the interaction with the Safe Transaction Service API",
"main": "dist/src/index.js",
"typings": "dist/src/index.d.ts",
Expand Down Expand Up @@ -39,8 +39,8 @@
],
"homepage": "https://github.com/safe-global/safe-core-sdk#readme",
"devDependencies": {
"@safe-global/relay-kit": "^3.2.1",
"@safe-global/testing-kit": "^0.1.0",
"@safe-global/relay-kit": "^3.2.4",
"@safe-global/testing-kit": "^0.1.1",
"@types/chai": "^4.3.19",
"@types/chai-as-promised": "^7.1.8",
"@types/mocha": "^10.0.8",
Expand All @@ -58,7 +58,7 @@
"web3": "^4.12.1"
},
"dependencies": {
"@safe-global/protocol-kit": "^5.0.1",
"@safe-global/protocol-kit": "^5.0.4",
"@safe-global/types-kit": "^1.0.0",
"node-fetch": "^2.7.0",
"viem": "^2.21.8"
Expand Down
60 changes: 58 additions & 2 deletions packages/api-kit/src/SafeApiKit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
ListOptions,
ModulesResponse,
OwnerResponse,
PendingTransactionsOptions,
ProposeTransactionProps,
SafeCreationInfoResponse,
SafeDelegateListResponse,
Expand Down Expand Up @@ -528,7 +529,7 @@ class SafeApiKit {
* Returns the list of multi-signature transactions that are waiting for the confirmation of the Safe owners.
*
* @param safeAddress - The Safe address
* @param currentNonce - Current nonce of the Safe
* @param currentNonce - Deprecated, use inside object property: Current nonce of the Safe.
* @returns The list of transactions waiting for the confirmation of the Safe owners
* @throws "Invalid Safe address"
* @throws "Invalid data"
Expand All @@ -537,15 +538,70 @@ class SafeApiKit {
async getPendingTransactions(
safeAddress: string,
currentNonce?: number
): Promise<SafeMultisigTransactionListResponse>
/**
* Returns the list of multi-signature transactions that are waiting for the confirmation of the Safe owners.
*
* @param safeAddress - The Safe address
* @param {PendingTransactionsOptions} options The options to filter the list of transactions
* @returns The list of transactions waiting for the confirmation of the Safe owners
* @throws "Invalid Safe address"
* @throws "Invalid data"
* @throws "Invalid ethereum address"
*/
async getPendingTransactions(
safeAddress: string,
{ currentNonce, hasConfirmations, ordering, limit, offset }: PendingTransactionsOptions
): Promise<SafeMultisigTransactionListResponse>
async getPendingTransactions(
safeAddress: string,
propsOrCurrentNonce: PendingTransactionsOptions | number = {}
): Promise<SafeMultisigTransactionListResponse> {
if (safeAddress === '') {
throw new Error('Invalid Safe address')
}

// TODO: Remove @deprecated migration code
let currentNonce: number | undefined
let hasConfirmations: boolean | undefined
let ordering: string | undefined
let limit: number | undefined
let offset: number | undefined
if (typeof propsOrCurrentNonce === 'object') {
;({ currentNonce, hasConfirmations, ordering, limit, offset } = propsOrCurrentNonce)
} else {
console.warn(
'Deprecated: Use `currentNonce` inside an object instead. See `PendingTransactionsOptions`.'
)
currentNonce = propsOrCurrentNonce
}
// END of @deprecated migration code

const { address } = this.#getEip3770Address(safeAddress)
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}`
)

if (hasConfirmations) {
url.searchParams.set('has_confirmations', hasConfirmations.toString())
}

if (ordering) {
url.searchParams.set('ordering', ordering)
}

if (limit != null) {
url.searchParams.set('limit', limit.toString())
}

if (offset != null) {
url.searchParams.set('offset', offset.toString())
}

return sendRequest({
url: `${this.#txServiceBaseUrl}/v1/safes/${address}/multisig-transactions/?executed=false&nonce__gte=${nonce}`,
url: url.toString(),
method: HttpMethod.Get
})
}
Expand Down
7 changes: 7 additions & 0 deletions packages/api-kit/src/types/safeTransactionServiceTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,13 @@ export type ProposeTransactionProps = {
origin?: string
}

export type PendingTransactionsOptions = {
currentNonce?: number
hasConfirmations?: boolean
/** Which field to use when ordering the results. It can be: `nonce`, `created`, `modified` (default: `-created`) */
ordering?: string
} & ListOptions

export type SafeMultisigTransactionListResponse = ListResponse<SafeMultisigTransactionResponse>

export type TransferResponse = {
Expand Down
22 changes: 22 additions & 0 deletions packages/api-kit/tests/e2e/getPendingTransactions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,26 @@ describe('getPendingTransactions', () => {
chai.expect(transactionList.count).to.be.equal(10)
chai.expect(transactionList.results.length).to.be.equal(10)
})

it('should return a maximum of 2 transactions with limit = 2', async () => {
const safeAddress = '0xCa2f5A815b642c79FC530B60BC15Aee4eF6252b3' // Safe with pending transaction
const transactionList = await safeApiKit.getPendingTransactions(safeAddress, {
limit: 2
})

chai.expect(transactionList).to.have.property('count').greaterThan(1)
chai.expect(transactionList).to.have.property('results').to.be.an('array')
chai.expect(transactionList.results.length).to.be.equal(2)
})

it('should return all pending transactions excluding the first one with offset = 1', async () => {
const safeAddress = '0xCa2f5A815b642c79FC530B60BC15Aee4eF6252b3' // Safe with pending transaction
const transactionList = await safeApiKit.getPendingTransactions(safeAddress, {
offset: 1
})

chai.expect(transactionList).to.have.property('count').greaterThan(1)
chai.expect(transactionList).to.have.property('results').to.be.an('array')
chai.expect(transactionList.results.length).to.be.lessThanOrEqual(transactionList.count - 1)
})
})
4 changes: 2 additions & 2 deletions packages/api-kit/tests/endpoint/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,7 @@ describe('Endpoint tests', () => {
it('getPendingTransactions', async () => {
const currentNonce = 1
await chai
.expect(safeApiKit.getPendingTransactions(safeAddress, currentNonce))
.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}`,
Expand All @@ -523,7 +523,7 @@ describe('Endpoint tests', () => {
it('getPendingTransactions EIP-3770', async () => {
const currentNonce = 1
await chai
.expect(safeApiKit.getPendingTransactions(eip3770SafeAddress, currentNonce))
.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}`,
Expand Down
7 changes: 3 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.1",
"version": "5.0.4",
"description": "SDK that facilitates the interaction with Safe Smart Accounts",
"main": "dist/src/index.js",
"types": "dist/src/index.d.ts",
Expand Down Expand Up @@ -51,8 +51,7 @@
],
"homepage": "https://github.com/safe-global/safe-core-sdk#readme",
"devDependencies": {
"@safe-global/safe-passkey": "0.2.0-alpha.1",
"@safe-global/testing-kit": "^0.1.0",
"@safe-global/testing-kit": "^0.1.1",
"@types/chai": "^4.3.19",
"@types/chai-as-promised": "^7.1.8",
"@types/mocha": "^10.0.8",
Expand All @@ -68,7 +67,7 @@
},
"dependencies": {
"@noble/hashes": "^1.3.3",
"@safe-global/safe-deployments": "^1.37.9",
"@safe-global/safe-deployments": "^1.37.14",
"@safe-global/safe-modules-deployments": "^2.2.4",
"@safe-global/types-kit": "^1.0.0",
"abitype": "^1.0.2",
Expand Down
11 changes: 8 additions & 3 deletions packages/protocol-kit/src/Safe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ class Safe {
return predictSafeAddress({
safeProvider: this.#safeProvider,
chainId,
isL1SafeSingleton: this.#contractManager.isL1SafeSingleton,
customContracts: this.#contractManager.contractNetworks?.[chainId.toString()],
...this.#predictedSafe
})
Expand Down Expand Up @@ -1520,27 +1521,31 @@ class Safe {

const isL1SafeSingleton = this.#contractManager.isL1SafeSingleton
const customContracts = this.#contractManager.contractNetworks?.[chainId.toString()]
const deploymentType = this.#predictedSafe.safeDeploymentConfig?.deploymentType

const safeSingletonContract = await getSafeContract({
safeProvider,
safeVersion,
isL1SafeSingleton,
customContracts
customContracts,
deploymentType
})

// we use the SafeProxyFactory.sol contract, see: https://github.com/safe-global/safe-contracts/blob/main/contracts/proxies/SafeProxyFactory.sol
const safeProxyFactoryContract = await getSafeProxyFactoryContract({
safeProvider,
safeVersion,
customContracts
customContracts,
deploymentType
})

// this is the call to the setup method that sets the threshold & owners of the new Safe, see: https://github.com/safe-global/safe-contracts/blob/main/contracts/Safe.sol#L95
const initializer = await encodeSetupCallData({
safeProvider,
safeContract: safeSingletonContract,
safeAccountConfig: safeAccountConfig,
customContracts
customContracts,
deploymentType
})

const safeDeployTransactionData = {
Expand Down
32 changes: 21 additions & 11 deletions packages/protocol-kit/src/contracts/BaseContract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import {
Chain
} from 'viem'
import { estimateContractGas, getTransactionReceipt } from 'viem/actions'
import { SingletonDeployment } from '@safe-global/safe-deployments'
import { SingletonDeploymentV2 } from '@safe-global/safe-deployments'
import { Deployment } from '@safe-global/safe-modules-deployments'
import { contractName, getContractDeployment } from '@safe-global/protocol-kit/contracts/config'
import { DeploymentType } from '@safe-global/protocol-kit/types'
import SafeProvider from '@safe-global/protocol-kit/SafeProvider'
Expand Down Expand Up @@ -105,26 +106,35 @@ class BaseContract<ContractAbiType extends Abi> {

#resolveAddress(
networkAddresses: string | string[] | undefined,
deployment: SingletonDeployment,
deployment: SingletonDeploymentV2 | Deployment | undefined,
deploymentType?: DeploymentType
): string | undefined {
// If there are no addresses for the given chainId we return undefined
if (!networkAddresses) {
return undefined
}

if (typeof networkAddresses === 'string') {
return networkAddresses
}

if (deploymentType) {
// If a custom deployment type is selected, we check that type is available in the given chain
// We ensure that we receive a SingletonDeploymentV2 object for this check,
// otherwise we continue with the next logic (`@safe-global/safe-module-deployments` is not having this property.)
if (deploymentType && deployment && 'deployments' in deployment) {
const customDeploymentTypeAddress = deployment.deployments[deploymentType]?.address

return (
networkAddresses.find((address) => address === customDeploymentTypeAddress) ??
networkAddresses[0]
)
if (typeof networkAddresses === 'string') {
return networkAddresses === customDeploymentTypeAddress
? customDeploymentTypeAddress
: undefined
}

return networkAddresses.find((address) => address === customDeploymentTypeAddress)
}

// Deployment type is not selected and there is only one address for this contract in the given chain, we return it
if (typeof networkAddresses === 'string') {
return networkAddresses
}

// If there are multiple addresses available for this contract, we return the first one.
return networkAddresses[0]
}

Expand Down
6 changes: 2 additions & 4 deletions packages/protocol-kit/src/contracts/Safe/SafeBaseContract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,13 @@ abstract class SafeBaseContract<
safeProvider: SafeProvider,
defaultAbi: SafeContractAbiType,
safeVersion: SafeVersion,
isL1SafeSingleton = false,
isL1SafeSingleton = safeDeploymentsL1ChainIds.includes(chainId),
customContractAddress?: string,
customContractAbi?: SafeContractAbiType,
deploymentType?: DeploymentType
) {
const isL1Contract =
safeDeploymentsL1ChainIds.includes(chainId) ||
isL1SafeSingleton ||
!hasSafeFeature(SAFE_FEATURES.SAFE_L2_CONTRACTS, safeVersion)
isL1SafeSingleton || !hasSafeFeature(SAFE_FEATURES.SAFE_L2_CONTRACTS, safeVersion)

const contractName = isL1Contract ? 'safeSingletonVersion' : 'safeSingletonL2Version'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class SafeContract_v1_0_0
constructor(
chainId: bigint,
safeProvider: SafeProvider,
isL1SafeSingleton = false,
isL1SafeSingleton?: boolean,
customContractAddress?: string,
customContractAbi?: SafeContract_v1_0_0_Abi,
deploymentType?: DeploymentType
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class SafeContract_v1_1_1
constructor(
chainId: bigint,
safeProvider: SafeProvider,
isL1SafeSingleton = false,
isL1SafeSingleton?: boolean,
customContractAddress?: string,
customContractAbi?: SafeContract_v1_1_1_Abi,
deploymentType?: DeploymentType
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class SafeContract_v1_2_0
constructor(
chainId: bigint,
safeProvider: SafeProvider,
isL1SafeSingleton = false,
isL1SafeSingleton?: boolean,
customContractAddress?: string,
customContractAbi?: SafeContract_v1_2_0_Abi,
deploymentType?: DeploymentType
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class SafeContract_v1_3_0
constructor(
chainId: bigint,
safeProvider: SafeProvider,
isL1SafeSingleton = false,
isL1SafeSingleton?: boolean,
customContractAddress?: string,
customContractAbi?: SafeContract_v1_3_0_Abi,
deploymentType?: DeploymentType
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class SafeContract_v1_4_1
constructor(
chainId: bigint,
safeProvider: SafeProvider,
isL1SafeSingleton = false,
isL1SafeSingleton?: boolean,
customContractAddress?: string,
customContractAbi?: SafeContract_v1_4_1_Abi,
deploymentType?: DeploymentType
Expand Down
6 changes: 2 additions & 4 deletions packages/protocol-kit/src/contracts/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,7 @@ export const safeDeploymentsL1ChainIds = [

const contractFunctions: Record<
contractName,
(
filter?: DeploymentFilter
) => SingletonDeployment | SingletonDeploymentV2 | undefined | Deployment
(filter?: DeploymentFilter) => SingletonDeploymentV2 | undefined | Deployment
> = {
safeSingletonVersion: getSafeSingletonDeployments,
safeSingletonL2Version: getSafeL2SingletonDeployments,
Expand Down Expand Up @@ -148,7 +146,7 @@ export function getContractDeployment(
released: true
}

const deployment = contractFunctions[contractName](filters) as SingletonDeployment
const deployment = contractFunctions[contractName](filters)

return deployment
}
Expand Down
Loading

0 comments on commit a66cdfa

Please sign in to comment.