Skip to content

Commit 31ac364

Browse files
A1igatorclaude
andcommitted
authCapture: drop chain-based assetTransferMethod auto-injection
The server's defaultMoneyConversion was inspecting a per-chain permit2Only flag and injecting assetTransferMethod: 'permit2' for BSC and Tempo. That's wrong framing — whether a token implements receiveWithAuthorization is a token-level capability, not a chain property. The current behavior happened to be correct for the canonical default stables on those chains, but it pre-locked merchants out of making their own choice if they used a different token. Now: defaultMoneyConversion only emits the EIP-712 domain (name, version) for the canonical stable. assetTransferMethod is always the merchant's call. If a merchant pairs eip3009 with a token that lacks receiveWithAuthorization, the failure surfaces as simulation_failed at the facilitator (consistent with how upstream specs handle the analogous case). Tests updated: dropped the two "should inject permit2 on BSC/Tempo" cases; replaced the "should NOT inject on ERC-3009 chains" with a generic "should never inject" that walks all three chain categories. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
1 parent 449971a commit 31ac364

2 files changed

Lines changed: 27 additions & 46 deletions

File tree

packages/evm/src/authCapture/server/scheme.ts

Lines changed: 18 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,13 @@ import type {
2020
* default stablecoin used by `defaultMoneyConversion` when the merchant gives a
2121
* decimal price like "$1.50".
2222
*
23-
* The `name` / `version` fields are the EIP-712 domain used by the ERC-3009
24-
* `assetTransferMethod`. Chains where the canonical stable does NOT support
25-
* ERC-3009 (BSC's Binance-Peg USDC, Tempo's pathUSD) are flagged `permit2Only`;
26-
* `defaultMoneyConversion` then injects `assetTransferMethod: "permit2"` into
27-
* the returned `extra` so the client signs a Permit2 PermitTransferFrom and
28-
* the facilitator dispatches to the Permit2 collector. Permit2 has its own
29-
* EIP-712 domain (chain-invariant), so the per-token name/version are
30-
* irrelevant on the Permit2 path.
23+
* `name` / `version` are the EIP-712 domain used by the ERC-3009
24+
* `assetTransferMethod`. Whether a token supports ERC-3009 is a token-level
25+
* capability, not a chain property; merchants whose chosen token lacks
26+
* `receiveWithAuthorization` (e.g., BSC's Binance-Peg USDC, Tempo's pathUSD)
27+
* MUST set `assetTransferMethod: "permit2"` in `extra` themselves. The server
28+
* does not auto-pick a method based on chain. If the wrong method is paired
29+
* with an incompatible token, the failure surfaces at facilitator simulation.
3130
*/
3231
const ASSET_INFO: Record<
3332
string,
@@ -36,8 +35,6 @@ const ASSET_INFO: Record<
3635
name: string
3736
version: string
3837
decimals: number
39-
/** If true, the canonical stable on this chain does not support ERC-3009; merchants must use Permit2. */
40-
permit2Only?: boolean
4138
}
4239
> = {
4340
// ----- Mainnets -----
@@ -128,22 +125,21 @@ const ASSET_INFO: Record<
128125
decimals: 6,
129126
},
130127

131-
// ----- Permit2-only mainnets -----
132-
// BNB Smart Chain — Binance-Peg USDC (18 decimals; lacks ERC-3009).
128+
// ----- Mainnets where the canonical stable lacks ERC-3009 -----
129+
// Merchants on these chains MUST set `assetTransferMethod: "permit2"` themselves.
130+
// BNB Smart Chain — Binance-Peg USDC (18 decimals; no `receiveWithAuthorization`).
133131
'eip155:56': {
134132
address: '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d',
135133
name: 'USDC',
136134
version: '1',
137135
decimals: 18,
138-
permit2Only: true,
139136
},
140-
// Tempo — pathUSD (TIP-20 predeploy, 6 decimals; not USDC, lacks ERC-3009).
137+
// Tempo — pathUSD (TIP-20 predeploy, 6 decimals; no `receiveWithAuthorization`).
141138
'eip155:4217': {
142139
address: '0x20c0000000000000000000000000000000000000',
143140
name: 'pathUSD',
144141
version: '1',
145142
decimals: 6,
146-
permit2Only: true,
147143
},
148144
}
149145

@@ -239,10 +235,9 @@ export class AuthCaptureServerScheme implements SchemeNetworkServer {
239235

240236
/**
241237
* Default money conversion — converts decimal amount to the default stablecoin on the network.
242-
*
243-
* For `permit2Only` chains, injects `assetTransferMethod: "permit2"` so the
244-
* client signs Permit2 instead of ERC-3009 (which the canonical stable on
245-
* those chains doesn't support).
238+
* Returns just the EIP-712 domain (`name` / `version`) in `extra`. The merchant
239+
* is responsible for setting `assetTransferMethod` if the chosen token doesn't
240+
* support the spec default (`"eip3009"`).
246241
*/
247242
private defaultMoneyConversion(amount: number, network: Network): AssetAmount {
248243
const assetInfo = ASSET_INFO[network]
@@ -252,18 +247,13 @@ export class AuthCaptureServerScheme implements SchemeNetworkServer {
252247

253248
const tokenAmount = convertToTokenAmount(String(amount), assetInfo.decimals)
254249

255-
const extra: Record<string, unknown> = {
256-
name: assetInfo.name,
257-
version: assetInfo.version,
258-
}
259-
if (assetInfo.permit2Only) {
260-
extra.assetTransferMethod = 'permit2'
261-
}
262-
263250
return {
264251
asset: assetInfo.address,
265252
amount: tokenAmount,
266-
extra,
253+
extra: {
254+
name: assetInfo.name,
255+
version: assetInfo.version,
256+
},
267257
}
268258
}
269259

packages/evm/test/unit/authCapture/server.test.ts

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -104,40 +104,31 @@ describe('AuthCaptureServerScheme', () => {
104104
)
105105
})
106106

107-
it('should inject assetTransferMethod: "permit2" on BSC (permit2Only chain)', async () => {
107+
it('should resolve BSC default to Binance-Peg USDC without setting assetTransferMethod', async () => {
108108
const scheme = new AuthCaptureServerScheme()
109109
const result = await scheme.parsePrice('$1.00', 'eip155:56')
110110

111111
expect(result.asset).toBe('0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d')
112112
// BSC's Binance-Peg USDC has 18 decimals, so $1.00 = 1e18 base units.
113113
expect(result.amount).toBe('1000000000000000000')
114-
expect(result.extra).toEqual({
115-
name: 'USDC',
116-
version: '1',
117-
assetTransferMethod: 'permit2',
118-
})
114+
expect(result.extra).toEqual({ name: 'USDC', version: '1' })
119115
})
120116

121-
it('should inject assetTransferMethod: "permit2" on Tempo (permit2Only chain)', async () => {
117+
it('should resolve Tempo default to pathUSD without setting assetTransferMethod', async () => {
122118
const scheme = new AuthCaptureServerScheme()
123119
const result = await scheme.parsePrice('$1.00', 'eip155:4217')
124120

125121
expect(result.asset).toBe('0x20c0000000000000000000000000000000000000')
126122
expect(result.amount).toBe('1000000')
127-
expect(result.extra).toEqual({
128-
name: 'pathUSD',
129-
version: '1',
130-
assetTransferMethod: 'permit2',
131-
})
123+
expect(result.extra).toEqual({ name: 'pathUSD', version: '1' })
132124
})
133125

134-
it('should NOT inject assetTransferMethod on ERC-3009 chains', async () => {
126+
it('should never inject assetTransferMethod (merchant decides)', async () => {
135127
const scheme = new AuthCaptureServerScheme()
136-
const result = await scheme.parsePrice('$1.00', 'eip155:8453')
137-
138-
// ERC-3009 chains rely on the default ('eip3009') in client/facilitator;
139-
// the server intentionally omits the field so merchants can override.
140-
expect(result.extra).not.toHaveProperty('assetTransferMethod')
128+
for (const network of ['eip155:8453', 'eip155:56', 'eip155:4217'] as const) {
129+
const result = await scheme.parsePrice('$1.00', network)
130+
expect(result.extra).not.toHaveProperty('assetTransferMethod')
131+
}
141132
})
142133
})
143134

0 commit comments

Comments
 (0)