mppx 0.6.10 → 0.6.11
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 +6 -0
- package/dist/cli/plugins/tempo.js +1 -1
- package/dist/cli/plugins/tempo.js.map +1 -1
- package/dist/tempo/client/ChannelOps.js +2 -2
- package/dist/tempo/client/ChannelOps.js.map +1 -1
- package/dist/tempo/client/Session.js +4 -4
- package/dist/tempo/client/Session.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/package.json +1 -1
- package/src/cli/cli.test.ts +94 -3
- package/src/cli/plugins/tempo.ts +1 -1
- package/src/tempo/client/ChannelOps.test.ts +2 -2
- package/src/tempo/client/ChannelOps.ts +1 -1
- package/src/tempo/client/Session.test.ts +60 -4
- package/src/tempo/client/Session.ts +2 -2
- package/src/tempo/server/internal/html.gen.ts +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"html.gen.js","sourceRoot":"","sources":["../../../../src/tempo/server/internal/html.gen.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAC3B,MAAM,CAAC,MAAM,IAAI,GAAG,
|
|
1
|
+
{"version":3,"file":"html.gen.js","sourceRoot":"","sources":["../../../../src/tempo/server/internal/html.gen.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAC3B,MAAM,CAAC,MAAM,IAAI,GAAG,ixtcAAixtc,CAAA"}
|
package/package.json
CHANGED
package/src/cli/cli.test.ts
CHANGED
|
@@ -4,13 +4,13 @@ import * as os from 'node:os'
|
|
|
4
4
|
import * as path from 'node:path'
|
|
5
5
|
import { pathToFileURL } from 'node:url'
|
|
6
6
|
|
|
7
|
-
import { parseUnits } from 'viem'
|
|
7
|
+
import { decodeFunctionData, erc20Abi, parseUnits, type Address } from 'viem'
|
|
8
8
|
import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'
|
|
9
|
-
import { Addresses } from 'viem/tempo'
|
|
9
|
+
import { Addresses, Transaction } from 'viem/tempo'
|
|
10
10
|
import { afterAll, describe, expect, test } from 'vp/test'
|
|
11
11
|
import * as Http from '~test/Http.js'
|
|
12
12
|
import { rpcUrl } from '~test/tempo/prool.js'
|
|
13
|
-
import { deployEscrow } from '~test/tempo/session.js'
|
|
13
|
+
import { deployEscrow, escrowAbi } from '~test/tempo/session.js'
|
|
14
14
|
import { accounts, asset, chain, client, fundAccount } from '~test/tempo/viem.js'
|
|
15
15
|
|
|
16
16
|
import * as Challenge from '../Challenge.js'
|
|
@@ -29,6 +29,10 @@ import cli from './cli.js'
|
|
|
29
29
|
const testPrivateKey = generatePrivateKey()
|
|
30
30
|
const testAccount = privateKeyToAccount(testPrivateKey)
|
|
31
31
|
const cliTestXdgDataHome = fs.mkdtempSync(path.join(os.tmpdir(), 'mppx-cli-xdg-'))
|
|
32
|
+
const cliSessionFeePayerPolicy = {
|
|
33
|
+
maxGas: 2_250_000n,
|
|
34
|
+
maxTotalFee: 60_000_000_000_000_000n,
|
|
35
|
+
}
|
|
32
36
|
|
|
33
37
|
afterAll(() => {
|
|
34
38
|
fs.rmSync(cliTestXdgDataHome, { recursive: true, force: true })
|
|
@@ -698,6 +702,7 @@ describe('session multi-fetch (examples/session/multi-fetch)', () => {
|
|
|
698
702
|
escrowContract: escrow,
|
|
699
703
|
chainId: client.chain.id,
|
|
700
704
|
feePayer: true,
|
|
705
|
+
feePayerPolicy: cliSessionFeePayerPolicy,
|
|
701
706
|
}),
|
|
702
707
|
],
|
|
703
708
|
realm: 'cli-test-multifetch',
|
|
@@ -727,6 +732,89 @@ describe('session multi-fetch (examples/session/multi-fetch)', () => {
|
|
|
727
732
|
}
|
|
728
733
|
})
|
|
729
734
|
|
|
735
|
+
test('prefers CLI deposit over server suggestedDeposit', { timeout: 120_000 }, async () => {
|
|
736
|
+
await fundAccount({ address: testAccount.address, token: Addresses.pathUsd })
|
|
737
|
+
await fundAccount({ address: testAccount.address, token: asset })
|
|
738
|
+
|
|
739
|
+
const escrow = await deployEscrow()
|
|
740
|
+
let openCredential: SessionCredentialPayload | undefined
|
|
741
|
+
|
|
742
|
+
const httpServer = await Http.createServer(async (req, res) => {
|
|
743
|
+
const authHeader = req.headers.authorization
|
|
744
|
+
if (!authHeader) {
|
|
745
|
+
res.writeHead(402, {
|
|
746
|
+
'WWW-Authenticate': Challenge.serialize(
|
|
747
|
+
Challenge.from({
|
|
748
|
+
id: 'cli-deposit-override',
|
|
749
|
+
realm: 'cli-test-deposit-override',
|
|
750
|
+
method: 'tempo',
|
|
751
|
+
intent: 'session',
|
|
752
|
+
request: {
|
|
753
|
+
amount: '1000000',
|
|
754
|
+
currency: asset,
|
|
755
|
+
decimals: 6,
|
|
756
|
+
recipient: accounts[0].address,
|
|
757
|
+
suggestedDeposit: '7000000',
|
|
758
|
+
unitType: 'token',
|
|
759
|
+
methodDetails: {
|
|
760
|
+
chainId: chain.id,
|
|
761
|
+
escrowContract: escrow,
|
|
762
|
+
},
|
|
763
|
+
},
|
|
764
|
+
}),
|
|
765
|
+
),
|
|
766
|
+
})
|
|
767
|
+
res.end()
|
|
768
|
+
return
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
try {
|
|
772
|
+
const cred = Credential.deserialize<SessionCredentialPayload>(authHeader)
|
|
773
|
+
if (cred.payload.action === 'open') openCredential = cred.payload
|
|
774
|
+
} catch {}
|
|
775
|
+
|
|
776
|
+
res.writeHead(200)
|
|
777
|
+
res.end('scraped-content')
|
|
778
|
+
})
|
|
779
|
+
|
|
780
|
+
try {
|
|
781
|
+
await serve([httpServer.url, '--rpc-url', rpcUrl, '-s', '-M', 'deposit=10'], {
|
|
782
|
+
env: { MPPX_PRIVATE_KEY: testPrivateKey },
|
|
783
|
+
})
|
|
784
|
+
|
|
785
|
+
expect(openCredential).toBeDefined()
|
|
786
|
+
expect(openCredential?.action).toBe('open')
|
|
787
|
+
if (!openCredential || openCredential.action !== 'open')
|
|
788
|
+
throw new Error('missing open credential')
|
|
789
|
+
|
|
790
|
+
const transaction = Transaction.deserialize(openCredential.transaction)
|
|
791
|
+
if (!('calls' in transaction)) throw new Error('unexpected transaction type')
|
|
792
|
+
const [approveCall, openCall] = transaction.calls as readonly [
|
|
793
|
+
{ to?: Address; data?: `0x${string}` },
|
|
794
|
+
{ to?: Address; data?: `0x${string}` },
|
|
795
|
+
]
|
|
796
|
+
const approve = decodeFunctionData({ abi: erc20Abi, data: approveCall.data ?? '0x' })
|
|
797
|
+
const open = decodeFunctionData({ abi: escrowAbi, data: openCall.data ?? '0x' })
|
|
798
|
+
const approveArgs = approve.args as readonly [Address, bigint]
|
|
799
|
+
const openArgs = open.args as readonly [Address, Address, bigint, string, Address]
|
|
800
|
+
|
|
801
|
+
expect(approveCall.to).toBe(asset)
|
|
802
|
+
expect(approve.functionName).toBe('approve')
|
|
803
|
+
expect(approveArgs[0].toLowerCase()).toBe(escrow.toLowerCase())
|
|
804
|
+
expect(approveArgs[1]).toBe(10_000_000n)
|
|
805
|
+
|
|
806
|
+
expect(openCall.to?.toLowerCase()).toBe(escrow.toLowerCase())
|
|
807
|
+
expect(open.functionName).toBe('open')
|
|
808
|
+
expect(openArgs[0].toLowerCase()).toBe(accounts[0].address.toLowerCase())
|
|
809
|
+
expect(openArgs[1].toLowerCase()).toBe(asset.toLowerCase())
|
|
810
|
+
expect(openArgs[2]).toBe(10_000_000n)
|
|
811
|
+
expect(openArgs[3]).toEqual(expect.any(String))
|
|
812
|
+
expect(openArgs[4].toLowerCase()).toBe(testAccount.address.toLowerCase())
|
|
813
|
+
} finally {
|
|
814
|
+
httpServer.close()
|
|
815
|
+
}
|
|
816
|
+
})
|
|
817
|
+
|
|
730
818
|
test('bug: non-SSE open should not double-charge tick amount', { timeout: 120_000 }, async () => {
|
|
731
819
|
await fundAccount({ address: testAccount.address, token: Addresses.pathUsd })
|
|
732
820
|
await fundAccount({ address: testAccount.address, token: asset })
|
|
@@ -744,6 +832,7 @@ describe('session multi-fetch (examples/session/multi-fetch)', () => {
|
|
|
744
832
|
escrowContract: escrow,
|
|
745
833
|
chainId: client.chain.id,
|
|
746
834
|
feePayer: true,
|
|
835
|
+
feePayerPolicy: cliSessionFeePayerPolicy,
|
|
747
836
|
}),
|
|
748
837
|
],
|
|
749
838
|
realm: 'cli-test-double-charge',
|
|
@@ -806,6 +895,7 @@ describe('session multi-fetch (examples/session/multi-fetch)', () => {
|
|
|
806
895
|
escrowContract: escrow,
|
|
807
896
|
chainId: client.chain.id,
|
|
808
897
|
feePayer: true,
|
|
898
|
+
feePayerPolicy: cliSessionFeePayerPolicy,
|
|
809
899
|
}),
|
|
810
900
|
],
|
|
811
901
|
realm: 'cli-test-close-action',
|
|
@@ -883,6 +973,7 @@ describe('session sse (examples/session/sse)', () => {
|
|
|
883
973
|
escrowContract: escrow,
|
|
884
974
|
chainId: client.chain.id,
|
|
885
975
|
feePayer: true,
|
|
976
|
+
feePayerPolicy: cliSessionFeePayerPolicy,
|
|
886
977
|
}),
|
|
887
978
|
],
|
|
888
979
|
realm: 'cli-test-sse',
|
package/src/cli/plugins/tempo.ts
CHANGED
|
@@ -164,7 +164,7 @@ export function tempo() {
|
|
|
164
164
|
.suggestedDeposit as string | undefined
|
|
165
165
|
const cliDeposit = tempoOpts.deposit !== undefined ? String(tempoOpts.deposit) : undefined
|
|
166
166
|
const resolved =
|
|
167
|
-
|
|
167
|
+
cliDeposit ?? suggestedDeposit ?? (isTestnet(client!.chain!) ? '10' : undefined)
|
|
168
168
|
if (!resolved) {
|
|
169
169
|
throw new Errors.IncurError({
|
|
170
170
|
code: 'MISSING_DEPOSIT',
|
|
@@ -51,12 +51,12 @@ function makeChallenge(overrides?: Partial<Challenge>): Challenge {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
describe('resolveEscrow', () => {
|
|
54
|
-
test('prefers challenge.request.methodDetails.escrowContract', () => {
|
|
54
|
+
test('prefers escrowContractOverride over challenge.request.methodDetails.escrowContract', () => {
|
|
55
55
|
const challenge = {
|
|
56
56
|
request: { methodDetails: { escrowContract: '0xChallengeEscrow' } },
|
|
57
57
|
}
|
|
58
58
|
const result = resolveEscrow(challenge, 42431, '0xOverride' as Address)
|
|
59
|
-
expect(result).toBe('
|
|
59
|
+
expect(result).toBe('0xOverride')
|
|
60
60
|
})
|
|
61
61
|
|
|
62
62
|
test('falls back to escrowContractOverride', () => {
|
|
@@ -46,8 +46,8 @@ export function resolveEscrow(
|
|
|
46
46
|
const challengeEscrow = (challenge.request.methodDetails as { escrowContract?: string })
|
|
47
47
|
?.escrowContract as Address | undefined
|
|
48
48
|
const escrow =
|
|
49
|
-
challengeEscrow ??
|
|
50
49
|
escrowContractOverride ??
|
|
50
|
+
challengeEscrow ??
|
|
51
51
|
defaults.escrowContract[chainId as keyof typeof defaults.escrowContract]
|
|
52
52
|
if (!escrow)
|
|
53
53
|
throw new Error(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { type Address, createClient, type Hex, http } from 'viem'
|
|
1
|
+
import { type Address, createClient, decodeFunctionData, erc20Abi, type Hex, http } from 'viem'
|
|
2
2
|
import { privateKeyToAccount } from 'viem/accounts'
|
|
3
|
-
import { Addresses } from 'viem/tempo'
|
|
3
|
+
import { Addresses, Transaction } from 'viem/tempo'
|
|
4
4
|
import { beforeAll, describe, expect, test } from 'vp/test'
|
|
5
5
|
import { nodeEnv } from '~test/config.js'
|
|
6
6
|
import { deployEscrow, openChannel } from '~test/tempo/session.js'
|
|
@@ -11,6 +11,7 @@ const isLocalnet = nodeEnv === 'localnet'
|
|
|
11
11
|
import * as Challenge from '../../Challenge.js'
|
|
12
12
|
import * as Credential from '../../Credential.js'
|
|
13
13
|
import { chainId, escrowContract as escrowContractDefaults } from '../internal/defaults.js'
|
|
14
|
+
import { escrowAbi } from '../session/Chain.js'
|
|
14
15
|
import type { SessionCredentialPayload } from '../session/Types.js'
|
|
15
16
|
import { session } from './Session.js'
|
|
16
17
|
|
|
@@ -287,11 +288,11 @@ describe.runIf(isLocalnet)('session (on-chain)', () => {
|
|
|
287
288
|
expect(cred.source).toContain(`did:pkh:eip155:${chain.id}:`)
|
|
288
289
|
})
|
|
289
290
|
|
|
290
|
-
test('suggestedDeposit
|
|
291
|
+
test('suggestedDeposit used when below maxDeposit', async () => {
|
|
291
292
|
const method = session({
|
|
292
293
|
getClient: () => client,
|
|
293
294
|
account: payer,
|
|
294
|
-
|
|
295
|
+
maxDeposit: '10',
|
|
295
296
|
escrowContract,
|
|
296
297
|
})
|
|
297
298
|
|
|
@@ -306,6 +307,61 @@ describe.runIf(isLocalnet)('session (on-chain)', () => {
|
|
|
306
307
|
}
|
|
307
308
|
})
|
|
308
309
|
|
|
310
|
+
test('prefers local escrowContract and deposit over server challenge overrides', async () => {
|
|
311
|
+
const maliciousEscrow = '0x4444444444444444444444444444444444444444' as Address
|
|
312
|
+
const method = session({
|
|
313
|
+
getClient: () => client,
|
|
314
|
+
account: payer,
|
|
315
|
+
deposit: '10',
|
|
316
|
+
escrowContract,
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
const result = await method.createCredential({
|
|
320
|
+
challenge: makeLiveChallenge({
|
|
321
|
+
suggestedDeposit: '7000000',
|
|
322
|
+
methodDetails: {
|
|
323
|
+
chainId: chain.id,
|
|
324
|
+
escrowContract: maliciousEscrow,
|
|
325
|
+
},
|
|
326
|
+
}),
|
|
327
|
+
context: {},
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
const cred = deserializePayload(result)
|
|
331
|
+
expect(cred.payload.action).toBe('open')
|
|
332
|
+
if (cred.payload.action !== 'open') throw new Error('unexpected action')
|
|
333
|
+
|
|
334
|
+
const transaction = Transaction.deserialize(cred.payload.transaction)
|
|
335
|
+
if (!('calls' in transaction)) throw new Error('unexpected transaction type')
|
|
336
|
+
const [approveCall, openCall] = transaction.calls as readonly [
|
|
337
|
+
{ to?: Address; data?: Hex },
|
|
338
|
+
{ to?: Address; data?: Hex },
|
|
339
|
+
]
|
|
340
|
+
const approve = decodeFunctionData({
|
|
341
|
+
abi: erc20Abi,
|
|
342
|
+
data: approveCall.data ?? '0x',
|
|
343
|
+
})
|
|
344
|
+
const open = decodeFunctionData({
|
|
345
|
+
abi: escrowAbi,
|
|
346
|
+
data: openCall.data ?? '0x',
|
|
347
|
+
})
|
|
348
|
+
const approveArgs = approve.args as readonly [Address, bigint]
|
|
349
|
+
const openArgs = open.args as readonly [Address, Address, bigint, string, Address]
|
|
350
|
+
|
|
351
|
+
expect(approveCall.to).toBe(asset)
|
|
352
|
+
expect(approve.functionName).toBe('approve')
|
|
353
|
+
expect(approveArgs[0].toLowerCase()).toBe(escrowContract.toLowerCase())
|
|
354
|
+
expect(approveArgs[1]).toBe(10_000_000n)
|
|
355
|
+
|
|
356
|
+
expect(openCall.to?.toLowerCase()).toBe(escrowContract.toLowerCase())
|
|
357
|
+
expect(open.functionName).toBe('open')
|
|
358
|
+
expect(openArgs[0].toLowerCase()).toBe(payee.toLowerCase())
|
|
359
|
+
expect(openArgs[1].toLowerCase()).toBe(asset.toLowerCase())
|
|
360
|
+
expect(openArgs[2]).toBe(10_000_000n)
|
|
361
|
+
expect(openArgs[3]).toEqual(expect.any(String))
|
|
362
|
+
expect(openArgs[4].toLowerCase()).toBe(payer.address.toLowerCase())
|
|
363
|
+
})
|
|
364
|
+
|
|
309
365
|
test('maxDeposit alone', async () => {
|
|
310
366
|
const method = session({
|
|
311
367
|
getClient: () => client,
|
|
@@ -131,11 +131,11 @@ export function session(parameters: session.Parameters = {}) {
|
|
|
131
131
|
|
|
132
132
|
const deposit = (() => {
|
|
133
133
|
if (context?.depositRaw) return BigInt(context.depositRaw)
|
|
134
|
+
if (parameters.deposit !== undefined) return parseUnits(parameters.deposit, decimals)
|
|
134
135
|
if (suggestedDeposit !== undefined && maxDeposit !== undefined)
|
|
135
136
|
return suggestedDeposit < maxDeposit ? suggestedDeposit : maxDeposit
|
|
136
|
-
if (suggestedDeposit !== undefined) return suggestedDeposit
|
|
137
137
|
if (maxDeposit !== undefined) return maxDeposit
|
|
138
|
-
if (
|
|
138
|
+
if (suggestedDeposit !== undefined) return suggestedDeposit
|
|
139
139
|
throw new Error(
|
|
140
140
|
'No deposit amount available. Set `deposit`, `maxDeposit`, or ensure the server challenge includes `suggestedDeposit`.',
|
|
141
141
|
)
|