diff --git a/packages/wallet/core/src/signers/session-manager.ts b/packages/wallet/core/src/signers/session-manager.ts index 301d54b05..309b3017d 100644 --- a/packages/wallet/core/src/signers/session-manager.ts +++ b/packages/wallet/core/src/signers/session-manager.ts @@ -193,7 +193,7 @@ export class SessionManager implements SapientSigner { throw new Error('Repeated usage hashes') } - const data = AbiFunction.encodeData(Constants.INCREMENT_USAGE_LIMIT, [uniqueIncrements]) + const data = AbiFunction.encodeData(Constants.INCREMENT_USAGE_LIMIT, [increments]) return { to: this.address, diff --git a/packages/wallet/core/src/signers/session/explicit.ts b/packages/wallet/core/src/signers/session/explicit.ts index 56d7f1c4c..dda49cb62 100644 --- a/packages/wallet/core/src/signers/session/explicit.ts +++ b/packages/wallet/core/src/signers/session/explicit.ts @@ -250,6 +250,7 @@ export class Explicit implements ExplicitSessionSigner { ): Promise { const increments: { usageHash: Hex.Hex; increment: bigint }[] = [] const usageValueHash = this.getValueUsageHash() + let valueUsed = 0n for (const call of calls) { // Find matching permission @@ -281,18 +282,15 @@ export class Explicit implements ExplicitSessionSigner { } } - // Check the value - if (call.value !== 0n) { - const existingIncrement = increments.find((i) => Hex.isEqual(i.usageHash, usageValueHash)) - if (existingIncrement) { - existingIncrement.increment += call.value - } else { - increments.push({ - usageHash: usageValueHash, - increment: call.value, - }) - } - } + valueUsed += call.value + } + + // Check the value + if (valueUsed > 0n) { + increments.push({ + usageHash: usageValueHash, + increment: valueUsed, + }) } // If no increments, return early diff --git a/packages/wallet/dapp-client/src/ChainSessionManager.ts b/packages/wallet/dapp-client/src/ChainSessionManager.ts index 0ba9d0341..cfec423fb 100644 --- a/packages/wallet/dapp-client/src/ChainSessionManager.ts +++ b/packages/wallet/dapp-client/src/ChainSessionManager.ts @@ -1,5 +1,5 @@ import { Envelope, Relayer, Signers, State, Wallet } from '@0xsequence/wallet-core' -import { Attestation, Extensions, Payload, SessionConfig } from '@0xsequence/wallet-primitives' +import { Attestation, Constants, Extensions, Payload, SessionConfig } from '@0xsequence/wallet-primitives' import { AbiFunction, Address, Hex, Provider, RpcTransport, Secp256k1 } from 'ox' import { DappTransport } from './DappTransport.js' @@ -36,7 +36,7 @@ import { Transaction, TransportMode, } from './types/index.js' -import { CACHE_DB_NAME } from './utils/constants.js' +import { CACHE_DB_NAME, VALUE_FORWARDER_ADDRESS } from './utils/constants.js' import { TypedData } from 'ox/TypedData' interface ChainSessionManagerEventMap { @@ -398,6 +398,11 @@ export class ChainSessionManager { try { if (!this.transport) throw new InitializationError('Transport failed to initialize.') + const session = this.sessions.find((s) => Address.isEqual(s.address, sessionAddress)) + if (!session) { + throw new ModifyExplicitSessionError('Session not found.') + } + const payload: ModifySessionPayload = { walletAddress: this.walletAddress, sessionAddress: sessionAddress, @@ -427,6 +432,8 @@ export class ChainSessionManager { throw new ModifyExplicitSessionError('Wallet or session address mismatch.') } + session.permissions = newPermissions + if (this.transport?.mode === TransportMode.POPUP) { this.transport?.closeWallet() } @@ -614,6 +621,8 @@ export class ChainSessionManager { this.sessions.push({ address: explicitSigner.address, isImplicit: false, + chainId: this.chainId, + permissions, }) return } catch (err) { @@ -660,7 +669,7 @@ export class ChainSessionManager { * @throws {InitializationError} If the session is not initialized. * @throws {TransactionError} If the transaction fails at any stage. */ - async buildSignAndSendTransactions(transactions: Transaction[], feeOption?: Relayer.FeeOption): Promise { + async buildSignAndSendTransactions(transactions: Transaction[], feeOption?: Relayer.FeeOption): Promise { if (!this.wallet || !this.sessionManager || !this.provider || !this.isInitialized) throw new InitializationError('Session is not initialized.') try { @@ -676,17 +685,30 @@ export class ChainSessionManager { const callsToSend = calls if (feeOption) { - const transfer = AbiFunction.from(['function transfer(address to, uint256 value)']) - const transferCall: Payload.Call = { - to: feeOption.token.contractAddress as `0x${string}`, - value: BigInt(0), - data: AbiFunction.encodeData(transfer, [feeOption.to as Address.Address, BigInt(feeOption.value)]), - gasLimit: BigInt(feeOption.gasLimit), - delegateCall: false, - onlyFallback: false, - behaviorOnError: 'revert' as const, + if (feeOption.token.contractAddress === Constants.ZeroAddress) { + const forwardValue = AbiFunction.from(['function forwardValue(address to, uint256 value)']) + callsToSend.unshift({ + to: VALUE_FORWARDER_ADDRESS, + value: BigInt(feeOption.value), + data: AbiFunction.encodeData(forwardValue, [feeOption.to as Address.Address, BigInt(feeOption.value)]), + gasLimit: BigInt(feeOption.gasLimit), + delegateCall: false, + onlyFallback: false, + behaviorOnError: 'revert' as const, + }) + } else { + const transfer = AbiFunction.from(['function transfer(address to, uint256 value)']) + const transferCall: Payload.Call = { + to: feeOption.token.contractAddress as `0x${string}`, + value: BigInt(0), + data: AbiFunction.encodeData(transfer, [feeOption.to as Address.Address, BigInt(feeOption.value)]), + gasLimit: BigInt(feeOption.gasLimit), + delegateCall: false, + onlyFallback: false, + behaviorOnError: 'revert' as const, + } + callsToSend.unshift(transferCall) } - callsToSend.unshift(transferCall) } const signedCalls = await this._buildAndSignCalls(callsToSend) const hash = await this.relayer.relay(signedCalls.to, signedCalls.data, BigInt(this.chainId)) diff --git a/packages/wallet/dapp-client/src/DappClient.ts b/packages/wallet/dapp-client/src/DappClient.ts index 4913bd121..9a1c47196 100644 --- a/packages/wallet/dapp-client/src/DappClient.ts +++ b/packages/wallet/dapp-client/src/DappClient.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { ChainId } from '@0xsequence/network' import { Relayer, Signers } from '@0xsequence/wallet-core' -import { Address } from 'ox' +import { Address, Hex } from 'ox' import { ChainSessionManager } from './ChainSessionManager.js' import { DappTransport } from './DappTransport.js' @@ -534,7 +534,11 @@ export class DappClient { * * const txHash = await dappClient.sendTransaction(1, [transaction]); */ - async sendTransaction(chainId: ChainId, transactions: Transaction[], feeOption?: Relayer.FeeOption): Promise { + async sendTransaction( + chainId: ChainId, + transactions: Transaction[], + feeOption?: Relayer.FeeOption, + ): Promise { if (!this.isInitialized) throw new InitializationError('Not initialized') const chainSessionManager = this.getChainSessionManager(chainId) if (!chainSessionManager.isInitialized) diff --git a/packages/wallet/dapp-client/src/types/index.ts b/packages/wallet/dapp-client/src/types/index.ts index d96d51339..1601cff1a 100644 --- a/packages/wallet/dapp-client/src/types/index.ts +++ b/packages/wallet/dapp-client/src/types/index.ts @@ -90,6 +90,8 @@ export type Transaction = export type Session = { address: Address.Address isImplicit: boolean + permissions?: Signers.Session.ExplicitParams + chainId?: ChainId } // --- Event Types --- diff --git a/packages/wallet/dapp-client/src/utils/constants.ts b/packages/wallet/dapp-client/src/utils/constants.ts index 95f1b0ec7..2232ac0e3 100644 --- a/packages/wallet/dapp-client/src/utils/constants.ts +++ b/packages/wallet/dapp-client/src/utils/constants.ts @@ -2,3 +2,4 @@ export const CACHE_DB_NAME = 'sequence-cache' export const NODES_URL = 'https://nodes.sequence.app/{network}' export const RELAYER_URL = 'https://dev-{network}-relayer.sequence.app' export const DEFAULT_KEYMACHINE_URL = 'https://v3-keymachine.sequence-dev.app' +export const VALUE_FORWARDER_ADDRESS = '0xABAAd93EeE2a569cF0632f39B10A9f5D734777ca'