diff --git a/package.json b/package.json index 38bc33117..af9b64c38 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "prettier": "^3.8.3", "rimraf": "^6.1.3", "syncpack": "^14.3.1", - "turbo": "^2.9.8", + "turbo": "^2.9.14", "typescript": "^6.0.3" }, "pnpm": { diff --git a/packages/services/api/CHANGELOG.md b/packages/services/api/CHANGELOG.md index 37b4ea332..abf1bac7a 100644 --- a/packages/services/api/CHANGELOG.md +++ b/packages/services/api/CHANGELOG.md @@ -1,5 +1,11 @@ # @0xsequence/api +## 3.0.11 + +### Patch Changes + +- Fix for relayer sponsored fees + ## 3.0.10 ### Patch Changes diff --git a/packages/services/api/package.json b/packages/services/api/package.json index d05f1ddbe..f17292579 100644 --- a/packages/services/api/package.json +++ b/packages/services/api/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/api", - "version": "3.0.10", + "version": "3.0.11", "description": "api sub-package for Sequence", "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/api", "author": "Sequence Platforms ULC", diff --git a/packages/services/builder/CHANGELOG.md b/packages/services/builder/CHANGELOG.md index 1e76b7c90..501288464 100644 --- a/packages/services/builder/CHANGELOG.md +++ b/packages/services/builder/CHANGELOG.md @@ -1,5 +1,11 @@ # @0xsequence/builder +## 3.0.11 + +### Patch Changes + +- Fix for relayer sponsored fees + ## 3.0.10 ### Patch Changes diff --git a/packages/services/builder/package.json b/packages/services/builder/package.json index 56f014585..84cf23976 100644 --- a/packages/services/builder/package.json +++ b/packages/services/builder/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/builder", - "version": "3.0.10", + "version": "3.0.11", "description": "builder sub-package for Sequence", "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/builder", "author": "Sequence Platforms ULC", diff --git a/packages/services/guard/CHANGELOG.md b/packages/services/guard/CHANGELOG.md index d0d37458d..f9125f1be 100644 --- a/packages/services/guard/CHANGELOG.md +++ b/packages/services/guard/CHANGELOG.md @@ -1,5 +1,11 @@ # @0xsequence/guard +## 3.0.11 + +### Patch Changes + +- Fix for relayer sponsored fees + ## 3.0.10 ### Patch Changes diff --git a/packages/services/guard/package.json b/packages/services/guard/package.json index 3bc3e0f66..e4c7c1f05 100644 --- a/packages/services/guard/package.json +++ b/packages/services/guard/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/guard", - "version": "3.0.10", + "version": "3.0.11", "description": "guard sub-package for Sequence", "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/guard", "author": "Sequence Platforms ULC", diff --git a/packages/services/identity-instrument/CHANGELOG.md b/packages/services/identity-instrument/CHANGELOG.md index 845912fe8..7a3f20388 100644 --- a/packages/services/identity-instrument/CHANGELOG.md +++ b/packages/services/identity-instrument/CHANGELOG.md @@ -1,5 +1,11 @@ # @0xsequence/identity-instrument +## 3.0.11 + +### Patch Changes + +- Fix for relayer sponsored fees + ## 3.0.10 ### Patch Changes diff --git a/packages/services/identity-instrument/package.json b/packages/services/identity-instrument/package.json index 7c37bbcc3..2dffdd488 100644 --- a/packages/services/identity-instrument/package.json +++ b/packages/services/identity-instrument/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/identity-instrument", - "version": "3.0.10", + "version": "3.0.11", "license": "Apache-2.0", "type": "module", "publishConfig": { diff --git a/packages/services/indexer/CHANGELOG.md b/packages/services/indexer/CHANGELOG.md index 693670b22..34389ffed 100644 --- a/packages/services/indexer/CHANGELOG.md +++ b/packages/services/indexer/CHANGELOG.md @@ -1,5 +1,11 @@ # @0xsequence/indexer +## 3.0.11 + +### Patch Changes + +- Fix for relayer sponsored fees + ## 3.0.10 ### Patch Changes diff --git a/packages/services/indexer/package.json b/packages/services/indexer/package.json index 9833ffaa9..f8b17dac8 100644 --- a/packages/services/indexer/package.json +++ b/packages/services/indexer/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/indexer", - "version": "3.0.10", + "version": "3.0.11", "description": "indexer sub-package for Sequence", "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/indexer", "author": "Sequence Platforms ULC", diff --git a/packages/services/marketplace/CHANGELOG.md b/packages/services/marketplace/CHANGELOG.md index b99659fe6..56b3ea4a6 100644 --- a/packages/services/marketplace/CHANGELOG.md +++ b/packages/services/marketplace/CHANGELOG.md @@ -1,5 +1,11 @@ # @0xsequence/marketplace +## 3.0.11 + +### Patch Changes + +- Fix for relayer sponsored fees + ## 3.0.10 ### Patch Changes diff --git a/packages/services/marketplace/package.json b/packages/services/marketplace/package.json index 1ac1889a4..23a7866f8 100644 --- a/packages/services/marketplace/package.json +++ b/packages/services/marketplace/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/marketplace", - "version": "3.0.10", + "version": "3.0.11", "description": "marketplace sub-package for Sequence", "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/marketplace", "author": "Sequence Platforms ULC", diff --git a/packages/services/metadata/CHANGELOG.md b/packages/services/metadata/CHANGELOG.md index c284615b6..74d47b401 100644 --- a/packages/services/metadata/CHANGELOG.md +++ b/packages/services/metadata/CHANGELOG.md @@ -1,5 +1,11 @@ # @0xsequence/metadata +## 3.0.11 + +### Patch Changes + +- Fix for relayer sponsored fees + ## 3.0.10 ### Patch Changes diff --git a/packages/services/metadata/package.json b/packages/services/metadata/package.json index db4ec068a..234b7ea9c 100644 --- a/packages/services/metadata/package.json +++ b/packages/services/metadata/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/metadata", - "version": "3.0.10", + "version": "3.0.11", "publishConfig": { "access": "public" }, diff --git a/packages/services/relayer/CHANGELOG.md b/packages/services/relayer/CHANGELOG.md index 507428d5b..951a31f26 100644 --- a/packages/services/relayer/CHANGELOG.md +++ b/packages/services/relayer/CHANGELOG.md @@ -1,5 +1,31 @@ # @0xsequence/relayer +## 3.1.0 + +### Minor Changes + +- 55121af: Surface explicit sponsorship signal on `feeOptions` and an error marker on + `feeOptions` / `feeTokens`. + - `RpcRelayer.feeOptions` now returns `sponsored: boolean`, forwarded from the + server's `FeeOptionsReturn.sponsored`. The `Relayer` interface and all + bundled implementations (`RpcRelayer`, `SequenceRelayer`, `LocalRelayer`, + `EIP6963Relayer`, `PkRelayer`) carry the new field. + - When `feeOptions` swallows a transport / server error it now returns + `{ options: [], sponsored: false, failed: true }` (was `{ options: [] }`). + - When `feeTokens` swallows an error it now returns + `{ isFeeRequired: false, failed: true }` (was `{ isFeeRequired: false }`). + + These changes are additive — existing consumers that ignore the new fields are + unaffected. Consumers that classified sponsorship by "no fee option attached" + should migrate to `sponsored === true` to distinguish a real subsidy from a + swallowed `/FeeOptions` error. + +### Patch Changes + +- Fix for relayer sponsored fees +- Updated dependencies + - @0xsequence/wallet-primitives@3.0.11 + ## 3.0.10 ### Patch Changes diff --git a/packages/services/relayer/package.json b/packages/services/relayer/package.json index ec2a8dcd9..284ceae37 100644 --- a/packages/services/relayer/package.json +++ b/packages/services/relayer/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/relayer", - "version": "3.0.10", + "version": "3.1.0", "type": "module", "publishConfig": { "access": "public" diff --git a/packages/services/relayer/src/relayer/relayer.ts b/packages/services/relayer/src/relayer/relayer.ts index 9f648004a..d0c558082 100644 --- a/packages/services/relayer/src/relayer/relayer.ts +++ b/packages/services/relayer/src/relayer/relayer.ts @@ -11,7 +11,12 @@ export interface Relayer { isAvailable(wallet: Address.Address, chainId: number): Promise - feeTokens(): Promise<{ isFeeRequired: boolean; tokens?: FeeToken[]; paymentAddress?: Address.Address }> + feeTokens(): Promise<{ + isFeeRequired: boolean + tokens?: FeeToken[] + paymentAddress?: Address.Address + failed?: boolean + }> feeOptions( wallet: Address.Address, @@ -19,7 +24,7 @@ export interface Relayer { to: Address.Address, calls: Payload.Call[], data?: Hex.Hex, - ): Promise<{ options: FeeOption[]; quote?: FeeQuote }> + ): Promise<{ options: FeeOption[]; quote?: FeeQuote; sponsored: boolean; failed?: boolean }> relay(to: Address.Address, data: Hex.Hex, chainId: number, quote?: FeeQuote): Promise<{ opHash: Hex.Hex }> diff --git a/packages/services/relayer/src/relayer/rpc-relayer/index.ts b/packages/services/relayer/src/relayer/rpc-relayer/index.ts index 814d25bbd..05ba8be76 100644 --- a/packages/services/relayer/src/relayer/rpc-relayer/index.ts +++ b/packages/services/relayer/src/relayer/rpc-relayer/index.ts @@ -123,7 +123,12 @@ export class RpcRelayer implements Relayer { return Promise.resolve(this.chainId === chainId) } - async feeTokens(): Promise<{ isFeeRequired: boolean; tokens?: RpcFeeToken[]; paymentAddress?: Address.Address }> { + async feeTokens(): Promise<{ + isFeeRequired: boolean + tokens?: RpcFeeToken[] + paymentAddress?: Address.Address + failed?: boolean + }> { try { const { isFeeRequired, tokens, paymentAddress } = await this.client.feeTokens() if (isFeeRequired) { @@ -140,7 +145,7 @@ export class RpcRelayer implements Relayer { } } catch (e) { console.warn('RpcRelayer.feeTokens failed:', e) - return { isFeeRequired: false } + return { isFeeRequired: false, failed: true } } } @@ -150,7 +155,7 @@ export class RpcRelayer implements Relayer { to: Address.Address, calls: Payload.Call[], data?: Hex.Hex, - ): Promise<{ options: FeeOption[]; quote?: FeeQuote }> { + ): Promise<{ options: FeeOption[]; quote?: FeeQuote; sponsored: boolean; failed?: boolean }> { // IMPORTANT: // The relayer FeeOptions endpoint simulates `eth_call(to, data)`. // Callers that already built a wallet transaction should pass its `to` and `data`. @@ -182,10 +187,10 @@ export class RpcRelayer implements Relayer { gasLimit: option.gasLimit, })) - return { options, quote } + return { options, quote, sponsored: result.sponsored } } catch (e) { console.warn('RpcRelayer.feeOptions failed:', e) - return { options: [] } + return { options: [], sponsored: false, failed: true } } } diff --git a/packages/services/relayer/src/relayer/standard/eip6963.ts b/packages/services/relayer/src/relayer/standard/eip6963.ts index a290b2c6f..957b13c44 100644 --- a/packages/services/relayer/src/relayer/standard/eip6963.ts +++ b/packages/services/relayer/src/relayer/standard/eip6963.ts @@ -23,7 +23,12 @@ export class EIP6963Relayer implements Relayer { return this.relayer.isAvailable(wallet, chainId) } - feeTokens(): Promise<{ isFeeRequired: boolean; tokens?: FeeToken[]; paymentAddress?: Address.Address }> { + feeTokens(): Promise<{ + isFeeRequired: boolean + tokens?: FeeToken[] + paymentAddress?: Address.Address + failed?: boolean + }> { return this.relayer.feeTokens() } @@ -32,7 +37,7 @@ export class EIP6963Relayer implements Relayer { chainId: number, to: Address.Address, calls: Payload.Call[], - ): Promise<{ options: FeeOption[]; quote?: FeeQuote }> { + ): Promise<{ options: FeeOption[]; quote?: FeeQuote; sponsored: boolean; failed?: boolean }> { return this.relayer.feeOptions(wallet, chainId, to, calls) } diff --git a/packages/services/relayer/src/relayer/standard/local.ts b/packages/services/relayer/src/relayer/standard/local.ts index 4135af3b3..e8066674c 100644 --- a/packages/services/relayer/src/relayer/standard/local.ts +++ b/packages/services/relayer/src/relayer/standard/local.ts @@ -56,7 +56,12 @@ export class LocalRelayer implements Relayer { return new LocalRelayer(new EIP1193ProviderAdapter(provider)) } - feeTokens(): Promise<{ isFeeRequired: boolean; tokens?: FeeToken[]; paymentAddress?: Address.Address }> { + feeTokens(): Promise<{ + isFeeRequired: boolean + tokens?: FeeToken[] + paymentAddress?: Address.Address + failed?: boolean + }> { return Promise.resolve({ isFeeRequired: false, }) @@ -67,8 +72,8 @@ export class LocalRelayer implements Relayer { _chainId: number, _to: Address.Address, _calls: Payload.Call[], - ): Promise<{ options: FeeOption[]; quote?: FeeQuote }> { - return Promise.resolve({ options: [] }) + ): Promise<{ options: FeeOption[]; quote?: FeeQuote; sponsored: boolean; failed?: boolean }> { + return Promise.resolve({ options: [], sponsored: false }) } async relay( diff --git a/packages/services/relayer/src/relayer/standard/pk-relayer.ts b/packages/services/relayer/src/relayer/standard/pk-relayer.ts index b1d420a58..28ea0850a 100644 --- a/packages/services/relayer/src/relayer/standard/pk-relayer.ts +++ b/packages/services/relayer/src/relayer/standard/pk-relayer.ts @@ -107,7 +107,12 @@ export class PkRelayer implements Relayer { return providerChainId === chainId } - feeTokens(): Promise<{ isFeeRequired: boolean; tokens?: FeeToken[]; paymentAddress?: Address.Address }> { + feeTokens(): Promise<{ + isFeeRequired: boolean + tokens?: FeeToken[] + paymentAddress?: Address.Address + failed?: boolean + }> { return this.relayer.feeTokens() } @@ -116,7 +121,7 @@ export class PkRelayer implements Relayer { chainId: number, to: Address.Address, calls: Payload.Call[], - ): Promise<{ options: FeeOption[]; quote?: FeeQuote }> { + ): Promise<{ options: FeeOption[]; quote?: FeeQuote; sponsored: boolean; failed?: boolean }> { return this.relayer.feeOptions(wallet, chainId, to, calls) } diff --git a/packages/services/relayer/src/relayer/standard/sequence.ts b/packages/services/relayer/src/relayer/standard/sequence.ts index 1ae5ec69b..8519c618a 100644 --- a/packages/services/relayer/src/relayer/standard/sequence.ts +++ b/packages/services/relayer/src/relayer/standard/sequence.ts @@ -17,7 +17,12 @@ export class SequenceRelayer implements Relayer { return true } - async feeTokens(): Promise<{ isFeeRequired: boolean; tokens?: FeeToken[]; paymentAddress?: Address.Address }> { + async feeTokens(): Promise<{ + isFeeRequired: boolean + tokens?: FeeToken[] + paymentAddress?: Address.Address + failed?: boolean + }> { const { isFeeRequired, tokens, paymentAddress } = await this.service.feeTokens() if (isFeeRequired) { Address.assert(paymentAddress) @@ -39,17 +44,18 @@ export class SequenceRelayer implements Relayer { to: Address.Address, calls: Payload.Call[], transactionData?: Hex.Hex, - ): Promise<{ options: FeeOption[]; quote?: FeeQuote }> { + ): Promise<{ options: FeeOption[]; quote?: FeeQuote; sponsored: boolean; failed?: boolean }> { const execute = AbiFunction.from('function execute(bytes calldata _payload, bytes calldata _signature)') const payload = Payload.encode({ type: 'call', space: 0n, nonce: 0n, calls }, to) const signature = '0x0001' // TODO: use a stub signature const data = transactionData ?? AbiFunction.encodeData(execute, [Bytes.toHex(payload), signature]) - const { options, quote } = await this.service.feeOptions({ wallet, to, data }) + const { options, quote, sponsored } = await this.service.feeOptions({ wallet, to, data }) return { options, quote: quote ? { _tag: 'FeeQuote', _quote: quote } : undefined, + sponsored, } } diff --git a/packages/services/relayer/test/relayer/relayer.test.ts b/packages/services/relayer/test/relayer/relayer.test.ts index 028ccf32f..cd8c096a3 100644 --- a/packages/services/relayer/test/relayer/relayer.test.ts +++ b/packages/services/relayer/test/relayer/relayer.test.ts @@ -294,6 +294,7 @@ describe('Relayer', () => { vi.mocked(mockRelayer.feeOptions).mockResolvedValue({ options: [], quote: undefined, + sponsored: false, }) vi.mocked(mockRelayer.relay).mockResolvedValue({ opHash: TEST_OP_HASH, @@ -375,6 +376,60 @@ describe('Relayer', () => { data: expectedData, }) }) + + it('should propagate sponsored:true from the server', async () => { + const fetchImpl = vi.fn( + async () => new Response(JSON.stringify({ options: [], sponsored: true }), { status: 200 }), + ) + const relayer = new Relayer.RpcRelayer('https://relayer.test', TEST_CHAIN_ID, 'https://rpc.test', fetchImpl) + + const result = await relayer.feeOptions(TEST_WALLET_ADDRESS, TEST_CHAIN_ID, TEST_TO_ADDRESS, [mockCall]) + + expect(result.sponsored).toBe(true) + expect(result.options).toEqual([]) + expect(result.failed).toBeUndefined() + }) + + it('should propagate sponsored:false from the server', async () => { + const fetchImpl = vi.fn( + async () => new Response(JSON.stringify({ options: [], sponsored: false }), { status: 200 }), + ) + const relayer = new Relayer.RpcRelayer('https://relayer.test', TEST_CHAIN_ID, 'https://rpc.test', fetchImpl) + + const result = await relayer.feeOptions(TEST_WALLET_ADDRESS, TEST_CHAIN_ID, TEST_TO_ADDRESS, [mockCall]) + + expect(result.sponsored).toBe(false) + expect(result.failed).toBeUndefined() + }) + + it('should return sponsored:false and failed:true when the server errors', async () => { + const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}) + const fetchImpl = vi.fn( + async () => + new Response(JSON.stringify({ error: 'Aborted', code: 1005, msg: 'simulation failed' }), { status: 400 }), + ) + const relayer = new Relayer.RpcRelayer('https://relayer.test', TEST_CHAIN_ID, 'https://rpc.test', fetchImpl) + + const result = await relayer.feeOptions(TEST_WALLET_ADDRESS, TEST_CHAIN_ID, TEST_TO_ADDRESS, [mockCall]) + + expect(result).toEqual({ options: [], sponsored: false, failed: true }) + expect(warn).toHaveBeenCalled() + warn.mockRestore() + }) + }) + + describe('RpcRelayer.feeTokens', () => { + it('should return failed:true when the server errors', async () => { + const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}) + const fetchImpl = vi.fn(async () => new Response('boom', { status: 500 })) + const relayer = new Relayer.RpcRelayer('https://relayer.test', TEST_CHAIN_ID, 'https://rpc.test', fetchImpl) + + const result = await relayer.feeTokens() + + expect(result).toEqual({ isFeeRequired: false, failed: true }) + expect(warn).toHaveBeenCalled() + warn.mockRestore() + }) }) describe('Type compatibility', () => { diff --git a/packages/services/userdata/CHANGELOG.md b/packages/services/userdata/CHANGELOG.md index 139297551..ebf00e9eb 100644 --- a/packages/services/userdata/CHANGELOG.md +++ b/packages/services/userdata/CHANGELOG.md @@ -1,5 +1,11 @@ # @0xsequence/userdata +## 3.0.11 + +### Patch Changes + +- Fix for relayer sponsored fees + ## 3.0.10 ### Patch Changes diff --git a/packages/services/userdata/package.json b/packages/services/userdata/package.json index e546d8f22..0483c76a3 100644 --- a/packages/services/userdata/package.json +++ b/packages/services/userdata/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/userdata", - "version": "3.0.10", + "version": "3.0.11", "description": "userdata sub-package for Sequence", "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/userdata", "author": "Sequence Platforms ULC", diff --git a/packages/utils/abi/CHANGELOG.md b/packages/utils/abi/CHANGELOG.md index 66866e4e3..fca1d35a4 100644 --- a/packages/utils/abi/CHANGELOG.md +++ b/packages/utils/abi/CHANGELOG.md @@ -1,5 +1,11 @@ # @0xsequence/abi +## 3.0.11 + +### Patch Changes + +- Fix for relayer sponsored fees + ## 3.0.10 ### Patch Changes diff --git a/packages/utils/abi/package.json b/packages/utils/abi/package.json index 34b211947..6b6023abd 100644 --- a/packages/utils/abi/package.json +++ b/packages/utils/abi/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/abi", - "version": "3.0.10", + "version": "3.0.11", "description": "abi sub-package for Sequence", "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/utils/abi", "author": "Sequence Platforms ULC", diff --git a/packages/wallet/core/CHANGELOG.md b/packages/wallet/core/CHANGELOG.md index 03d06db61..8c79704d7 100644 --- a/packages/wallet/core/CHANGELOG.md +++ b/packages/wallet/core/CHANGELOG.md @@ -1,5 +1,16 @@ # @0xsequence/wallet-core +## 3.0.11 + +### Patch Changes + +- Fix for relayer sponsored fees +- Updated dependencies +- Updated dependencies [55121af] + - @0xsequence/guard@3.0.11 + - @0xsequence/relayer@3.1.0 + - @0xsequence/wallet-primitives@3.0.11 + ## 3.0.10 ### Patch Changes diff --git a/packages/wallet/core/package.json b/packages/wallet/core/package.json index 95457a578..0c9ca7535 100644 --- a/packages/wallet/core/package.json +++ b/packages/wallet/core/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/wallet-core", - "version": "3.0.10", + "version": "3.0.11", "license": "Apache-2.0", "type": "module", "publishConfig": { diff --git a/packages/wallet/dapp-client/CHANGELOG.md b/packages/wallet/dapp-client/CHANGELOG.md index 492c1ec2e..03750f575 100644 --- a/packages/wallet/dapp-client/CHANGELOG.md +++ b/packages/wallet/dapp-client/CHANGELOG.md @@ -1,5 +1,35 @@ # @0xsequence/dapp-client +## 3.1.0 + +### Minor Changes + +- 55121af: Add `isSponsored` for explicit sponsorship checks. + + `DappClient.isSponsored(chainId, transactions)` and + `ChainSessionManager.isSponsored(calls)` return `true` only when the relayer's + `/FeeOptions` endpoint explicitly reports sponsorship. Any error, network + failure, or absence of sponsorship returns `false`, so a `true` result is + always safe to surface as "free gas" in UI. + + Prefer this over inferring sponsorship from an empty `getFeeOptions` array — a + swallowed `/FeeOptions` error also produces an empty array, so the inference + can misclassify a failed quote as a real subsidy. The new method uses the + positive `sponsored: boolean` signal from `@0xsequence/relayer`'s widened + `feeOptions` return. + + `getFeeOptions` is unchanged. + +### Patch Changes + +- Fix for relayer sponsored fees +- Updated dependencies +- Updated dependencies [55121af] + - @0xsequence/guard@3.0.11 + - @0xsequence/relayer@3.1.0 + - @0xsequence/wallet-core@3.0.11 + - @0xsequence/wallet-primitives@3.0.11 + ## 3.0.10 ### Patch Changes diff --git a/packages/wallet/dapp-client/package.json b/packages/wallet/dapp-client/package.json index 74b4beeab..6d42b0a89 100644 --- a/packages/wallet/dapp-client/package.json +++ b/packages/wallet/dapp-client/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/dapp-client", - "version": "3.0.10", + "version": "3.1.0", "license": "Apache-2.0", "type": "module", "publishConfig": { diff --git a/packages/wallet/dapp-client/src/ChainSessionManager.ts b/packages/wallet/dapp-client/src/ChainSessionManager.ts index dc1f30e23..d2ebd1a8e 100644 --- a/packages/wallet/dapp-client/src/ChainSessionManager.ts +++ b/packages/wallet/dapp-client/src/ChainSessionManager.ts @@ -878,6 +878,54 @@ export class ChainSessionManager { } } + /** + * Checks whether the given transactions would be sponsored by an active + * relayer policy on this chain. + * + * Returns `true` only when the relayer's `/FeeOptions` endpoint explicitly + * reports sponsorship. A failed quote, network error, or absence of + * sponsorship all return `false`, so a `true` result is always safe to + * surface as "free gas" in UI. + */ + async isSponsored(calls: Transaction[]): Promise { + const callsToSend = calls.map((tx) => ({ + to: tx.to, + value: tx.value, + data: tx.data, + gasLimit: tx.gasLimit ?? BigInt(0), + delegateCall: tx.delegateCall ?? false, + onlyFallback: tx.onlyFallback ?? false, + behaviorOnError: tx.behaviorOnError ?? ('revert' as const), + })) + try { + const signedCall = await this._buildAndSignCalls(callsToSend) + const fingerprint = this._fingerprintCalls(callsToSend) + if (fingerprint) { + this.lastSignedCallCache = { + fingerprint, + signedCall, + createdAtMs: Date.now(), + } + } + const walletAddress = this.walletAddress + if (!walletAddress) throw new InitializationError('Wallet is not initialized.') + const feeOptions = await this.relayer.feeOptions( + walletAddress, + this.chainId, + signedCall.to, + callsToSend, + signedCall.data, + ) + return feeOptions.sponsored === true && !feeOptions.failed + } catch (err) { + console.warn( + `isSponsored check failed for chain ${this.chainId}:`, + err instanceof Error ? err.message : String(err), + ) + return false + } + } + /** * Builds, signs, and sends a batch of transactions. * @param transactions The transactions to be sent. diff --git a/packages/wallet/dapp-client/src/DappClient.ts b/packages/wallet/dapp-client/src/DappClient.ts index e580bd912..cc6bcba4d 100644 --- a/packages/wallet/dapp-client/src/DappClient.ts +++ b/packages/wallet/dapp-client/src/DappClient.ts @@ -806,6 +806,30 @@ export class DappClient { return await chainSessionManager.getFeeOptions(transactions) } + /** + * Checks whether the given transactions would be sponsored on `chainId`. + * + * Returns `true` only when the relayer's `/FeeOptions` endpoint explicitly + * reports sponsorship. A failed quote, network error, or absence of + * sponsorship all return `false`, so a `true` result is always safe to + * surface as "free gas" in UI. + * + * Prefer this over inferring sponsorship from an empty `getFeeOptions` + * array — a swallowed `/FeeOptions` error also produces an empty array. + * + * @example + * if (await dappClient.isSponsored(1, transactions)) { + * // safe to show "Free gas, sponsored by app" + * } else { + * const feeOptions = await dappClient.getFeeOptions(1, transactions) + * // present feeOptions[0..n] to the user as payment choices + * } + */ + async isSponsored(chainId: number, transactions: Transaction[]): Promise { + const chainSessionManager = await this.getOrInitializeChainManager(chainId) + return await chainSessionManager.isSponsored(transactions) + } + /** * Fetches fee tokens for a chain. * @returns A promise that resolves with the fee tokens response. {@link GetFeeTokensResponse} diff --git a/packages/wallet/primitives/CHANGELOG.md b/packages/wallet/primitives/CHANGELOG.md index b2f04d8d5..c3ffa3d19 100644 --- a/packages/wallet/primitives/CHANGELOG.md +++ b/packages/wallet/primitives/CHANGELOG.md @@ -1,5 +1,11 @@ # @0xsequence/wallet-primitives +## 3.0.11 + +### Patch Changes + +- Fix for relayer sponsored fees + ## 3.0.10 ### Patch Changes diff --git a/packages/wallet/primitives/package.json b/packages/wallet/primitives/package.json index a34f382e6..4bd610f9e 100644 --- a/packages/wallet/primitives/package.json +++ b/packages/wallet/primitives/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/wallet-primitives", - "version": "3.0.10", + "version": "3.0.11", "license": "Apache-2.0", "type": "module", "publishConfig": { diff --git a/packages/wallet/wdk/CHANGELOG.md b/packages/wallet/wdk/CHANGELOG.md index 47bc13af2..891397004 100644 --- a/packages/wallet/wdk/CHANGELOG.md +++ b/packages/wallet/wdk/CHANGELOG.md @@ -1,5 +1,39 @@ # @0xsequence/wallet-wdk +## 3.1.0 + +### Minor Changes + +- 55121af: Carry `sponsored` / `failed` through `StandardRelayerOption`. + + `StandardRelayerOption` now exposes two optional fields: + - `sponsored?: boolean` — populated from the relayer SDK's new `feeOptions` + return field. `true` means the server confirmed an active sponsorship policy + match; `false` means it did not (or the quote failed). + - `failed?: boolean` — `true` when the relayer's `feeOptions` call was swallowed + due to a transport or server error. + + Both fields are populated on the empty-options construction branch and the + per-option mapping branch in `transactions.ts`. The new `isStandardRelayerOption` + and `isERC4337RelayerOption` runtime helpers are now re-exported from the + package root for consumers that need to narrow `RelayerOption` before reading + the new fields. + + UI / wallet consumers that previously classified sponsorship by "no `feeOption` + attached" should switch to `sponsored === true` so a real subsidy is no longer + indistinguishable from a swallowed `/FeeOptions` error. + +### Patch Changes + +- Fix for relayer sponsored fees +- Updated dependencies +- Updated dependencies [55121af] + - @0xsequence/guard@3.0.11 + - @0xsequence/identity-instrument@3.0.11 + - @0xsequence/relayer@3.1.0 + - @0xsequence/wallet-core@3.0.11 + - @0xsequence/wallet-primitives@3.0.11 + ## 3.0.10 ### Patch Changes diff --git a/packages/wallet/wdk/package.json b/packages/wallet/wdk/package.json index 65a237c55..12e5d6dd8 100644 --- a/packages/wallet/wdk/package.json +++ b/packages/wallet/wdk/package.json @@ -1,6 +1,6 @@ { "name": "@0xsequence/wallet-wdk", - "version": "3.0.10", + "version": "3.1.0", "license": "Apache-2.0", "type": "module", "publishConfig": { diff --git a/packages/wallet/wdk/src/sequence/transactions.ts b/packages/wallet/wdk/src/sequence/transactions.ts index 146d66999..c5549f28d 100644 --- a/packages/wallet/wdk/src/sequence/transactions.ts +++ b/packages/wallet/wdk/src/sequence/transactions.ts @@ -371,6 +371,8 @@ export class Transactions implements TransactionsInterface { id: uuidv7(), relayerType: relayer.type, relayerId: relayer.id, + sponsored: feeOptions.sponsored, + failed: feeOptions.failed, name, icon, } as StandardRelayerOption, @@ -383,6 +385,8 @@ export class Transactions implements TransactionsInterface { feeOption, relayerType: relayer.type, relayerId: relayer.id, + sponsored: feeOptions.sponsored, + failed: feeOptions.failed, quote: feeOptions.quote, })) }), diff --git a/packages/wallet/wdk/src/sequence/types/index.ts b/packages/wallet/wdk/src/sequence/types/index.ts index 066e8a071..946ee9bf3 100644 --- a/packages/wallet/wdk/src/sequence/types/index.ts +++ b/packages/wallet/wdk/src/sequence/types/index.ts @@ -27,5 +27,6 @@ export type { TransactionRequest, TransactionRequested, } from './transaction-request.js' +export { isERC4337RelayerOption, isStandardRelayerOption } from './transaction-request.js' export type { Wallet } from './wallet.js' export type { Module } from './module.js' diff --git a/packages/wallet/wdk/src/sequence/types/transaction-request.ts b/packages/wallet/wdk/src/sequence/types/transaction-request.ts index 51160a049..5511bc61b 100644 --- a/packages/wallet/wdk/src/sequence/types/transaction-request.ts +++ b/packages/wallet/wdk/src/sequence/types/transaction-request.ts @@ -21,6 +21,8 @@ export type StandardRelayerOption = BaseRelayerOption & { kind: 'standard' feeOption?: Relayer.FeeOption quote?: Relayer.FeeQuote + sponsored?: boolean + failed?: boolean name?: string icon?: string } diff --git a/packages/wallet/wdk/test/transactions.test.ts b/packages/wallet/wdk/test/transactions.test.ts index 295d00d01..8d494347d 100644 --- a/packages/wallet/wdk/test/transactions.test.ts +++ b/packages/wallet/wdk/test/transactions.test.ts @@ -1,5 +1,6 @@ import { afterEach, describe, expect, it } from 'vitest' import { + isStandardRelayerOption, Manager, SignerActionable, Transaction, @@ -56,6 +57,14 @@ describe('Transactions', () => { expect(tx.relayerOptions.length).toBe(1) expect(tx.relayerOptions[0]!.id).toBeDefined() + // PkRelayer/LocalRelayer never report sponsorship, so sponsored should be + // explicitly false (not undefined) and failed should remain undefined. + const firstOption = tx.relayerOptions[0]! + if (isStandardRelayerOption(firstOption)) { + expect(firstOption.sponsored).toBe(false) + expect(firstOption.failed).toBeUndefined() + } + const sigId = await manager.transactions.selectRelayer(txId!, tx.relayerOptions[0]!.id) expect(sigId).toBeDefined() @@ -169,6 +178,14 @@ describe('Transactions', () => { expect(tx.relayerOptions.length).toBe(1) expect(tx.relayerOptions[0]!.id).toBeDefined() + // PkRelayer/LocalRelayer never report sponsorship, so sponsored should be + // explicitly false (not undefined) and failed should remain undefined. + const firstOption = tx.relayerOptions[0]! + if (isStandardRelayerOption(firstOption)) { + expect(firstOption.sponsored).toBe(false) + expect(firstOption.failed).toBeUndefined() + } + const sigId = await manager.transactions.selectRelayer(txId!, tx.relayerOptions[0]!.id) expect(sigId).toBeDefined() diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7a369a88a..406d256af 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -27,8 +27,8 @@ importers: specifier: ^14.3.1 version: 14.3.1 turbo: - specifier: ^2.9.8 - version: 2.9.8 + specifier: ^2.9.14 + version: 2.9.14 typescript: specifier: ^6.0.3 version: 6.0.3 @@ -530,7 +530,7 @@ importers: version: 7.0.1(eslint@9.39.2) eslint-plugin-turbo: specifier: ^2.6.3 - version: 2.6.3(eslint@9.39.2)(turbo@2.9.9) + version: 2.6.3(eslint@9.39.2)(turbo@2.9.14) globals: specifier: ^16.5.0 version: 16.5.0 @@ -1357,23 +1357,13 @@ packages: '@tsconfig/node16@1.0.4': resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} - '@turbo/darwin-64@2.9.8': - resolution: {integrity: sha512-zU1P95ygDpsQ+2QHh7CVTqvYwi9UBlhKWzoIyUnP3vUoge7H9SQEzrd8dj+XcTrslAp9Db3vIBcXtMVoTEYDnA==} + '@turbo/darwin-64@2.9.14': + resolution: {integrity: sha512-t7QiPflaEyBE4oayeZtSmu4mEfjgIrcNlNNl1z1dmIVPqEdtA7+CfTf8d7KXsOGPh6aNgWjKxyvQg9uGfDQF+A==} cpu: [x64] os: [darwin] - '@turbo/darwin-64@2.9.9': - resolution: {integrity: sha512-hTEiNu2ABZZOO1qbjnKASI8eF3BdOOzU6iKv5w5uGOK65DDMc10cS40N1kqM99YT0uSAGUwNu6GdFctRPeEeVA==} - cpu: [x64] - os: [darwin] - - '@turbo/darwin-arm64@2.9.8': - resolution: {integrity: sha512-nKRFI5ZhCGUi4eXNlrojzWcT/CehMj0raot1WE4lw5qf66ZxZHbRbBqcwNEy+ZLY7RkJJRY+TaU89fuj3BcgGg==} - cpu: [arm64] - os: [darwin] - - '@turbo/darwin-arm64@2.9.9': - resolution: {integrity: sha512-MinO40EEcP5mJiTVpfjtEulsEBhVeryfq21QhYtJZ8hQJLHGgy459rcmDVAY8/JERe4dkVU4KW+zoLF22o01EA==} + '@turbo/darwin-arm64@2.9.14': + resolution: {integrity: sha512-d23147mC9BsCPA9mJ0h/ubcpbRgcJBXbcG3+Vq7YLhjz3IXuvQsJ1UXH8f4MD76ZjJ4m/E4aRdJV+MW88CDfbw==} cpu: [arm64] os: [darwin] @@ -1381,43 +1371,23 @@ packages: resolution: {integrity: sha512-PK38N1fHhDUyjLi0mUjv0RbX0xXGwDLQeRSGsIlLcVpP1B5fwodSIwIYXc9vJok26Yne94BX5AGjueYsUT3uUw==} hasBin: true - '@turbo/linux-64@2.9.8': - resolution: {integrity: sha512-Wf/kQpVDCaWM3P5d6lKvJnqjYn/ofUBGbT4h4vRFrdC4N6B/nsun03S2kQNJJMXpXg39woeS4CI367RMU3/OAg==} - cpu: [x64] - os: [linux] - - '@turbo/linux-64@2.9.9': - resolution: {integrity: sha512-7JNLw88Isk+gMlbsC8pulLDkrqe2B827ZsKFEHilb17AC6Xn/62pzH7afjY7fEU6Ayp4XP/vGhlRWOzqBvBvIQ==} + '@turbo/linux-64@2.9.14': + resolution: {integrity: sha512-P3ZKB5tuUDdDQWuAsACGUR1qv9W7BNWxdxqVJ0kZNuNNPRaVYTPPikLcp79+GiEcW3npsR+KyP38lnQiBc5aSA==} cpu: [x64] os: [linux] - '@turbo/linux-arm64@2.9.8': - resolution: {integrity: sha512-v6S3HuKVoa9CEx16IxKj1i/+crxXx22A9O80zW1350zyUlcX0T/zLOxVf1k+ruK/7ssXnDJVg8uSYOxlYRedlA==} + '@turbo/linux-arm64@2.9.14': + resolution: {integrity: sha512-ZRTlzcUMrrPv9ZuDzRF9n60Ym13bKeG9jDB8WjxyLhWNzV+AJQN+zdpIk3NJYf2zQsGUm1mNar2P0elRzLw25g==} cpu: [arm64] os: [linux] - '@turbo/linux-arm64@2.9.9': - resolution: {integrity: sha512-0pnXDwPw1rHii98JZPRg7SvsjIzy7jrhkwGU9Jy5fVYoMdYd3P2vbtLfII+OJ0Mm4Ar5yykdHDTz3RWiRI1o9g==} - cpu: [arm64] - os: [linux] - - '@turbo/windows-64@2.9.8': - resolution: {integrity: sha512-JaefWOJNBazDylAn3f+lLB34XMNu8nEBbgPRP/Ewysg81cBubGfcyyyzpQOGVuMwfaqdNAE/kitG7w3AbJn9/g==} - cpu: [x64] - os: [win32] - - '@turbo/windows-64@2.9.9': - resolution: {integrity: sha512-vjDQycz4gQVvIq4n2rPtiiIESwJlAc406qtkiZlqyL+fHZEd9SxYNlBIFYtc5cuMuwrk+sIKrhN7XvwjmvS9YQ==} + '@turbo/windows-64@2.9.14': + resolution: {integrity: sha512-exanwN6sIduZwykYeiTQj8kCmOhazP5WOz3bvXMcYtjhL6Z3iRWLewKrXCBq0bqwSP3iBMb/AerRCnHI4lx46A==} cpu: [x64] os: [win32] - '@turbo/windows-arm64@2.9.8': - resolution: {integrity: sha512-Or6ljjB4TiiwCdVKDYWew0SokQ9kep5zruL8P3nbum9WdkH5XA41rQID4Ulc215Z+R3DrB+qXSHPsJjU3/n2ng==} - cpu: [arm64] - os: [win32] - - '@turbo/windows-arm64@2.9.9': - resolution: {integrity: sha512-V6NiH43oCctepbOdQFp7UjqLyK8p6Tt824QA+G4TE+B1BBHu80A0W8OCL+H7uBJ3XZjAj/hvPDw3k3l65DoDGw==} + '@turbo/windows-arm64@2.9.14': + resolution: {integrity: sha512-fVdCsnmYoKICsycbWuuGp6Jvi51/3G/UluFWuAUCvR8PIW5IJkAk5BM9UF8PSm0Q2IphWHFZjYEgjHsh3B9y/g==} cpu: [arm64] os: [win32] @@ -3611,12 +3581,8 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - turbo@2.9.8: - resolution: {integrity: sha512-REEB2rVTVDTf4hav1gJ5dIsGylWZrNonvjXFtk1dCi8gND3PhZtnYkyry1bra/Fo+iP6ctTEZbg6vWfdfHq/1A==} - hasBin: true - - turbo@2.9.9: - resolution: {integrity: sha512-3xfzXE/yTjhh0S5dIWlE+3E+J9A09REpLI1ZqVh2+HrNZoVzZn0pkvjiRgVK/Ev3PF9XnaTwCntTx+CADWXcyA==} + turbo@2.9.14: + resolution: {integrity: sha512-BQqXRr4UoWI3UPFrtznCLykYHxwxWh53iCB57x092jPMjIlW1wnm3N895g5irpiXmnxUhREBB0n6+y8BHhs4nw==} hasBin: true type-check@0.4.0: @@ -4607,16 +4573,10 @@ snapshots: '@tsconfig/node16@1.0.4': {} - '@turbo/darwin-64@2.9.8': - optional: true - - '@turbo/darwin-64@2.9.9': - optional: true - - '@turbo/darwin-arm64@2.9.8': + '@turbo/darwin-64@2.9.14': optional: true - '@turbo/darwin-arm64@2.9.9': + '@turbo/darwin-arm64@2.9.14': optional: true '@turbo/gen@1.13.4(@types/node@25.3.0)(typescript@6.0.3)': @@ -4639,28 +4599,16 @@ snapshots: - supports-color - typescript - '@turbo/linux-64@2.9.8': - optional: true - - '@turbo/linux-64@2.9.9': - optional: true - - '@turbo/linux-arm64@2.9.8': - optional: true - - '@turbo/linux-arm64@2.9.9': - optional: true - - '@turbo/windows-64@2.9.8': + '@turbo/linux-64@2.9.14': optional: true - '@turbo/windows-64@2.9.9': + '@turbo/linux-arm64@2.9.14': optional: true - '@turbo/windows-arm64@2.9.8': + '@turbo/windows-64@2.9.14': optional: true - '@turbo/windows-arm64@2.9.9': + '@turbo/windows-arm64@2.9.14': optional: true '@turbo/workspaces@1.13.4(@types/node@25.3.0)': @@ -5530,11 +5478,11 @@ snapshots: string.prototype.matchall: 4.0.12 string.prototype.repeat: 1.0.0 - eslint-plugin-turbo@2.6.3(eslint@9.39.2)(turbo@2.9.9): + eslint-plugin-turbo@2.6.3(eslint@9.39.2)(turbo@2.9.14): dependencies: dotenv: 16.0.3 eslint: 9.39.2 - turbo: 2.9.9 + turbo: 2.9.14 eslint-scope@8.4.0: dependencies: @@ -7206,23 +7154,14 @@ snapshots: tslib@2.8.1: {} - turbo@2.9.8: - optionalDependencies: - '@turbo/darwin-64': 2.9.8 - '@turbo/darwin-arm64': 2.9.8 - '@turbo/linux-64': 2.9.8 - '@turbo/linux-arm64': 2.9.8 - '@turbo/windows-64': 2.9.8 - '@turbo/windows-arm64': 2.9.8 - - turbo@2.9.9: + turbo@2.9.14: optionalDependencies: - '@turbo/darwin-64': 2.9.9 - '@turbo/darwin-arm64': 2.9.9 - '@turbo/linux-64': 2.9.9 - '@turbo/linux-arm64': 2.9.9 - '@turbo/windows-64': 2.9.9 - '@turbo/windows-arm64': 2.9.9 + '@turbo/darwin-64': 2.9.14 + '@turbo/darwin-arm64': 2.9.14 + '@turbo/linux-64': 2.9.14 + '@turbo/linux-arm64': 2.9.14 + '@turbo/windows-64': 2.9.14 + '@turbo/windows-arm64': 2.9.14 type-check@0.4.0: dependencies: