Skip to content

Commit

Permalink
fix: 4337 issues (safe-global#904)
Browse files Browse the repository at this point in the history
  • Loading branch information
yagopv authored Jul 15, 2024
1 parent 8c85ff3 commit 2d4d92a
Show file tree
Hide file tree
Showing 14 changed files with 60 additions and 39 deletions.
8 changes: 6 additions & 2 deletions packages/api-kit/src/SafeApiKit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,10 @@ class SafeApiKit {
throw new Error('Signature must not be empty')
}

// We are receiving the timestamp in seconds (block timestamp), but the API expects it in milliseconds
const getISOString = (date: number | undefined) =>
!date ? null : new Date(date * 1000).toISOString()

return sendRequest({
url: `${this.#txServiceBaseUrl}/v1/safes/${safeAddress}/safe-operations/`,
method: HttpMethod.Post,
Expand All @@ -847,8 +851,8 @@ class SafeApiKit {
? null
: userOperation.paymasterAndData,
entryPoint,
validAfter: !options?.validAfter ? null : options?.validAfter,
validUntil: !options?.validUntil ? null : options?.validUntil,
validAfter: getISOString(options?.validAfter),
validUntil: getISOString(options?.validUntil),
signature: userOperation.signature,
moduleAddress
}
Expand Down
11 changes: 9 additions & 2 deletions packages/api-kit/tests/endpoint/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,13 @@ describe('Endpoint tests', () => {
}

const entryPoint = '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789'
const options = { validAfter: 123, validUntil: 234 }

const ethersProvider = protocolKit.getSafeProvider().getExternalProvider()
const timestamp = (await ethersProvider.getBlock('latest'))?.timestamp || 0

const validAfter = timestamp - 60_000
const validUntil = timestamp + 60_000
const options = { validAfter, validUntil }

await chai
.expect(
Expand Down Expand Up @@ -711,7 +717,8 @@ describe('Endpoint tests', () => {
maxPriorityFeePerGas: userOperation.maxPriorityFeePerGas.toString(),
paymasterAndData: userOperation.paymasterAndData,
entryPoint,
...options,
validAfter: new Date(validAfter * 1000).toISOString(),
validUntil: new Date(validUntil * 1000).toISOString(),
signature: userOperation.signature,
moduleAddress
}
Expand Down
10 changes: 5 additions & 5 deletions packages/relay-kit/src/packs/safe-4337/Safe4337Pack.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ describe('Safe4337Pack', () => {
maxFeePerGas: 100000n,
maxPriorityFeePerGas: 200000n,
verificationGasLimit: 150000n,
preVerificationGas: 100000n
preVerificationGas: 105000n
})
})

Expand Down Expand Up @@ -395,7 +395,7 @@ describe('Safe4337Pack', () => {
maxFeePerGas: 100000n,
maxPriorityFeePerGas: 200000n,
verificationGasLimit: 150000n,
preVerificationGas: 100000n
preVerificationGas: 105000n
})
})

Expand Down Expand Up @@ -454,7 +454,7 @@ describe('Safe4337Pack', () => {
maxFeePerGas: 100000n,
maxPriorityFeePerGas: 200000n,
verificationGasLimit: 150000n,
preVerificationGas: 100000n
preVerificationGas: 105000n
})
})

Expand Down Expand Up @@ -529,7 +529,7 @@ describe('Safe4337Pack', () => {
maxFeePerGas: 100000n,
maxPriorityFeePerGas: 200000n,
verificationGasLimit: 150000n,
preVerificationGas: 100000n
preVerificationGas: 105000n
})
})
})
Expand Down Expand Up @@ -557,7 +557,7 @@ describe('Safe4337Pack', () => {
fixtures.OWNER_1.toLowerCase(),
new protocolKit.EthSafeSignature(
fixtures.OWNER_1,
'0x63de7fdf99bcf20a1981ae74c3960604139d8bf025da894abc11604b30f438e82ceb0d37e73bf16b0b8b896f8be82a49750433733c0414fe4a3b8182a3875e1f1c',
'0x8ce4849928aef19e8f5cc199e069a451568dcbaca194a86dc953ae24acac3cbb02a458343127b2a52e1af3b99622b2fc8f1bd9957f84828c33940532a94ea3261c',
false
)
)
Expand Down
21 changes: 16 additions & 5 deletions packages/relay-kit/src/packs/safe-4337/Safe4337Pack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -478,15 +478,17 @@ export class Safe4337Pack extends RelayKitBasePack<{
preVerificationGas: BigInt(userOperation?.preVerificationGas || 0),
maxFeePerGas: BigInt(userOperation?.maxFeePerGas || 0),
maxPriorityFeePerGas: BigInt(userOperation?.maxPriorityFeePerGas || 0),
paymasterAndData: userOperation?.paymasterData || '0x',
signature: userOperation?.signature || '0x'
paymasterAndData: ethers.hexlify(
ethers.concat([userOperation?.paymaster || '0x', userOperation?.paymasterData || '0x'])
),
signature: safeOperationResponse.preparedSignature || '0x'
},
{
chainId: this.#chainId,
moduleAddress: this.#SAFE_4337_MODULE_ADDRESS,
entryPoint: userOperation?.entryPoint || this.#ENTRYPOINT_ADDRESS,
validAfter: validAfter ? new Date(validAfter).getTime() : undefined,
validUntil: validUntil ? new Date(validUntil).getTime() : undefined
validAfter: this.#timestamp(validAfter),
validUntil: this.#timestamp(validUntil)
}
)

Expand All @@ -499,6 +501,15 @@ export class Safe4337Pack extends RelayKitBasePack<{
return safeOperation
}

/**
*
* @param date An ISO string date
* @returns The timestamp in seconds to send to the bundler
*/
#timestamp(date: string | null) {
return date ? new Date(date).getTime() / 1000 : undefined
}

/**
* Signs a safe operation.
*
Expand Down Expand Up @@ -546,7 +557,7 @@ export class Safe4337Pack extends RelayKitBasePack<{
this.#SAFE_4337_MODULE_ADDRESS
)
} else {
const safeOpHash = await safeOp.getHash()
const safeOpHash = safeOp.getHash()

signature = await this.protocolKit.signHash(safeOpHash)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ export class PimlicoFeeEstimator implements IFeeEstimator {
return {
callGasLimit: userOperation.callGasLimit + userOperation.callGasLimit / 2n,
verificationGasLimit:
userOperation.verificationGasLimit + userOperation.verificationGasLimit / 2n
userOperation.verificationGasLimit + userOperation.verificationGasLimit / 2n,
preVerificationGas: userOperation.preVerificationGas + userOperation.preVerificationGas / 20n
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,7 @@ export class SafeOperationClient {
safeOperation = await this.safe4337Pack.signSafeOperation(safeOperation)

if (isMultisigSafe) {
const userOperation = safeOperation.toUserOperation()
userOperation.signature = safeOperation.encodedSignatures() // Without validity dates

await this.apiKit.addSafeOperation({
entryPoint: safeOperation.data.entryPoint,
moduleAddress: safeOperation.moduleAddress,
safeAddress: safeOperation.data.safe,
userOperation,
options: {
validAfter: safeOperation.data.validAfter,
validUntil: safeOperation.data.validUntil
}
})
await this.apiKit.addSafeOperation(safeOperation)

const safeOperationHash = safeOperation.getHash()

Expand Down
2 changes: 1 addition & 1 deletion playground/relay-kit/api-kit-interoperability.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const SAFE_ADDRESS = '' // Safe 2/N
const CHAIN_NAME = 'sepolia'

// Constants
const BUNDLER_URL = `https://api.pimlico.io/v1/${CHAIN_NAME}/rpc?apikey=${PIMLICO_API_KEY}`
const BUNDLER_URL = `https://api.pimlico.io/v2/${CHAIN_NAME}/rpc?apikey=${PIMLICO_API_KEY}`
const PAYMASTER_URL = `https://api.pimlico.io/v2/${CHAIN_NAME}/rpc?apikey=${PIMLICO_API_KEY}`
const RPC_URL = 'https://sepolia.gateway.tenderly.co'
const PAYMASTER_ADDRESS = '0x0000000000325602a77416A16136FDafd04b299f' // SEPOLIA
Expand Down
2 changes: 1 addition & 1 deletion playground/relay-kit/usdc-transfer-4337-counterfactual.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const CHAIN_NAME = 'sepolia'
// const CHAIN_NAME = 'gnosis'

// Bundler URL
const BUNDLER_URL = `https://api.pimlico.io/v1/${CHAIN_NAME}/rpc?apikey=${PIMLICO_API_KEY}` // PIMLICO
const BUNDLER_URL = `https://api.pimlico.io/v2/${CHAIN_NAME}/rpc?apikey=${PIMLICO_API_KEY}` // PIMLICO

// USDC CONTRACT ADDRESS IN SEPOLIA
// faucet: https://faucet.circle.com/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const RPC_URL = 'https://sepolia.gateway.tenderly.co' // SEPOLIA
// const RPC_URL = 'https://rpc.gnosischain.com/' // GNOSIS

// Bundler URL
const BUNDLER_URL = `https://api.pimlico.io/v1/${CHAIN_NAME}/rpc?apikey=${PIMLICO_API_KEY}` // PIMLICO
const BUNDLER_URL = `https://api.pimlico.io/v2/${CHAIN_NAME}/rpc?apikey=${PIMLICO_API_KEY}` // PIMLICO

// PAYMASTER ADDRESS
const paymasterAddress = '0x0000000000325602a77416A16136FDafd04b299f' // SEPOLIA
Expand Down
2 changes: 1 addition & 1 deletion playground/relay-kit/usdc-transfer-4337-erc20.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const RPC_URL = 'https://sepolia.gateway.tenderly.co' // SEPOLIA
// const RPC_URL = 'https://rpc.gnosischain.com/' // GNOSIS

// Bundler URL
const BUNDLER_URL = `https://api.pimlico.io/v1/${CHAIN_NAME}/rpc?apikey=${PIMLICO_API_KEY}` // PIMLICO
const BUNDLER_URL = `https://api.pimlico.io/v2/${CHAIN_NAME}/rpc?apikey=${PIMLICO_API_KEY}` // PIMLICO

// PAYMASTER ADDRESS
const paymasterAddress = '0x0000000000325602a77416A16136FDafd04b299f' // SEPOLIA
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const RPC_URL = 'https://sepolia.gateway.tenderly.co' // SEPOLIA
// const RPC_URL = 'https://rpc.gnosischain.com/' // GNOSIS

// Bundler URL
const BUNDLER_URL = `https://api.pimlico.io/v1/${CHAIN_NAME}/rpc?apikey=${PIMLICO_API_KEY}` // PIMLICO
const BUNDLER_URL = `https://api.pimlico.io/v2/${CHAIN_NAME}/rpc?apikey=${PIMLICO_API_KEY}` // PIMLICO

// Paymaster URL
const PAYMASTER_URL = `https://api.pimlico.io/v2/${CHAIN_NAME}/rpc?apikey=${PIMLICO_API_KEY}` // PIMLICO
Expand Down
2 changes: 1 addition & 1 deletion playground/relay-kit/usdc-transfer-4337-sponsored.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const RPC_URL = 'https://sepolia.gateway.tenderly.co' // SEPOLIA
// const RPC_URL = 'https://rpc.gnosischain.com/' // GNOSIS

// Bundler URL
const BUNDLER_URL = `https://api.pimlico.io/v1/${CHAIN_NAME}/rpc?apikey=${PIMLICO_API_KEY}` // PIMLICO
const BUNDLER_URL = `https://api.pimlico.io/v2/${CHAIN_NAME}/rpc?apikey=${PIMLICO_API_KEY}` // PIMLICO

// Paymaster URL
const PAYMASTER_URL = `https://api.pimlico.io/v2/${CHAIN_NAME}/rpc?apikey=${PIMLICO_API_KEY}` // PIMLICO
Expand Down
2 changes: 1 addition & 1 deletion playground/relay-kit/usdc-transfer-4337.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const PIMLICO_API_KEY = ''
const SAFE_ADDRESS = ''

// Bundler URL
const BUNDLER_URL = `https://api.pimlico.io/v1/sepolia/rpc?apikey=${PIMLICO_API_KEY}` // PIMLICO
const BUNDLER_URL = `https://api.pimlico.io/v2/sepolia/rpc?apikey=${PIMLICO_API_KEY}` // PIMLICO

// RPC URL
const RPC_URL = 'https://sepolia.gateway.tenderly.co'
Expand Down
18 changes: 14 additions & 4 deletions playground/safe-kit/send-safe-operation.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ethers } from 'ethers'
import { SafeClientResult, createSafeClient, safeOperations } from '@safe-global/safe-kit'
import { generateTransferCallData } from '../utils'

Expand All @@ -20,9 +21,9 @@ const usdcAmount = 10_000n // 0.01 USDC
const paymasterAddress = '0x0000000000325602a77416A16136FDafd04b299f' // SEPOLIA

// Paymaster URL
const PIMLICO_API_KEY = '30b296fa-8947-4775-b44a-b225336e2a66'
const PIMLICO_API_KEY = ''
const PAYMASTER_URL = `https://api.pimlico.io/v2/sepolia/rpc?apikey=${PIMLICO_API_KEY}` // PIMLICO
const BUNDLER_URL = `https://api.pimlico.io/v1/sepolia/rpc?apikey=${PIMLICO_API_KEY}`
const BUNDLER_URL = `https://api.pimlico.io/v2/sepolia/rpc?apikey=${PIMLICO_API_KEY}`

async function send(): Promise<SafeClientResult> {
const safeClient = await createSafeClient({
Expand Down Expand Up @@ -59,7 +60,16 @@ async function send(): Promise<SafeClientResult> {
}
const transactions = [transferUSDC, transferUSDC]

const safeOperationResult = await safeClientWithSafeOperation.sendSafeOperation({ transactions })
const ethersProvider = new ethers.JsonRpcProvider(RPC_URL)
const timestamp = (await ethersProvider.getBlock('latest'))?.timestamp || 0

const safeOperationResult = await safeClientWithSafeOperation.sendSafeOperation({
transactions,
options: {
validAfter: timestamp - 60_000,
validUntil: timestamp + 60_000
}
})

console.log('-Send result: ', safeOperationResult)

Expand All @@ -73,7 +83,7 @@ async function confirm(safeClientResult: SafeClientResult, pk: string) {

const safeClient = await createSafeClient({
provider: RPC_URL,
signer: OWNER_1_PRIVATE_KEY,
signer: pk,
safeOptions: {
owners: [OWNER_1_ADDRESS, OWNER_2_ADDRESS, OWNER_3_ADDRESS],
threshold: THRESHOLD,
Expand Down

0 comments on commit 2d4d92a

Please sign in to comment.