Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(relay-kit): Add UserOperation custom nonce support #1126

Merged
merged 69 commits into from
Feb 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
22ceffc
Add a specific userOperations module
yagopv Jan 10, 2025
5ab7193
eth_estimateUserOperationGas v0.7 support
yagopv Jan 13, 2025
ee61a99
Fix initCode
yagopv Jan 13, 2025
7a1a767
Change SafeOp structure
yagopv Jan 15, 2025
36c2da9
Working version for sponsored transactions
yagopv Jan 17, 2025
feda7e8
Update playgrounds
yagopv Jan 20, 2025
77c9973
Fix SigningMethod evaluated incorrectly
yagopv Jan 20, 2025
8eff392
Fix paymaster 0x0000...
yagopv Jan 20, 2025
dbe373d
Add more RPC calls
yagopv Jan 22, 2025
9fcea54
Update to work with new Pimlico paymasters
yagopv Jan 22, 2025
5eff169
Improvements
yagopv Jan 23, 2025
19b63c7
Update playgrounds
yagopv Jan 23, 2025
71048b4
Update comments in playgrounds
yagopv Jan 23, 2025
a27f78d
Improve SafeOperation
yagopv Jan 24, 2025
d94bbe8
Remove calculateSafeUserOperationHash
yagopv Jan 24, 2025
0cd4cfa
Improve dummySignatures retrieval
yagopv Jan 24, 2025
ce90770
Improve version management for SafeOperations
yagopv Jan 24, 2025
3c75c5d
Merge branch 'development' of https://github.com/safe-global/safe-cor…
yagopv Jan 24, 2025
958316c
Fix other kits
yagopv Jan 24, 2025
503a7cc
Refactor rpc schemas
yagopv Jan 27, 2025
c60dd14
Some improvements
yagopv Jan 27, 2025
38c85fb
Improve playgrounds
yagopv Jan 27, 2025
e1fa4b4
Fix SafeOperationV06
yagopv Jan 28, 2025
0a4ba15
Update playgrounds
yagopv Jan 28, 2025
3c524f3
uncomment
yagopv Jan 28, 2025
e245d03
Merge branch 'development' of https://github.com/safe-global/safe-cor…
yagopv Jan 28, 2025
e6559c1
Improve imports
yagopv Jan 28, 2025
a204a8a
Fix tests
yagopv Jan 29, 2025
800d2a0
Downgrade viem version breaking tests
yagopv Jan 30, 2025
d0e1147
Fix tests
yagopv Jan 30, 2025
dc086be
Merge branch 'development' of https://github.com/safe-global/safe-cor…
yagopv Jan 30, 2025
069e4f5
Fix nonce
yagopv Jan 30, 2025
6b5f563
Fix starter-kit
yagopv Jan 30, 2025
f57bab9
Fix api-kit tests
yagopv Jan 30, 2025
7529635
Fix some tests
yagopv Jan 31, 2025
705be62
Fix api kit tests
yagopv Jan 31, 2025
a8d7ba0
protocol-kit shouldn't be a dep of SafeOperation
yagopv Jan 31, 2025
d0d81e9
Merge branch 'development' of https://github.com/safe-global/safe-cor…
yagopv Jan 31, 2025
4a21b2c
Fix protocol-kit tests
yagopv Jan 31, 2025
0a3842f
Update test
yagopv Jan 31, 2025
bd31756
Not neccesary sharedSigner
yagopv Jan 31, 2025
5159d69
Add SafeOperationV07 tests
yagopv Feb 3, 2025
2e9e136
Fix tests
yagopv Feb 3, 2025
1d52221
Merge branch 'development' of https://github.com/safe-global/safe-cor…
yagopv Feb 3, 2025
f7ce30b
Improve gas estimation
yagopv Feb 4, 2025
9601e11
Improve SafeOperationFactory
yagopv Feb 4, 2025
479fd13
Add SPONSORSHIP_POLICY_ID env var
yagopv Feb 4, 2025
1b1b163
Improve estimator
yagopv Feb 4, 2025
a9f2c35
Add tests
yagopv Feb 4, 2025
e0ace1d
Add test
yagopv Feb 4, 2025
bfe5591
Fix checksum shared signer
yagopv Feb 5, 2025
e145ac4
Update api-kit-interoperability
yagopv Feb 5, 2025
6dc64ad
Update playground/utils.ts
yagopv Feb 6, 2025
3182c56
Update packages/protocol-kit/src/utils/signatures/utils.ts
yagopv Feb 6, 2025
36f2f16
Update packages/relay-kit/src/packs/safe-4337/Safe4337Pack.test.ts
yagopv Feb 6, 2025
6130264
Fix using viem sepolia chain in tests
yagopv Feb 6, 2025
c3a3e20
Merge branch 'feat/entrypoint-v7' of https://github.com/safe-global/s…
yagopv Feb 6, 2025
e49b3e8
Remove SafeOperation Interface
yagopv Feb 6, 2025
609aa1b
Renamed SafeOperationBase -> SafeOperation
yagopv Feb 6, 2025
d775fb4
Merge branch 'development' into feat/entrypoint-v7
yagopv Feb 6, 2025
028c582
Add test
yagopv Feb 6, 2025
3ffab9f
Add paralell useroperation support
yagopv Feb 7, 2025
480f84a
restore SafeOperation interface and rename the class to BaseSafeOpera…
yagopv Feb 7, 2025
e9b2851
Merge branch 'feat/entrypoint-v7' into feat/user-operation-custom-nonce
yagopv Feb 7, 2025
13fec41
Fix
yagopv Feb 7, 2025
d5e088f
Remove redundant comment
yagopv Feb 7, 2025
2a96632
Improve playground
yagopv Feb 10, 2025
3e1ee43
Merge branch 'development' of https://github.com/safe-global/safe-cor…
yagopv Feb 10, 2025
a3ff22e
Add test
yagopv Feb 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions packages/relay-kit/src/packs/safe-4337/Safe4337Pack.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1042,5 +1042,35 @@ describe('Safe4337Pack', () => {
'5afe006137303238633936636562316132623939353333646561393063346135'
)
})

it('should allow to use custom nonces', async () => {
const transferUSDC = {
to: fixtures.PAYMASTER_TOKEN_ADDRESS,
data: generateTransferCallData(fixtures.SAFE_ADDRESS_v1_4_1_WITH_0_3_0_MODULE, 100_000n),
value: '0',
operation: 0
}

const safe4337Pack = await createSafe4337Pack({
options: {
safeAddress: fixtures.SAFE_ADDRESS_v1_4_1_WITH_0_3_0_MODULE
}
})

const customNonce = utils.encodeNonce({
key: BigInt(Date.now()),
sequence: 0n
})

let safeOperation = await safe4337Pack.createTransaction({
transactions: [transferUSDC],
options: { customNonce }
})

expect(safeOperation.getUserOperation()).toHaveProperty('nonce', customNonce.toString())
expect(safeOperation.getSafeOperation()).toHaveProperty('nonce', customNonce.toString())

safeOperation = await safe4337Pack.signSafeOperation(safeOperation)
})
})
})
5 changes: 3 additions & 2 deletions packages/relay-kit/src/packs/safe-4337/Safe4337Pack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -463,12 +463,13 @@ export class Safe4337Pack extends RelayKitBasePack<{
transactions,
options = {}
}: Safe4337CreateTransactionProps): Promise<BaseSafeOperation> {
const { amountToApprove, validUntil, validAfter, feeEstimator } = options
const { amountToApprove, validUntil, validAfter, feeEstimator, customNonce } = options

const userOperation = await createUserOperation(this.protocolKit, transactions, {
entryPoint: this.#ENTRYPOINT_ADDRESS,
paymasterOptions: this.#paymasterOptions,
amountToApprove
amountToApprove,
customNonce
})

if (this.#onchainIdentifier) {
Expand Down
1 change: 1 addition & 0 deletions packages/relay-kit/src/packs/safe-4337/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export type Safe4337CreateTransactionProps = {
validUntil?: number
validAfter?: number
feeEstimator?: IFeeEstimator
customNonce?: bigint
}
}

Expand Down
8 changes: 8 additions & 0 deletions packages/relay-kit/src/packs/safe-4337/utils/encodeNonce.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { toHex } from 'viem'

export function encodeNonce(args: { key: bigint; sequence: bigint }): bigint {
const key = BigInt(toHex(args.key, { size: 24 }))
const sequence = BigInt(toHex(args.sequence, { size: 8 }))

return (key << BigInt(64)) + sequence
}
1 change: 1 addition & 0 deletions packages/relay-kit/src/packs/safe-4337/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,4 @@ export * from './entrypoint'
export * from './signing'
export * from './userOperations'
export * from './getRelayKitVersion'
export * from './encodeNonce'
13 changes: 10 additions & 3 deletions packages/relay-kit/src/packs/safe-4337/utils/userOperations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,18 @@ export async function createUserOperation(
{
amountToApprove,
entryPoint,
paymasterOptions
}: { entryPoint: string; amountToApprove?: bigint; paymasterOptions: PaymasterOptions }
paymasterOptions,
customNonce
}: {
entryPoint: string
amountToApprove?: bigint
paymasterOptions: PaymasterOptions
customNonce?: bigint
}
): Promise<UserOperation> {
const safeAddress = await protocolKit.getAddress()
const nonce = await getSafeNonceFromEntrypoint(protocolKit, safeAddress, entryPoint)
const nonce =
customNonce || (await getSafeNonceFromEntrypoint(protocolKit, safeAddress, entryPoint))
const isSafeDeployed = await protocolKit.isSafeDeployed()
const paymasterAndData =
paymasterOptions && 'paymasterAddress' in paymasterOptions
Expand Down
4 changes: 3 additions & 1 deletion playground/config/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ const playgroundRelayKitPaths = {
'userop-erc20-paymaster': 'relay-kit/userop-erc20-paymaster',
'userop-erc20-paymaster-counterfactual': 'relay-kit/userop-erc20-paymaster-counterfactual',
'userop-verifying-paymaster': 'relay-kit/userop-verifying-paymaster',
'userop-verifying-paymaster-counterfactual': 'relay-kit/userop-verifying-paymaster-counterfactual'
'userop-verifying-paymaster-counterfactual':
'relay-kit/userop-verifying-paymaster-counterfactual',
'userop-parallel-execution': 'relay-kit/userop-parallel-execution'
}

const playgroundStarterKitPaths = {
Expand Down
81 changes: 81 additions & 0 deletions playground/relay-kit/userop-parallel-execution.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import * as dotenv from 'dotenv'
import { encodeNonce, Safe4337Pack } from '@safe-global/relay-kit'
import { waitForOperationToFinish, setup4337Playground } from '../utils'

dotenv.config({ path: './playground/relay-kit/.env' })

const {
PRIVATE_KEY,
SAFE_ADDRESS = '0x',
RPC_URL = '',
CHAIN_ID = '',
BUNDLER_URL = ''
} = process.env

// PIM test token contract address
// faucet: https://dashboard.pimlico.io/test-erc20-faucet
const pimlicoTokenAddress = '0xFC3e86566895Fb007c6A0d3809eb2827DF94F751'

const NUMBER_OF_OPERATIONS = 2

async function main() {
// 1) Initialize pack
const safe4337Pack = await Safe4337Pack.init({
provider: RPC_URL,
signer: PRIVATE_KEY,
bundlerUrl: BUNDLER_URL,
safeModulesVersion: '0.3.0', // Blank or 0.3.0 for Entrypoint v0.7, 0.2.0 for Entrypoint v0.6
options: {
safeAddress: SAFE_ADDRESS
}
})

// 2) Setup Playground
const { transactions, timestamp } = await setup4337Playground(safe4337Pack, {
// nativeTokenAmount: parseEther('0.05'), // Increase this value when is not enough to cover the gas fees
erc20TokenAmount: 200_000n,
erc20TokenContractAddress: pimlicoTokenAddress
})

// 3) Create Multiple SafeOperations
const safeOperations = []

for (let i = 0; i < NUMBER_OF_OPERATIONS; i++) {
safeOperations.push(
safe4337Pack.createTransaction({
transactions,
options: {
validAfter: Number(timestamp - 60_000n),
validUntil: Number(timestamp + 60_000n),
customNonce: encodeNonce({
key: BigInt(Date.now()) + BigInt(i), // Ensure unique nonce
sequence: 0n
})
}
})
)
}

const createdSafeOperations = await Promise.all(safeOperations)

// 4) Sign all SafeOperations
const signingPromises = createdSafeOperations.map((op) => safe4337Pack.signSafeOperation(op))
const signedOperations = await Promise.all(signingPromises)

// Log all operations
signedOperations.forEach((op, index) => console.log(`SafeOperation ${index + 1}`, op))

// 5) Execute all operations in parallel
const executionPromises = signedOperations.map((op) =>
safe4337Pack.executeTransaction({ executable: op })
)

const userOperationHashes = await Promise.all(executionPromises)

// Wait for all operations to complete
await Promise.all(
userOperationHashes.map((hash) => waitForOperationToFinish(hash, CHAIN_ID, safe4337Pack))
)
}

main()
Loading