Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ jobs:
version: nightly
- name: Start Anvil in background
run: anvil --fork-url https://nodes.sequence.app/arbitrum &
- run: pnpm build
- run: pnpm test

# NOTE: if you'd like to see example of how to run
Expand Down
2 changes: 0 additions & 2 deletions packages/wallet/core/test/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,3 @@ export const USDC_ADDRESS: Address.Address = '0xaf88d065e77c8cc2239327c5edb3a432

// Environment variables
export const LOCAL_RPC_URL = process.env.LOCAL_RPC_URL || 'http://localhost:8545'
export const { RPC_URL, PRIVATE_KEY } = process.env
export const CAN_RUN_LIVE = !!RPC_URL && !!PRIVATE_KEY
27 changes: 20 additions & 7 deletions packages/wallet/core/test/session-manager.test.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
import { Extensions } from '@0xsequence/wallet-primitives'
import { AbiEvent, AbiFunction, Address, Bytes, Hex, Provider, RpcTransport, Secp256k1 } from 'ox'
import { describe, expect, it } from 'vitest'

import { Attestation, GenericTree, Payload, Permission, SessionConfig } from '../../primitives/src/index.js'
import { Envelope, Signers, State, Utils, Wallet } from '../src/index.js'

import { ExplicitSessionConfig } from '../src/utils/session/types.js'
import {
EMITTER_FUNCTIONS,
EMITTER_ADDRESS1,
EMITTER_ADDRESS2,
EMITTER_EVENT_TOPICS,
EMITTER_FUNCTIONS,
LOCAL_RPC_URL,
USDC_ADDRESS,
} from './constants'
import { Extensions } from '@0xsequence/wallet-primitives'
import { ExplicitSessionConfig } from '../src/utils/session/types.js'

const { PermissionBuilder, ERC20PermissionBuilder } = Utils

function randomAddress(): Address.Address {
Expand Down Expand Up @@ -605,10 +602,26 @@ for (const extension of ALL_EXTENSIONS) {
transaction: { to: Address.Address; data: Hex.Hex },
expectedEventTopic?: Hex.Hex,
) => {
// Generate and use a random sender address to prevent race conditions
const senderAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: Secp256k1.randomPrivateKey() }))
await provider.request({
method: 'anvil_setBalance',
params: [senderAddress, Hex.fromNumber(1000000000000000000n)],
})
await provider.request({
method: 'anvil_impersonateAccount',
params: [senderAddress],
})

console.log('Simulating transaction', transaction)
const txHash = await provider.request({
method: 'eth_sendTransaction',
params: [transaction],
params: [
{
...transaction,
from: senderAddress,
},
],
})
console.log('Transaction hash:', txHash)

Expand Down
9 changes: 0 additions & 9 deletions packages/wallet/core/vitest.config.ts

This file was deleted.

5 changes: 0 additions & 5 deletions packages/wallet/wdk/.env.test

This file was deleted.

2 changes: 0 additions & 2 deletions packages/wallet/wdk/test/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ export const EMITTER_ADDRESS: Address.Address = '0xb7bE532959236170064cf099e1a33
export const EMITTER_ABI = Abi.from(['function explicitEmit()', 'function implicitEmit()'])

// Environment variables
export const { RPC_URL, PRIVATE_KEY } = process.env
export const CAN_RUN_LIVE = !!RPC_URL && !!PRIVATE_KEY
export const LOCAL_RPC_URL = process.env.LOCAL_RPC_URL || 'http://localhost:8545'

let testIdCounter = 0
Expand Down
188 changes: 48 additions & 140 deletions packages/wallet/wdk/test/sessions.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { AbiFunction, Address, Bytes, Hex, Mnemonic, Provider, RpcTransport } from 'ox'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { AbiFunction, Address, Bytes, Hex, Mnemonic, Provider, RpcTransport, Secp256k1 } from 'ox'
import { beforeEach, describe, expect, it } from 'vitest'
import { Signers as CoreSigners, Wallet as CoreWallet, Envelope, State } from '../../core/src/index.js'
import { Attestation, Constants, Extensions, Network, Payload, Permission } from '../../primitives/src/index.js'
import { Sequence } from '../src/index.js'
import { CAN_RUN_LIVE, EMITTER_ABI, EMITTER_ADDRESS, PRIVATE_KEY, RPC_URL } from './constants'
import { ExplicitSession } from '../../core/src/utils/session/types.js'
import { Extensions, Network, Payload, Permission } from '../../primitives/src/index.js'
import { Sequence } from '../src/index.js'
import { EMITTER_ABI, EMITTER_ADDRESS, LOCAL_RPC_URL } from './constants'

const ALL_EXTENSIONS = [
{
Expand Down Expand Up @@ -67,26 +67,17 @@ for (const extension of ALL_EXTENSIONS) {
}

beforeEach(async () => {
// Create provider or use arbitrum sepolia
if (RPC_URL) {
provider = Provider.from(
RpcTransport.fromHttp(RPC_URL, {
fetchOptions: {
headers: {
'x-requested-with': 'XMLHttpRequest',
},
// Create provider using LOCAL_RPC_URL
provider = Provider.from(
RpcTransport.fromHttp(LOCAL_RPC_URL, {
fetchOptions: {
headers: {
'x-requested-with': 'XMLHttpRequest',
},
}),
)
chainId = Number(await provider.request({ method: 'eth_chainId' }))
} else {
provider = vi.mocked<Provider.Provider>({
request: vi.fn(),
on: vi.fn(),
removeListener: vi.fn(),
})
chainId = Network.ChainId.MAINNET
}
},
}),
)
chainId = Number(await provider.request({ method: 'eth_chainId' }))

// Create state provider
stateProvider = new State.Local.Provider()
Expand All @@ -99,7 +90,7 @@ for (const extension of ALL_EXTENSIONS) {
{
chainId,
type: Network.NetworkType.MAINNET,
rpcUrl: RPC_URL ?? 'XXX',
rpcUrl: LOCAL_RPC_URL,
name: 'XXX',
blockExplorer: { url: 'XXX' },
nativeCurrency: {
Expand Down Expand Up @@ -185,18 +176,32 @@ for (const extension of ALL_EXTENSIONS) {
const transaction = await dapp.wallet.buildTransaction(provider, signedEnvelope)
console.log('tx', transaction)

// Generate and use a random sender address to prevent race conditions
const senderAddress = Address.fromPublicKey(Secp256k1.getPublicKey({ privateKey: Secp256k1.randomPrivateKey() }))
await provider.request({
method: 'anvil_setBalance',
params: [senderAddress, Hex.fromNumber(1000000000000000000n)],
})
await provider.request({
method: 'anvil_impersonateAccount',
params: [senderAddress],
})

// Send the transaction
if (CAN_RUN_LIVE && PRIVATE_KEY) {
// Load the sender
const senderPk = Hex.from(PRIVATE_KEY as `0x${string}`)
const pkRelayer = new Relayer.Standard.PkRelayer(senderPk, provider)
const tx = await pkRelayer.relay(transaction.to, transaction.data, chainId, undefined)
console.log('Transaction sent', tx)
await new Promise((resolve) => setTimeout(resolve, 3000))
const receipt = await provider.request({ method: 'eth_getTransactionReceipt', params: [tx.opHash] })
console.log('Transaction receipt', receipt)
return tx.opHash
}
const txHash = await provider.request({
method: 'eth_sendTransaction',
params: [
{
...transaction,
from: senderAddress,
},
],
})
console.log('Transaction sent', txHash)
await new Promise((resolve) => setTimeout(resolve, 3000))
const receipt = await provider.request({ method: 'eth_getTransactionReceipt', params: [txHash] })
console.log('Transaction receipt', receipt)
return txHash
}

it(
Expand Down Expand Up @@ -243,6 +248,7 @@ for (const extension of ALL_EXTENSIONS) {
throw new Error('Failed to create pk store')
}
const explicitSession: ExplicitSession = {
type: 'explicit',
sessionAddress: e.address,
chainId,
valueLimit: 0n,
Expand Down Expand Up @@ -271,34 +277,10 @@ for (const extension of ALL_EXTENSIONS) {
behaviorOnError: 'revert',
}

if (!RPC_URL) {
// Configure mock provider
;(provider as any).request.mockImplementation(({ method, params }) => {
if (method === 'eth_chainId') {
return Promise.resolve(chainId.toString())
}
if (method === 'eth_call' && params[0].data === AbiFunction.encodeData(Constants.GET_IMPLEMENTATION)) {
// Undeployed wallet
return Promise.resolve('0x')
}
if (method === 'eth_call' && params[0].data === AbiFunction.encodeData(Constants.READ_NONCE, [0n])) {
// Nonce is 0
return Promise.resolve('0x00')
}
if (
method === 'eth_call' &&
params[0].data?.startsWith(AbiFunction.getSelector(Constants.GET_LIMIT_USAGE))
) {
// Return 0 for usage limit (no usage yet)
return Promise.resolve('0x0000000000000000000000000000000000000000000000000000000000000000')
}
})
}

// Sign and send the transaction
await signAndSend(call)
},
PRIVATE_KEY || RPC_URL ? { timeout: 60000 } : undefined,
{ timeout: 60000 },
)

it(
Expand All @@ -311,6 +293,7 @@ for (const extension of ALL_EXTENSIONS) {
throw new Error('Failed to create pk store')
}
const explicitSession: ExplicitSession = {
type: 'explicit',
sessionAddress: e.address,
chainId,
valueLimit: 0n,
Expand Down Expand Up @@ -348,34 +331,10 @@ for (const extension of ALL_EXTENSIONS) {
behaviorOnError: 'revert',
}

if (!RPC_URL) {
// Configure mock provider
;(provider as any).request.mockImplementation(({ method, params }) => {
if (method === 'eth_chainId') {
return Promise.resolve(chainId.toString())
}
if (method === 'eth_call' && params[0].data === AbiFunction.encodeData(Constants.GET_IMPLEMENTATION)) {
// Undeployed wallet
return Promise.resolve('0x')
}
if (method === 'eth_call' && params[0].data === AbiFunction.encodeData(Constants.READ_NONCE, [0n])) {
// Nonce is 0
return Promise.resolve('0x00')
}
if (
method === 'eth_call' &&
params[0].data?.startsWith(AbiFunction.getSelector(Constants.GET_LIMIT_USAGE))
) {
// Return 0 for usage limit (no usage yet)
return Promise.resolve('0x0000000000000000000000000000000000000000000000000000000000000000')
}
})
}

// Sign and send the transaction
await signAndSend(call)
},
PRIVATE_KEY || RPC_URL ? { timeout: 60000 } : undefined,
{ timeout: 60000 },
)

it(
Expand All @@ -389,6 +348,7 @@ for (const extension of ALL_EXTENSIONS) {
}
// Create the initial permissions
let explicitSession: ExplicitSession = {
type: 'explicit',
sessionAddress: e.address,
chainId,
valueLimit: 0n,
Expand Down Expand Up @@ -426,30 +386,6 @@ for (const extension of ALL_EXTENSIONS) {
behaviorOnError: 'revert',
}

if (!RPC_URL) {
// Configure mock provider
;(provider as any).request.mockImplementation(({ method, params }) => {
if (method === 'eth_chainId') {
return Promise.resolve(chainId.toString())
}
if (method === 'eth_call' && params[0].data === AbiFunction.encodeData(Constants.GET_IMPLEMENTATION)) {
// Undeployed wallet
return Promise.resolve('0x')
}
if (method === 'eth_call' && params[0].data === AbiFunction.encodeData(Constants.READ_NONCE, [0n])) {
// Nonce is 0
return Promise.resolve('0x00')
}
if (
method === 'eth_call' &&
params[0].data?.startsWith(AbiFunction.getSelector(Constants.GET_LIMIT_USAGE))
) {
// Return 0 for usage limit (no usage yet)
return Promise.resolve('0x0000000000000000000000000000000000000000000000000000000000000000')
}
})
}

// Sign and send the transaction
await signAndSend(call)

Expand All @@ -463,7 +399,7 @@ for (const extension of ALL_EXTENSIONS) {
// Should fail with 'No signer supported for call'
await expect(signAndSend(call)).rejects.toThrow('No signer supported for call')
},
PRIVATE_KEY || RPC_URL ? { timeout: 60000 } : undefined,
{ timeout: 60000 },
)

it(
Expand Down Expand Up @@ -516,38 +452,10 @@ for (const extension of ALL_EXTENSIONS) {
behaviorOnError: 'revert',
}

if (!RPC_URL) {
// Configure mock provider
;(provider as any).request.mockImplementation(({ method, params }) => {
if (method === 'eth_chainId') {
return Promise.resolve(chainId.toString())
}
if (method === 'eth_call' && params[0].data === AbiFunction.encodeData(Constants.GET_IMPLEMENTATION)) {
// Undeployed wallet
return Promise.resolve('0x')
}
if (method === 'eth_call' && params[0].data === AbiFunction.encodeData(Constants.READ_NONCE, [0n])) {
// Nonce is 0
return Promise.resolve('0x00')
}
if (
method === 'eth_call' &&
Address.isEqual(params[0].from, dapp.sessionManager.address) &&
Address.isEqual(params[0].to, call.to)
) {
// Implicit request simulation result
const expectedResult = Bytes.toHex(
Attestation.generateImplicitRequestMagic(attestation, dapp.wallet.address),
)
return Promise.resolve(expectedResult)
}
})
}

// Sign and send the transaction
await signAndSend(call)
},
PRIVATE_KEY || RPC_URL ? { timeout: 60000 } : undefined,
{ timeout: 60000 },
)
})
}
1 change: 0 additions & 1 deletion packages/wallet/wdk/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { BrowserNavigationCrossOriginPolicyEnum } from 'happy-dom'
import { defineConfig } from 'vitest/config'

export default defineConfig({
Expand Down