mppx 0.7.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +41 -0
- package/README.md +20 -11
- package/dist/Challenge.d.ts.map +1 -1
- package/dist/Challenge.js +18 -6
- package/dist/Challenge.js.map +1 -1
- package/dist/Mcp.d.ts +3 -0
- package/dist/Mcp.d.ts.map +1 -1
- package/dist/Mcp.js +2 -0
- package/dist/Mcp.js.map +1 -1
- package/dist/PaymentRequest.d.ts +10 -10
- package/dist/PaymentRequest.js +8 -8
- package/dist/cli/internal.d.ts +1 -0
- package/dist/cli/internal.d.ts.map +1 -1
- package/dist/cli/internal.js +1 -15
- package/dist/cli/internal.js.map +1 -1
- package/dist/client/Mppx.js +2 -2
- package/dist/client/Mppx.js.map +1 -1
- package/dist/client/Transport.d.ts +11 -16
- package/dist/client/Transport.d.ts.map +1 -1
- package/dist/client/Transport.js +55 -75
- package/dist/client/Transport.js.map +1 -1
- package/dist/client/index.d.ts +3 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/internal/Fetch.d.ts.map +1 -1
- package/dist/client/internal/Fetch.js +46 -7
- package/dist/client/internal/Fetch.js.map +1 -1
- package/dist/client/internal/protocols/Mcp.d.ts +7 -0
- package/dist/client/internal/protocols/Mcp.d.ts.map +1 -0
- package/dist/client/internal/protocols/Mcp.js +159 -0
- package/dist/client/internal/protocols/Mcp.js.map +1 -0
- package/dist/client/internal/protocols/Mpp.d.ts +4 -0
- package/dist/client/internal/protocols/Mpp.d.ts.map +1 -0
- package/dist/client/internal/protocols/Mpp.js +18 -0
- package/dist/client/internal/protocols/Mpp.js.map +1 -0
- package/dist/client/internal/protocols/Protocol.d.ts +10 -0
- package/dist/client/internal/protocols/Protocol.d.ts.map +1 -0
- package/dist/client/internal/protocols/Protocol.js +2 -0
- package/dist/client/internal/protocols/Protocol.js.map +1 -0
- package/dist/client/internal/protocols/Shared.d.ts +5 -0
- package/dist/client/internal/protocols/Shared.d.ts.map +1 -0
- package/dist/client/internal/protocols/Shared.js +20 -0
- package/dist/client/internal/protocols/Shared.js.map +1 -0
- package/dist/client/internal/protocols/X402.d.ts +8 -0
- package/dist/client/internal/protocols/X402.d.ts.map +1 -0
- package/dist/client/internal/protocols/X402.js +39 -0
- package/dist/client/internal/protocols/X402.js.map +1 -0
- package/dist/evm/client/index.d.ts +1 -0
- package/dist/evm/client/index.d.ts.map +1 -1
- package/dist/evm/client/index.js +1 -0
- package/dist/evm/client/index.js.map +1 -1
- package/dist/evm/index.d.ts +2 -0
- package/dist/evm/index.d.ts.map +1 -1
- package/dist/evm/index.js +2 -0
- package/dist/evm/index.js.map +1 -1
- package/dist/evm/server/index.d.ts +1 -0
- package/dist/evm/server/index.d.ts.map +1 -1
- package/dist/evm/server/index.js +1 -0
- package/dist/evm/server/index.js.map +1 -1
- package/dist/mcp/client/McpClient.d.ts +101 -0
- package/dist/mcp/client/McpClient.d.ts.map +1 -0
- package/dist/mcp/client/McpClient.js +162 -0
- package/dist/mcp/client/McpClient.js.map +1 -0
- package/dist/mcp/client/index.d.ts.map +1 -0
- package/dist/mcp/client/index.js.map +1 -0
- package/dist/mcp/server/Transport.d.ts.map +1 -0
- package/dist/mcp/server/Transport.js.map +1 -0
- package/dist/mcp/server/index.d.ts.map +1 -0
- package/dist/mcp/server/index.js.map +1 -0
- package/dist/server/Mppx.d.ts +1 -1
- package/dist/server/Mppx.d.ts.map +1 -1
- package/dist/server/Mppx.js +9 -0
- package/dist/server/Mppx.js.map +1 -1
- package/dist/server/Transport.d.ts +1 -1
- package/dist/server/Transport.d.ts.map +1 -1
- package/dist/server/Transport.js +1 -1
- package/dist/server/Transport.js.map +1 -1
- package/dist/stripe/server/internal/html.gen.d.ts +1 -1
- package/dist/stripe/server/internal/html.gen.d.ts.map +1 -1
- package/dist/stripe/server/internal/html.gen.js +1 -1
- package/dist/stripe/server/internal/html.gen.js.map +1 -1
- package/dist/tempo/Proof.d.ts +85 -1
- package/dist/tempo/Proof.d.ts.map +1 -1
- package/dist/tempo/Proof.js +35 -0
- package/dist/tempo/Proof.js.map +1 -1
- package/dist/tempo/client/Charge.d.ts +13 -1
- package/dist/tempo/client/Charge.d.ts.map +1 -1
- package/dist/tempo/client/Charge.js +38 -25
- package/dist/tempo/client/Charge.js.map +1 -1
- package/dist/tempo/client/Methods.d.ts +5 -3
- package/dist/tempo/client/Methods.d.ts.map +1 -1
- package/dist/tempo/client/Methods.js +4 -2
- package/dist/tempo/client/Methods.js.map +1 -1
- package/dist/tempo/client/ResolveAccount.d.ts +40 -0
- package/dist/tempo/client/ResolveAccount.d.ts.map +1 -0
- package/dist/tempo/client/ResolveAccount.js +2 -0
- package/dist/tempo/client/ResolveAccount.js.map +1 -0
- package/dist/tempo/internal/fee-payer.d.ts +26 -1
- package/dist/tempo/internal/fee-payer.d.ts.map +1 -1
- package/dist/tempo/internal/fee-payer.js +83 -30
- package/dist/tempo/internal/fee-payer.js.map +1 -1
- package/dist/tempo/internal/proof.d.ts +71 -5
- package/dist/tempo/internal/proof.d.ts.map +1 -1
- package/dist/tempo/internal/proof.js +42 -6
- package/dist/tempo/internal/proof.js.map +1 -1
- package/dist/tempo/legacy/client/SessionManager.d.ts.map +1 -1
- package/dist/tempo/legacy/client/SessionManager.js +10 -3
- package/dist/tempo/legacy/client/SessionManager.js.map +1 -1
- package/dist/tempo/server/Charge.d.ts.map +1 -1
- package/dist/tempo/server/Charge.js +46 -18
- package/dist/tempo/server/Charge.js.map +1 -1
- package/dist/tempo/server/Methods.d.ts +4 -2
- package/dist/tempo/server/Methods.d.ts.map +1 -1
- package/dist/tempo/server/Methods.js +4 -2
- package/dist/tempo/server/Methods.js.map +1 -1
- package/dist/tempo/server/Subscription.d.ts +10 -0
- package/dist/tempo/server/Subscription.d.ts.map +1 -1
- package/dist/tempo/server/Subscription.js +135 -23
- package/dist/tempo/server/Subscription.js.map +1 -1
- package/dist/tempo/server/internal/html.gen.d.ts +1 -1
- package/dist/tempo/server/internal/html.gen.d.ts.map +1 -1
- package/dist/tempo/server/internal/html.gen.js +1 -1
- package/dist/tempo/server/internal/html.gen.js.map +1 -1
- package/dist/tempo/session/client/ChannelOps.d.ts +2 -3
- package/dist/tempo/session/client/ChannelOps.d.ts.map +1 -1
- package/dist/tempo/session/client/ChannelOps.js +7 -10
- package/dist/tempo/session/client/ChannelOps.js.map +1 -1
- package/dist/tempo/session/client/ChannelStore.d.ts +51 -0
- package/dist/tempo/session/client/ChannelStore.d.ts.map +1 -0
- package/dist/tempo/session/client/ChannelStore.js +63 -0
- package/dist/tempo/session/client/ChannelStore.js.map +1 -0
- package/dist/tempo/session/client/CredentialState.d.ts +7 -24
- package/dist/tempo/session/client/CredentialState.d.ts.map +1 -1
- package/dist/tempo/session/client/CredentialState.js +51 -49
- package/dist/tempo/session/client/CredentialState.js.map +1 -1
- package/dist/tempo/session/client/Session.d.ts +8 -2
- package/dist/tempo/session/client/Session.d.ts.map +1 -1
- package/dist/tempo/session/client/Session.js +22 -8
- package/dist/tempo/session/client/Session.js.map +1 -1
- package/dist/tempo/session/client/SessionManager.d.ts +4 -40
- package/dist/tempo/session/client/SessionManager.d.ts.map +1 -1
- package/dist/tempo/session/client/SessionManager.js +124 -174
- package/dist/tempo/session/client/SessionManager.js.map +1 -1
- package/dist/tempo/session/client/index.d.ts +3 -4
- package/dist/tempo/session/client/index.d.ts.map +1 -1
- package/dist/tempo/session/client/index.js +1 -0
- package/dist/tempo/session/client/index.js.map +1 -1
- package/dist/tempo/session/precompile/Voucher.d.ts +3 -3
- package/dist/tempo/session/precompile/Voucher.d.ts.map +1 -1
- package/dist/tempo/session/precompile/Voucher.js +24 -25
- package/dist/tempo/session/precompile/Voucher.js.map +1 -1
- package/dist/tempo/session/server/CredentialVerification.d.ts +6 -0
- package/dist/tempo/session/server/CredentialVerification.d.ts.map +1 -1
- package/dist/tempo/session/server/CredentialVerification.js +13 -0
- package/dist/tempo/session/server/CredentialVerification.js.map +1 -1
- package/dist/tempo/session/server/Settlement.d.ts.map +1 -1
- package/dist/tempo/session/server/Settlement.js +4 -2
- package/dist/tempo/session/server/Settlement.js.map +1 -1
- package/dist/tempo/session/server/Sse.d.ts.map +1 -1
- package/dist/tempo/session/server/Sse.js.map +1 -1
- package/dist/tempo/session/server/Ws.d.ts.map +1 -1
- package/dist/tempo/session/server/Ws.js.map +1 -1
- package/dist/tempo/subscription/KeyAuthorization.d.ts +712 -1
- package/dist/tempo/subscription/KeyAuthorization.d.ts.map +1 -1
- package/dist/tempo/subscription/Store.d.ts +2 -0
- package/dist/tempo/subscription/Store.d.ts.map +1 -1
- package/dist/tempo/subscription/Store.js +16 -1
- package/dist/tempo/subscription/Store.js.map +1 -1
- package/dist/x402/index.d.ts +1 -0
- package/dist/x402/index.d.ts.map +1 -1
- package/dist/x402/index.js +1 -0
- package/dist/x402/index.js.map +1 -1
- package/package.json +21 -10
- package/src/Challenge.test.ts +40 -0
- package/src/Challenge.ts +19 -6
- package/src/Mcp.ts +4 -0
- package/src/PaymentRequest.ts +10 -10
- package/src/cli/cli.test.ts +15 -15
- package/src/cli/config.test.ts +13 -7
- package/src/cli/internal.ts +1 -16
- package/src/client/Mppx.test-d.ts +21 -1
- package/src/client/Mppx.test.ts +1 -1
- package/src/client/Mppx.ts +2 -2
- package/src/client/Transport.test.ts +225 -178
- package/src/client/Transport.ts +77 -83
- package/src/client/index.ts +14 -0
- package/src/client/internal/Fetch.test.ts +207 -2
- package/src/client/internal/Fetch.ts +52 -6
- package/src/client/internal/protocols/Mcp.test.ts +220 -0
- package/src/client/internal/protocols/Mcp.ts +162 -0
- package/src/client/internal/protocols/Mpp.ts +21 -0
- package/src/client/internal/protocols/Protocol.ts +10 -0
- package/src/client/internal/protocols/Shared.ts +25 -0
- package/src/client/internal/protocols/X402.ts +42 -0
- package/src/discovery/OpenApi.test.ts +1 -1
- package/src/evm/PublicInterface.test-d.ts +1 -1
- package/src/evm/client/index.ts +1 -0
- package/src/evm/index.ts +2 -0
- package/src/evm/server/Charge.test.ts +1 -1
- package/src/evm/server/index.ts +1 -0
- package/src/{mcp-sdk → mcp}/client/McpClient.integration.test.ts +10 -4
- package/src/{mcp-sdk → mcp}/client/McpClient.test-d.ts +45 -18
- package/src/{mcp-sdk → mcp}/client/McpClient.test.ts +211 -5
- package/src/mcp/client/McpClient.ts +307 -0
- package/src/{mcp-sdk → mcp}/client/McpClient.unit.test.ts +9 -5
- package/src/middlewares/elysia.test.ts +1 -1
- package/src/middlewares/express.test.ts +1 -1
- package/src/middlewares/hono.test.ts +1 -1
- package/src/middlewares/internal/mppx.test.ts +1 -1
- package/src/middlewares/nextjs.test.ts +1 -1
- package/src/proxy/Proxy.test.ts +1 -1
- package/src/proxy/services/anthropic.test.ts +1 -1
- package/src/proxy/services/openai.test.ts +1 -1
- package/src/proxy/services/stripe.test.ts +1 -1
- package/src/server/Mppx.authorize.test.ts +1 -1
- package/src/server/Mppx.test-d.ts +1 -1
- package/src/server/Mppx.test.ts +20 -2
- package/src/server/Mppx.ts +14 -1
- package/src/server/Transport.test.ts +6 -6
- package/src/server/Transport.ts +1 -1
- package/src/stripe/Charge.integration.test.ts +1 -1
- package/src/stripe/client/Charge.test.ts +1 -1
- package/src/stripe/server/Charge.test.ts +1 -1
- package/src/stripe/server/internal/html/package.json +1 -1
- package/src/stripe/server/internal/html.gen.ts +1 -1
- package/src/tempo/Proof.conformance.test.ts +146 -0
- package/src/tempo/Proof.test-d.ts +15 -0
- package/src/tempo/Proof.ts +52 -1
- package/src/tempo/Subscription.integration.test.ts +1 -1
- package/src/tempo/client/Charge.test.ts +173 -0
- package/src/tempo/client/Charge.ts +65 -36
- package/src/tempo/client/Methods.ts +4 -2
- package/src/tempo/client/ResolveAccount.ts +46 -0
- package/src/tempo/internal/fee-payer.test.ts +89 -10
- package/src/tempo/internal/fee-payer.ts +128 -41
- package/src/tempo/internal/proof.test.ts +12 -4
- package/src/tempo/internal/proof.ts +55 -6
- package/src/tempo/legacy/client/SessionManager.ts +11 -3
- package/src/tempo/legacy/server/Session.test.ts +91 -26
- package/src/tempo/server/Charge.test.ts +388 -17
- package/src/tempo/server/Charge.ts +50 -24
- package/src/tempo/server/Methods.ts +4 -2
- package/src/tempo/server/Subscription.test.ts +465 -3
- package/src/tempo/server/Subscription.ts +174 -19
- package/src/tempo/server/internal/html/package.json +2 -2
- package/src/tempo/server/internal/html.gen.ts +1 -1
- package/src/tempo/session/client/ChannelOps.ts +5 -19
- package/src/tempo/session/client/ChannelStore.ts +111 -0
- package/src/tempo/session/client/CredentialState.test.ts +206 -62
- package/src/tempo/session/client/CredentialState.ts +58 -73
- package/src/tempo/session/client/Session.test.ts +41 -1
- package/src/tempo/session/client/Session.ts +36 -10
- package/src/tempo/session/client/SessionManager.test.ts +154 -65
- package/src/tempo/session/client/SessionManager.ts +141 -235
- package/src/tempo/session/client/index.ts +8 -5
- package/src/tempo/session/precompile/Voucher.test.ts +45 -7
- package/src/tempo/session/precompile/Voucher.ts +27 -25
- package/src/tempo/session/server/CredentialVerification.test.ts +36 -0
- package/src/tempo/session/server/CredentialVerification.ts +18 -0
- package/src/tempo/session/server/Session.test.ts +4 -4
- package/src/tempo/session/server/Settlement.test.ts +88 -1
- package/src/tempo/session/server/Settlement.ts +2 -1
- package/src/tempo/session/server/Sse.ts +0 -2
- package/src/tempo/session/server/Ws.ts +0 -4
- package/src/tempo/subscription/Store.ts +27 -9
- package/src/x402/Exact.e2e.test.ts +1 -1
- package/src/x402/PublicInterface.test-d.ts +1 -1
- package/src/x402/index.ts +1 -0
- package/dist/mcp-sdk/client/McpClient.d.ts +0 -85
- package/dist/mcp-sdk/client/McpClient.d.ts.map +0 -1
- package/dist/mcp-sdk/client/McpClient.js +0 -118
- package/dist/mcp-sdk/client/McpClient.js.map +0 -1
- package/dist/mcp-sdk/client/index.d.ts.map +0 -1
- package/dist/mcp-sdk/client/index.js.map +0 -1
- package/dist/mcp-sdk/server/Transport.d.ts.map +0 -1
- package/dist/mcp-sdk/server/Transport.js.map +0 -1
- package/dist/mcp-sdk/server/index.d.ts.map +0 -1
- package/dist/mcp-sdk/server/index.js.map +0 -1
- package/src/mcp-sdk/client/McpClient.ts +0 -228
- /package/dist/{mcp-sdk → mcp}/client/index.d.ts +0 -0
- /package/dist/{mcp-sdk → mcp}/client/index.js +0 -0
- /package/dist/{mcp-sdk → mcp}/server/Transport.d.ts +0 -0
- /package/dist/{mcp-sdk → mcp}/server/Transport.js +0 -0
- /package/dist/{mcp-sdk → mcp}/server/index.d.ts +0 -0
- /package/dist/{mcp-sdk → mcp}/server/index.js +0 -0
- /package/src/{mcp-sdk → mcp}/client/index.ts +0 -0
- /package/src/{mcp-sdk → mcp}/server/Transport.test.ts +0 -0
- /package/src/{mcp-sdk → mcp}/server/Transport.ts +0 -0
- /package/src/{mcp-sdk → mcp}/server/index.ts +0 -0
|
@@ -29,7 +29,7 @@ import * as defaults from '../internal/defaults.js'
|
|
|
29
29
|
import * as Proof from '../internal/proof.js'
|
|
30
30
|
|
|
31
31
|
const realm = 'api.example.com'
|
|
32
|
-
const secretKey = 'test-secret-key'
|
|
32
|
+
const secretKey = 'test-secret-key-test-secret-key-32'
|
|
33
33
|
|
|
34
34
|
function isPairAlreadyExistsError(error: unknown) {
|
|
35
35
|
if (typeof error !== 'object' || error === null) return false
|
|
@@ -1604,6 +1604,228 @@ describe('tempo', () => {
|
|
|
1604
1604
|
httpServer.close()
|
|
1605
1605
|
})
|
|
1606
1606
|
|
|
1607
|
+
test('behavior: fee payer pre-broadcast simulation targets the co-signed transaction', async () => {
|
|
1608
|
+
// The pre-broadcast simulation must reflect the FINAL co-signed envelope
|
|
1609
|
+
// (concrete sponsor fee payer), not the pre-cosign 0x78 (`feePayer: true`).
|
|
1610
|
+
const callRequests: any[] = []
|
|
1611
|
+
const interceptingClient = createClient({
|
|
1612
|
+
account: accounts[0],
|
|
1613
|
+
chain: client.chain,
|
|
1614
|
+
transport: custom({
|
|
1615
|
+
async request(args: any) {
|
|
1616
|
+
if (args.method === 'eth_call') callRequests.push(args.params?.[0])
|
|
1617
|
+
return client.transport.request(args)
|
|
1618
|
+
},
|
|
1619
|
+
}),
|
|
1620
|
+
})
|
|
1621
|
+
|
|
1622
|
+
const serverWithTrace = Mppx_server.create({
|
|
1623
|
+
methods: [
|
|
1624
|
+
tempo_server.charge({
|
|
1625
|
+
getClient() {
|
|
1626
|
+
return interceptingClient
|
|
1627
|
+
},
|
|
1628
|
+
currency: asset,
|
|
1629
|
+
account: accounts[0],
|
|
1630
|
+
}),
|
|
1631
|
+
],
|
|
1632
|
+
realm,
|
|
1633
|
+
secretKey,
|
|
1634
|
+
})
|
|
1635
|
+
|
|
1636
|
+
const mppx = Mppx_client.create({
|
|
1637
|
+
polyfill: false,
|
|
1638
|
+
methods: [
|
|
1639
|
+
tempo_client({
|
|
1640
|
+
account: accounts[1],
|
|
1641
|
+
getClient() {
|
|
1642
|
+
return client
|
|
1643
|
+
},
|
|
1644
|
+
}),
|
|
1645
|
+
],
|
|
1646
|
+
})
|
|
1647
|
+
|
|
1648
|
+
const httpServer = await Http.createServer(async (req, res) => {
|
|
1649
|
+
const result = await Mppx_server.toNodeListener(
|
|
1650
|
+
serverWithTrace.charge({
|
|
1651
|
+
feePayer: accounts[0],
|
|
1652
|
+
amount: '1',
|
|
1653
|
+
currency: asset,
|
|
1654
|
+
recipient: accounts[0].address,
|
|
1655
|
+
}),
|
|
1656
|
+
)(req, res)
|
|
1657
|
+
if (result.status === 402) return
|
|
1658
|
+
res.end('OK')
|
|
1659
|
+
})
|
|
1660
|
+
|
|
1661
|
+
const challengeResponse = await fetch(httpServer.url)
|
|
1662
|
+
const credential = await mppx.createCredential(challengeResponse)
|
|
1663
|
+
callRequests.length = 0
|
|
1664
|
+
|
|
1665
|
+
const authResponse = await fetch(httpServer.url, {
|
|
1666
|
+
headers: { Authorization: credential },
|
|
1667
|
+
})
|
|
1668
|
+
expect(authResponse.status).toBe(200)
|
|
1669
|
+
|
|
1670
|
+
expect(callRequests.length).toBeGreaterThan(0)
|
|
1671
|
+
const simRequest = callRequests[0]
|
|
1672
|
+
// The co-signed envelope names a concrete sponsor as fee payer. The
|
|
1673
|
+
// pre-cosign 0x78 instead carries `feePayer: true`; asserting the address
|
|
1674
|
+
// proves we simulate the transaction the sponsor actually broadcasts.
|
|
1675
|
+
expect(simRequest.feePayer).not.toBe(true)
|
|
1676
|
+
expect(typeof simRequest.feePayer).toBe('string')
|
|
1677
|
+
expect((simRequest.feePayer as string).toLowerCase()).toBe(accounts[0].address.toLowerCase())
|
|
1678
|
+
// Execution runs as the sender, not the sponsor.
|
|
1679
|
+
expect((simRequest.from as string).toLowerCase()).toBe(accounts[1].address.toLowerCase())
|
|
1680
|
+
expect(simRequest.calls?.length).toBeGreaterThan(0)
|
|
1681
|
+
|
|
1682
|
+
httpServer.close()
|
|
1683
|
+
})
|
|
1684
|
+
|
|
1685
|
+
test('behavior: fee payer fails closed when pre-broadcast simulation reverts', async () => {
|
|
1686
|
+
// A reverting pre-broadcast simulation must abort before broadcast so the
|
|
1687
|
+
// sponsor never pays gas for a transaction that would revert.
|
|
1688
|
+
const rpcMethods: string[] = []
|
|
1689
|
+
const interceptingClient = createClient({
|
|
1690
|
+
account: accounts[0],
|
|
1691
|
+
chain: client.chain,
|
|
1692
|
+
transport: custom({
|
|
1693
|
+
async request(args: any) {
|
|
1694
|
+
rpcMethods.push(args.method)
|
|
1695
|
+
if (args.method === 'eth_call')
|
|
1696
|
+
throw new Error('execution reverted: simulation fixture')
|
|
1697
|
+
return client.transport.request(args)
|
|
1698
|
+
},
|
|
1699
|
+
}),
|
|
1700
|
+
})
|
|
1701
|
+
|
|
1702
|
+
const serverWithRevert = Mppx_server.create({
|
|
1703
|
+
methods: [
|
|
1704
|
+
tempo_server.charge({
|
|
1705
|
+
getClient() {
|
|
1706
|
+
return interceptingClient
|
|
1707
|
+
},
|
|
1708
|
+
currency: asset,
|
|
1709
|
+
account: accounts[0],
|
|
1710
|
+
}),
|
|
1711
|
+
],
|
|
1712
|
+
realm,
|
|
1713
|
+
secretKey,
|
|
1714
|
+
})
|
|
1715
|
+
|
|
1716
|
+
const mppx = Mppx_client.create({
|
|
1717
|
+
polyfill: false,
|
|
1718
|
+
methods: [
|
|
1719
|
+
tempo_client({
|
|
1720
|
+
account: accounts[1],
|
|
1721
|
+
getClient() {
|
|
1722
|
+
return client
|
|
1723
|
+
},
|
|
1724
|
+
}),
|
|
1725
|
+
],
|
|
1726
|
+
})
|
|
1727
|
+
|
|
1728
|
+
const httpServer = await Http.createServer(async (req, res) => {
|
|
1729
|
+
const result = await Mppx_server.toNodeListener(
|
|
1730
|
+
serverWithRevert.charge({
|
|
1731
|
+
feePayer: accounts[0],
|
|
1732
|
+
amount: '1',
|
|
1733
|
+
currency: asset,
|
|
1734
|
+
recipient: accounts[0].address,
|
|
1735
|
+
}),
|
|
1736
|
+
)(req, res)
|
|
1737
|
+
if (result.status === 402) return
|
|
1738
|
+
res.end('OK')
|
|
1739
|
+
})
|
|
1740
|
+
|
|
1741
|
+
const challengeResponse = await fetch(httpServer.url)
|
|
1742
|
+
const credential = await mppx.createCredential(challengeResponse)
|
|
1743
|
+
rpcMethods.length = 0
|
|
1744
|
+
|
|
1745
|
+
const authResponse = await fetch(httpServer.url, {
|
|
1746
|
+
headers: { Authorization: credential },
|
|
1747
|
+
})
|
|
1748
|
+
|
|
1749
|
+
// Fails closed: not successful, and the transaction is never broadcast.
|
|
1750
|
+
expect(authResponse.status).not.toBe(200)
|
|
1751
|
+
expect(rpcMethods).toContain('eth_call')
|
|
1752
|
+
expect(rpcMethods).not.toContain('eth_sendRawTransactionSync')
|
|
1753
|
+
expect(rpcMethods).not.toContain('eth_sendRawTransaction')
|
|
1754
|
+
|
|
1755
|
+
httpServer.close()
|
|
1756
|
+
})
|
|
1757
|
+
|
|
1758
|
+
test('behavior: fee payer fails closed when simulation reverts (optimistic mode)', async () => {
|
|
1759
|
+
const rpcMethods: string[] = []
|
|
1760
|
+
const interceptingClient = createClient({
|
|
1761
|
+
account: accounts[0],
|
|
1762
|
+
chain: client.chain,
|
|
1763
|
+
transport: custom({
|
|
1764
|
+
async request(args: any) {
|
|
1765
|
+
rpcMethods.push(args.method)
|
|
1766
|
+
if (args.method === 'eth_call')
|
|
1767
|
+
throw new Error('execution reverted: simulation fixture')
|
|
1768
|
+
return client.transport.request(args)
|
|
1769
|
+
},
|
|
1770
|
+
}),
|
|
1771
|
+
})
|
|
1772
|
+
|
|
1773
|
+
const serverNoWait = Mppx_server.create({
|
|
1774
|
+
methods: [
|
|
1775
|
+
tempo_server.charge({
|
|
1776
|
+
getClient() {
|
|
1777
|
+
return interceptingClient
|
|
1778
|
+
},
|
|
1779
|
+
currency: asset,
|
|
1780
|
+
account: accounts[0],
|
|
1781
|
+
waitForConfirmation: false,
|
|
1782
|
+
}),
|
|
1783
|
+
],
|
|
1784
|
+
realm,
|
|
1785
|
+
secretKey,
|
|
1786
|
+
})
|
|
1787
|
+
|
|
1788
|
+
const mppx = Mppx_client.create({
|
|
1789
|
+
polyfill: false,
|
|
1790
|
+
methods: [
|
|
1791
|
+
tempo_client({
|
|
1792
|
+
account: accounts[1],
|
|
1793
|
+
getClient() {
|
|
1794
|
+
return client
|
|
1795
|
+
},
|
|
1796
|
+
}),
|
|
1797
|
+
],
|
|
1798
|
+
})
|
|
1799
|
+
|
|
1800
|
+
const httpServer = await Http.createServer(async (req, res) => {
|
|
1801
|
+
const result = await Mppx_server.toNodeListener(
|
|
1802
|
+
serverNoWait.charge({
|
|
1803
|
+
feePayer: accounts[0],
|
|
1804
|
+
amount: '1',
|
|
1805
|
+
currency: asset,
|
|
1806
|
+
recipient: accounts[0].address,
|
|
1807
|
+
}),
|
|
1808
|
+
)(req, res)
|
|
1809
|
+
if (result.status === 402) return
|
|
1810
|
+
res.end('OK')
|
|
1811
|
+
})
|
|
1812
|
+
|
|
1813
|
+
const challengeResponse = await fetch(httpServer.url)
|
|
1814
|
+
const credential = await mppx.createCredential(challengeResponse)
|
|
1815
|
+
rpcMethods.length = 0
|
|
1816
|
+
|
|
1817
|
+
const authResponse = await fetch(httpServer.url, {
|
|
1818
|
+
headers: { Authorization: credential },
|
|
1819
|
+
})
|
|
1820
|
+
|
|
1821
|
+
expect(authResponse.status).not.toBe(200)
|
|
1822
|
+
expect(rpcMethods).toContain('eth_call')
|
|
1823
|
+
expect(rpcMethods).not.toContain('eth_sendRawTransaction')
|
|
1824
|
+
expect(rpcMethods).not.toContain('eth_sendRawTransactionSync')
|
|
1825
|
+
|
|
1826
|
+
httpServer.close()
|
|
1827
|
+
})
|
|
1828
|
+
|
|
1607
1829
|
test('behavior: fee payer rejects concurrent in-flight transactions from one sender', async () => {
|
|
1608
1830
|
let releaseSimulation!: () => void
|
|
1609
1831
|
let resolveSimulationStarted!: () => void
|
|
@@ -2484,7 +2706,11 @@ describe('tempo', () => {
|
|
|
2484
2706
|
domain: Proof.domain(chain.id),
|
|
2485
2707
|
types: Proof.types,
|
|
2486
2708
|
primaryType: 'Proof',
|
|
2487
|
-
message: Proof.message(
|
|
2709
|
+
message: Proof.message({
|
|
2710
|
+
account: accounts[1].address,
|
|
2711
|
+
challengeId: challenge.id,
|
|
2712
|
+
realm: challenge.realm,
|
|
2713
|
+
}),
|
|
2488
2714
|
})
|
|
2489
2715
|
|
|
2490
2716
|
const credential = Credential.from({
|
|
@@ -2522,7 +2748,11 @@ describe('tempo', () => {
|
|
|
2522
2748
|
domain: Proof.domain(chain.id),
|
|
2523
2749
|
types: Proof.types,
|
|
2524
2750
|
primaryType: 'Proof',
|
|
2525
|
-
message: Proof.message(
|
|
2751
|
+
message: Proof.message({
|
|
2752
|
+
account: accounts[1].address,
|
|
2753
|
+
challengeId: challenge.id,
|
|
2754
|
+
realm: challenge.realm,
|
|
2755
|
+
}),
|
|
2526
2756
|
})
|
|
2527
2757
|
|
|
2528
2758
|
const credential = Credential.from({
|
|
@@ -2637,7 +2867,11 @@ describe('tempo', () => {
|
|
|
2637
2867
|
domain: Proof.domain(chain.id),
|
|
2638
2868
|
types: Proof.types,
|
|
2639
2869
|
primaryType: 'Proof',
|
|
2640
|
-
message: Proof.message(
|
|
2870
|
+
message: Proof.message({
|
|
2871
|
+
account: accounts[1].address,
|
|
2872
|
+
challengeId: challenge.id,
|
|
2873
|
+
realm: challenge.realm,
|
|
2874
|
+
}),
|
|
2641
2875
|
})
|
|
2642
2876
|
|
|
2643
2877
|
const credential = Credential.from({
|
|
@@ -2761,6 +2995,91 @@ describe('tempo', () => {
|
|
|
2761
2995
|
},
|
|
2762
2996
|
)
|
|
2763
2997
|
|
|
2998
|
+
test('behavior: rejects access-key proof replayed across a co-authorized account', async () => {
|
|
2999
|
+
// The same access key is authorized by two roots (`payer` and `other`),
|
|
3000
|
+
// so a proof bound to `payer` must not be claimable as `other` even
|
|
3001
|
+
// though `other` independently authorized the key.
|
|
3002
|
+
const payer = accounts[1]
|
|
3003
|
+
const other = accounts[2]
|
|
3004
|
+
const accessKey = Account.fromSecp256k1(Secp256k1.randomPrivateKey(), {
|
|
3005
|
+
access: payer,
|
|
3006
|
+
})
|
|
3007
|
+
|
|
3008
|
+
await fundAccount({ address: other.address, token: asset })
|
|
3009
|
+
await Actions.accessKey.authorizeSync(client, {
|
|
3010
|
+
account: payer,
|
|
3011
|
+
accessKey,
|
|
3012
|
+
feeToken: asset,
|
|
3013
|
+
})
|
|
3014
|
+
await Actions.accessKey.authorizeSync(client, {
|
|
3015
|
+
account: other,
|
|
3016
|
+
accessKey,
|
|
3017
|
+
feeToken: asset,
|
|
3018
|
+
})
|
|
3019
|
+
|
|
3020
|
+
const httpServer = await Http.createServer(async (req, res) => {
|
|
3021
|
+
const result = await Mppx_server.toNodeListener(
|
|
3022
|
+
server.charge({ amount: '0', decimals: 6 }),
|
|
3023
|
+
)(req, res)
|
|
3024
|
+
if (result.status === 402) return
|
|
3025
|
+
res.end('OK')
|
|
3026
|
+
})
|
|
3027
|
+
|
|
3028
|
+
try {
|
|
3029
|
+
const response1 = await fetch(httpServer.url)
|
|
3030
|
+
expect(response1.status).toBe(402)
|
|
3031
|
+
|
|
3032
|
+
const challenge = Challenge.fromResponse(response1, {
|
|
3033
|
+
methods: [tempo_client.charge()],
|
|
3034
|
+
})
|
|
3035
|
+
|
|
3036
|
+
// Access key signs a proof bound to `payer` (the `account` field).
|
|
3037
|
+
const signature = await signTypedData(client, {
|
|
3038
|
+
account: accessKey,
|
|
3039
|
+
domain: Proof.domain(chain.id),
|
|
3040
|
+
types: Proof.types,
|
|
3041
|
+
primaryType: 'Proof',
|
|
3042
|
+
message: Proof.message({
|
|
3043
|
+
account: payer.address,
|
|
3044
|
+
challengeId: challenge.id,
|
|
3045
|
+
realm: challenge.realm,
|
|
3046
|
+
}),
|
|
3047
|
+
})
|
|
3048
|
+
|
|
3049
|
+
// The bound payer accepts the proof.
|
|
3050
|
+
const accepted = await fetch(httpServer.url, {
|
|
3051
|
+
headers: {
|
|
3052
|
+
Authorization: Credential.serialize(
|
|
3053
|
+
Credential.from({
|
|
3054
|
+
challenge,
|
|
3055
|
+
payload: { signature, type: 'proof' as const },
|
|
3056
|
+
source: `did:pkh:eip155:${chain.id}:${payer.address}`,
|
|
3057
|
+
}),
|
|
3058
|
+
),
|
|
3059
|
+
},
|
|
3060
|
+
})
|
|
3061
|
+
expect(accepted.status).toBe(200)
|
|
3062
|
+
|
|
3063
|
+
// Replaying the same proof as `other` is rejected by the wallet binding.
|
|
3064
|
+
const replay = await fetch(httpServer.url, {
|
|
3065
|
+
headers: {
|
|
3066
|
+
Authorization: Credential.serialize(
|
|
3067
|
+
Credential.from({
|
|
3068
|
+
challenge,
|
|
3069
|
+
payload: { signature, type: 'proof' as const },
|
|
3070
|
+
source: `did:pkh:eip155:${chain.id}:${other.address}`,
|
|
3071
|
+
}),
|
|
3072
|
+
),
|
|
3073
|
+
},
|
|
3074
|
+
})
|
|
3075
|
+
expect(replay.status).toBe(402)
|
|
3076
|
+
const body = (await replay.json()) as { detail: string }
|
|
3077
|
+
expect(body.detail).toContain('Proof signature does not match source.')
|
|
3078
|
+
} finally {
|
|
3079
|
+
httpServer.close()
|
|
3080
|
+
}
|
|
3081
|
+
})
|
|
3082
|
+
|
|
2764
3083
|
test('behavior: rejects replayed proof credential when store is configured', async () => {
|
|
2765
3084
|
const replayStore = Store.memory()
|
|
2766
3085
|
const server_ = Mppx_server.create({
|
|
@@ -2798,7 +3117,11 @@ describe('tempo', () => {
|
|
|
2798
3117
|
domain: Proof.domain(chain.id),
|
|
2799
3118
|
types: Proof.types,
|
|
2800
3119
|
primaryType: 'Proof',
|
|
2801
|
-
message: Proof.message(
|
|
3120
|
+
message: Proof.message({
|
|
3121
|
+
account: accounts[1].address,
|
|
3122
|
+
challengeId: challenge.id,
|
|
3123
|
+
realm: challenge.realm,
|
|
3124
|
+
}),
|
|
2802
3125
|
})
|
|
2803
3126
|
|
|
2804
3127
|
const credential = Credential.from({
|
|
@@ -2859,7 +3182,11 @@ describe('tempo', () => {
|
|
|
2859
3182
|
domain: Proof.domain(chain.id),
|
|
2860
3183
|
types: Proof.types,
|
|
2861
3184
|
primaryType: 'Proof',
|
|
2862
|
-
message: Proof.message(
|
|
3185
|
+
message: Proof.message({
|
|
3186
|
+
account: accounts[1].address,
|
|
3187
|
+
challengeId: challenge.id,
|
|
3188
|
+
realm: challenge.realm,
|
|
3189
|
+
}),
|
|
2863
3190
|
})
|
|
2864
3191
|
|
|
2865
3192
|
const credential = Credential.serialize(
|
|
@@ -2933,7 +3260,11 @@ describe('tempo', () => {
|
|
|
2933
3260
|
domain: Proof.domain(chain.id),
|
|
2934
3261
|
types: Proof.types,
|
|
2935
3262
|
primaryType: 'Proof',
|
|
2936
|
-
message: Proof.message(
|
|
3263
|
+
message: Proof.message({
|
|
3264
|
+
account: accounts[1].address,
|
|
3265
|
+
challengeId: challenge.id,
|
|
3266
|
+
realm: challenge.realm,
|
|
3267
|
+
}),
|
|
2937
3268
|
})
|
|
2938
3269
|
|
|
2939
3270
|
const credential = Credential.from({
|
|
@@ -2996,7 +3327,11 @@ describe('tempo', () => {
|
|
|
2996
3327
|
domain: Proof.domain(chain.id),
|
|
2997
3328
|
types: Proof.types,
|
|
2998
3329
|
primaryType: 'Proof',
|
|
2999
|
-
message: Proof.message(
|
|
3330
|
+
message: Proof.message({
|
|
3331
|
+
account: accounts[1].address,
|
|
3332
|
+
challengeId: challenge1.id,
|
|
3333
|
+
realm: challenge1.realm,
|
|
3334
|
+
}),
|
|
3000
3335
|
})
|
|
3001
3336
|
|
|
3002
3337
|
const credential1 = Credential.from({
|
|
@@ -3027,7 +3362,11 @@ describe('tempo', () => {
|
|
|
3027
3362
|
domain: Proof.domain(chain.id),
|
|
3028
3363
|
types: Proof.types,
|
|
3029
3364
|
primaryType: 'Proof',
|
|
3030
|
-
message: Proof.message(
|
|
3365
|
+
message: Proof.message({
|
|
3366
|
+
account: accounts[1].address,
|
|
3367
|
+
challengeId: challenge2.id,
|
|
3368
|
+
realm: challenge2.realm,
|
|
3369
|
+
}),
|
|
3031
3370
|
})
|
|
3032
3371
|
|
|
3033
3372
|
const credential2 = Credential.from({
|
|
@@ -3064,7 +3403,11 @@ describe('tempo', () => {
|
|
|
3064
3403
|
domain: Proof.domain(chain.id),
|
|
3065
3404
|
types: Proof.types,
|
|
3066
3405
|
primaryType: 'Proof',
|
|
3067
|
-
message: Proof.message(
|
|
3406
|
+
message: Proof.message({
|
|
3407
|
+
account: accounts[1].address,
|
|
3408
|
+
challengeId: challenge.id,
|
|
3409
|
+
realm: challenge.realm,
|
|
3410
|
+
}),
|
|
3068
3411
|
})
|
|
3069
3412
|
|
|
3070
3413
|
const credential = Credential.from({
|
|
@@ -3100,7 +3443,11 @@ describe('tempo', () => {
|
|
|
3100
3443
|
domain: Proof.domain(chain.id),
|
|
3101
3444
|
types: Proof.types,
|
|
3102
3445
|
primaryType: 'Proof',
|
|
3103
|
-
message: Proof.message(
|
|
3446
|
+
message: Proof.message({
|
|
3447
|
+
account: accounts[1].address,
|
|
3448
|
+
challengeId: challenge.id,
|
|
3449
|
+
realm: challenge.realm,
|
|
3450
|
+
}),
|
|
3104
3451
|
})
|
|
3105
3452
|
|
|
3106
3453
|
const credential = Credential.from({
|
|
@@ -3199,7 +3546,11 @@ describe('tempo', () => {
|
|
|
3199
3546
|
domain: Proof.domain(chain.id),
|
|
3200
3547
|
types: Proof.types,
|
|
3201
3548
|
primaryType: 'Proof',
|
|
3202
|
-
message: Proof.message(
|
|
3549
|
+
message: Proof.message({
|
|
3550
|
+
account: accounts[1].address,
|
|
3551
|
+
challengeId: challenge.id,
|
|
3552
|
+
realm: challenge.realm,
|
|
3553
|
+
}),
|
|
3203
3554
|
})
|
|
3204
3555
|
|
|
3205
3556
|
const credential = Credential.from({
|
|
@@ -3237,7 +3588,11 @@ describe('tempo', () => {
|
|
|
3237
3588
|
domain: Proof.domain(chain.id),
|
|
3238
3589
|
types: Proof.types,
|
|
3239
3590
|
primaryType: 'Proof',
|
|
3240
|
-
message: Proof.message(
|
|
3591
|
+
message: Proof.message({
|
|
3592
|
+
account: accounts[1].address,
|
|
3593
|
+
challengeId: challenge.id,
|
|
3594
|
+
realm: challenge.realm,
|
|
3595
|
+
}),
|
|
3241
3596
|
})
|
|
3242
3597
|
|
|
3243
3598
|
const credential = Credential.from({
|
|
@@ -3276,7 +3631,11 @@ describe('tempo', () => {
|
|
|
3276
3631
|
domain: Proof.domain(99999),
|
|
3277
3632
|
types: Proof.types,
|
|
3278
3633
|
primaryType: 'Proof',
|
|
3279
|
-
message: Proof.message(
|
|
3634
|
+
message: Proof.message({
|
|
3635
|
+
account: accounts[1].address,
|
|
3636
|
+
challengeId: challenge.id,
|
|
3637
|
+
realm: challenge.realm,
|
|
3638
|
+
}),
|
|
3280
3639
|
})
|
|
3281
3640
|
|
|
3282
3641
|
const credential = Credential.from({
|
|
@@ -3312,7 +3671,11 @@ describe('tempo', () => {
|
|
|
3312
3671
|
domain: Proof.domain(chain.id),
|
|
3313
3672
|
types: Proof.types,
|
|
3314
3673
|
primaryType: 'Proof',
|
|
3315
|
-
message: Proof.message(
|
|
3674
|
+
message: Proof.message({
|
|
3675
|
+
account: accounts[1].address,
|
|
3676
|
+
challengeId: challenge.id,
|
|
3677
|
+
realm: 'evil.example.com',
|
|
3678
|
+
}),
|
|
3316
3679
|
})
|
|
3317
3680
|
|
|
3318
3681
|
const credential = Credential.from({
|
|
@@ -3348,7 +3711,11 @@ describe('tempo', () => {
|
|
|
3348
3711
|
domain: Proof.domain(chain.id),
|
|
3349
3712
|
types: Proof.types,
|
|
3350
3713
|
primaryType: 'Proof',
|
|
3351
|
-
message: Proof.message(
|
|
3714
|
+
message: Proof.message({
|
|
3715
|
+
account: accounts[1].address,
|
|
3716
|
+
challengeId: challenge.id,
|
|
3717
|
+
realm: challenge.realm,
|
|
3718
|
+
}),
|
|
3352
3719
|
})
|
|
3353
3720
|
|
|
3354
3721
|
const credential = Credential.from({
|
|
@@ -3384,7 +3751,11 @@ describe('tempo', () => {
|
|
|
3384
3751
|
domain: Proof.domain(chain.id),
|
|
3385
3752
|
types: Proof.types,
|
|
3386
3753
|
primaryType: 'Proof',
|
|
3387
|
-
message: Proof.message(
|
|
3754
|
+
message: Proof.message({
|
|
3755
|
+
account: accounts[1].address,
|
|
3756
|
+
challengeId: challenge.id,
|
|
3757
|
+
realm: challenge.realm,
|
|
3758
|
+
}),
|
|
3388
3759
|
})
|
|
3389
3760
|
|
|
3390
3761
|
const credential = Credential.from({
|
|
@@ -2,7 +2,6 @@ import * as SignatureEnvelope from 'ox/tempo/SignatureEnvelope'
|
|
|
2
2
|
import {
|
|
3
3
|
decodeFunctionData,
|
|
4
4
|
formatUnits,
|
|
5
|
-
hashTypedData,
|
|
6
5
|
keccak256,
|
|
7
6
|
parseEventLogs,
|
|
8
7
|
type TransactionReceipt,
|
|
@@ -292,11 +291,15 @@ export function charge<const parameters extends charge.Parameters>(
|
|
|
292
291
|
}
|
|
293
292
|
|
|
294
293
|
const valid = await verifyTypedData(client, {
|
|
294
|
+
// Bind verification to the claimed payer (`source.address`): the
|
|
295
|
+
// signer may be the payer itself or an access key authorized for it.
|
|
295
296
|
address: source.address,
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
297
|
+
...Proof.typedData({
|
|
298
|
+
account: source.address,
|
|
299
|
+
chainId: resolvedChainId,
|
|
300
|
+
challengeId: challenge.id,
|
|
301
|
+
realm: challenge.realm,
|
|
302
|
+
}),
|
|
300
303
|
signature: payload.signature as `0x${string}`,
|
|
301
304
|
})
|
|
302
305
|
if (!valid) {
|
|
@@ -403,6 +406,13 @@ export function charge<const parameters extends charge.Parameters>(
|
|
|
403
406
|
const allowedFeeTokens = FeePayer.defaultAllowedFeeTokens(chainId)
|
|
404
407
|
if (isFeePayerTx) FeePayer.assertAllowedFeeToken(transaction, allowedFeeTokens)
|
|
405
408
|
|
|
409
|
+
// Request for the pre-broadcast simulation; for sponsored payments
|
|
410
|
+
// this is overwritten below with the co-signed shape.
|
|
411
|
+
let simulationRequest: Record<string, unknown> = FeePayer.simulationTransaction(
|
|
412
|
+
transaction,
|
|
413
|
+
{ feePayer: isFeePayerTx },
|
|
414
|
+
)
|
|
415
|
+
|
|
406
416
|
const serializedTransaction_final = await (async () => {
|
|
407
417
|
if (feePayerAccount && methodDetails?.feePayer !== false) {
|
|
408
418
|
const sponsored = FeePayer.prepareSponsoredTransaction({
|
|
@@ -417,22 +427,47 @@ export function charge<const parameters extends charge.Parameters>(
|
|
|
417
427
|
feeToken: transaction.feeToken ?? defaults.tokens.pathUsd,
|
|
418
428
|
},
|
|
419
429
|
})
|
|
430
|
+
// `account` is the sender (eth_call `from`); `feePayer` is the
|
|
431
|
+
// sponsor that pays gas.
|
|
432
|
+
simulationRequest = {
|
|
433
|
+
...sponsored,
|
|
434
|
+
account: transaction.from,
|
|
435
|
+
feePayer: feePayerAccount.address,
|
|
436
|
+
feePayerSignature: undefined,
|
|
437
|
+
signature: undefined,
|
|
438
|
+
}
|
|
420
439
|
return signTransaction(client, sponsored as never)
|
|
421
440
|
}
|
|
422
|
-
if (feePayerUrl && isFeePayerTx)
|
|
423
|
-
|
|
441
|
+
if (feePayerUrl && isFeePayerTx) {
|
|
442
|
+
const hosted = await FeePayer.fillHostedFeePayerTransaction({
|
|
424
443
|
allowedFeeTokens,
|
|
444
|
+
challengeExpires: expires,
|
|
445
|
+
chainId: chainId ?? client.chain!.id,
|
|
446
|
+
details: { amount, currency, recipient },
|
|
447
|
+
policy: feePayerPolicy,
|
|
425
448
|
transaction,
|
|
426
449
|
url: feePayerUrl,
|
|
427
450
|
})
|
|
451
|
+
// Simulate the co-signed envelope (concrete fee payer + chosen
|
|
452
|
+
// fee token), mirroring the local-sponsor path.
|
|
453
|
+
simulationRequest = {
|
|
454
|
+
...transaction,
|
|
455
|
+
account: transaction.from,
|
|
456
|
+
feePayer: hosted.feePayer,
|
|
457
|
+
feePayerSignature: undefined,
|
|
458
|
+
feeToken: hosted.feeToken,
|
|
459
|
+
signature: undefined,
|
|
460
|
+
}
|
|
461
|
+
return hosted.serializedTransaction
|
|
462
|
+
}
|
|
428
463
|
return serializedTransaction
|
|
429
464
|
})()
|
|
430
465
|
|
|
466
|
+
// Pre-broadcast simulation: fail closed before broadcast so the
|
|
467
|
+
// sponsor never pays gas for a transaction that would revert.
|
|
468
|
+
await viem_call(client, simulationRequest as never)
|
|
469
|
+
|
|
431
470
|
if (waitForConfirmation) {
|
|
432
|
-
await viem_call(
|
|
433
|
-
client,
|
|
434
|
-
FeePayer.simulationTransaction(transaction, { feePayer: isFeePayerTx }),
|
|
435
|
-
)
|
|
436
471
|
const receipt = await sendRawTransactionSync(client, {
|
|
437
472
|
serializedTransaction: serializedTransaction_final,
|
|
438
473
|
})
|
|
@@ -462,13 +497,9 @@ export function charge<const parameters extends charge.Parameters>(
|
|
|
462
497
|
return toReceipt(receipt)
|
|
463
498
|
}
|
|
464
499
|
|
|
465
|
-
// Optimistic path:
|
|
466
|
-
//
|
|
467
|
-
//
|
|
468
|
-
await viem_call(
|
|
469
|
-
client,
|
|
470
|
-
FeePayer.simulationTransaction(transaction, { feePayer: isFeePayerTx }),
|
|
471
|
-
)
|
|
500
|
+
// Optimistic path: broadcast without waiting for confirmation
|
|
501
|
+
// (simulation above already ran). The returned receipt assumes
|
|
502
|
+
// success — callers opt in via waitForConfirmation: false.
|
|
472
503
|
const reference = await sendRawTransaction(client, {
|
|
473
504
|
serializedTransaction: serializedTransaction_final,
|
|
474
505
|
})
|
|
@@ -1007,12 +1038,7 @@ function recoverAuthorizedProofSigner(parameters: {
|
|
|
1007
1038
|
|
|
1008
1039
|
try {
|
|
1009
1040
|
const envelope = SignatureEnvelope.from(signature)
|
|
1010
|
-
const proofHash =
|
|
1011
|
-
domain: Proof.domain(chainId),
|
|
1012
|
-
types: Proof.types,
|
|
1013
|
-
primaryType: 'Proof',
|
|
1014
|
-
message: Proof.message(challengeId, realm),
|
|
1015
|
-
})
|
|
1041
|
+
const proofHash = Proof.hash({ account: sourceAddress, chainId, challengeId, realm })
|
|
1016
1042
|
|
|
1017
1043
|
if (envelope.type === 'keychain') {
|
|
1018
1044
|
if (!TempoAddress.isEqual(envelope.userAddress, sourceAddress)) return null
|
|
@@ -49,14 +49,14 @@ function createSessionMethod<const parameters extends tempo.Parameters>(
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
|
-
* Creates
|
|
52
|
+
* Creates the common Tempo `charge` and `session` methods from shared parameters.
|
|
53
53
|
*
|
|
54
54
|
* @example
|
|
55
55
|
* ```ts
|
|
56
56
|
* import { Mppx, tempo } from 'mppx/server'
|
|
57
57
|
*
|
|
58
58
|
* const mppx = Mppx.create({
|
|
59
|
-
* methods: [tempo({ currency: '0x...', recipient: '0x...' })],
|
|
59
|
+
* methods: [tempo.common({ currency: '0x...', recipient: '0x...' })],
|
|
60
60
|
* })
|
|
61
61
|
* ```
|
|
62
62
|
*/
|
|
@@ -69,6 +69,8 @@ export namespace tempo {
|
|
|
69
69
|
|
|
70
70
|
/** Creates a Tempo `charge` method for one-time TIP-20 token transfers. */
|
|
71
71
|
export const charge = charge_
|
|
72
|
+
/** Creates the common Tempo `charge` and `session` methods from shared parameters. */
|
|
73
|
+
export const common = tempo
|
|
72
74
|
/** Creates a TIP-1034 Tempo `session` method for session-based TIP-20 token payments. */
|
|
73
75
|
export const session = sessionServer
|
|
74
76
|
/** @deprecated Use `tempo.session()` for the TIP-1034 session server method. */
|