Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changelog/autoswap-access-key.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
tempo-wallet: patch
---

Authorize both USDC.e and PathUSD spending for mainnet CLI access keys so paid
clients can atomically swap PathUSD into a service's requested currency.
4 changes: 2 additions & 2 deletions src/commands/identity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export async function loginHandler(options: {
network: options.network,
noBrowser: options.browser === false,
});
const result = await connect(provider);
const result = await connect(provider, options.network);

return {
accounts: result.accounts.map((account) => account.address),
Expand All @@ -61,7 +61,7 @@ export async function loginHandler(options: {
export async function refreshHandler(options: { network?: string | undefined }) {
console.error(`Auth URL: ${refreshAuthUrl(options.network)}`);
const provider = createProvider({ network: options.network });
const result = await connect(provider);
const result = await connect(provider, options.network);

return {
accounts: result.accounts.map((account) => account.address),
Expand Down
11 changes: 7 additions & 4 deletions src/commands/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@ async function payAndRetryRequest(
payment.onChallengeReceived(async ({ challenge, createCredential }) => {
enforceMaxSpend(challenge, options);
if (provider && providerState!.store.getState().accounts.length === 0)
await ensureProviderAccounts(provider);
await ensureProviderAccounts(provider, options.network);
return await createCredential(paymentContext(challenge, options) as never);
});

Expand Down Expand Up @@ -670,7 +670,7 @@ export async function resolvePaymentIdentity(options: RequestOptions) {
const providerState = provider as unknown as {
store: { getState(): { accounts: { address: string }[]; activeAccount: number } };
};
await ensureProviderAccounts(provider);
await ensureProviderAccounts(provider, options.network);
const getClient = ({ chainId }: { chainId?: number | undefined }) => {
const client = provider.getClient({ chainId });
const state = providerState.store.getState();
Expand Down Expand Up @@ -699,10 +699,13 @@ export async function resolvePaymentIdentity(options: RequestOptions) {
};
}

async function ensureProviderAccounts(provider: Parameters<typeof connect>[0]) {
async function ensureProviderAccounts(
provider: Parameters<typeof connect>[0],
network?: string | undefined,
) {
const accounts = (await provider.request({ method: "eth_accounts" })) as unknown[];
if (accounts.length > 0) return;
await connect(provider);
await connect(provider, network);
}

export async function storedAccessKeyIdentity(walletState: WalletState, options: RequestOptions) {
Expand Down
10 changes: 9 additions & 1 deletion src/provider.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type { Provider as CoreProvider } from "accounts";
import { Provider, Storage } from "accounts/cli";
import { parseUnits, toHex } from "viem";

import { openExternal } from "./shared/process.js";
import { moderatoToken, usdcToken } from "./shared/constants.js";

export const accessKeyAuthorizationSeconds = 30 * 86_400;

Expand All @@ -23,14 +25,20 @@ export function createProvider(
});
}

export async function connect(provider: CoreProvider.Provider) {
export function accessKeyLimits(network: string | undefined) {
const tokens = network === "testnet" ? [moderatoToken] : [usdcToken, moderatoToken];
return tokens.map((token) => ({ limit: toHex(parseUnits("100", 6)), token }));
}

export async function connect(provider: CoreProvider.Provider, network?: string | undefined) {
return provider.request({
method: "wallet_connect",
params: [
{
capabilities: {
authorizeAccessKey: {
expiry: Math.floor(Date.now() / 1000) + accessKeyAuthorizationSeconds,
limits: accessKeyLimits(network),
},
},
},
Expand Down
29 changes: 29 additions & 0 deletions test/identity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -627,10 +627,39 @@ limit = "100000000"
capabilities: {
authorizeAccessKey: {
expiry: Math.floor(Date.now() / 1000) + accessKeyAuthorizationSeconds,
limits: [
{ limit: "0x5f5e100", token: "0x20c000000000000000000000b9537d11c60e8b50" },
{ limit: "0x5f5e100", token: "0x20c0000000000000000000000000000000000000" },
],
},
},
},
],
});
});

it("requests only PathUSD spending permission on testnet", async () => {
const request = vi.fn().mockResolvedValue({ accounts: [] });

await connect({ request } as never, "testnet");

expect(request).toHaveBeenCalledWith(
expect.objectContaining({
params: [
expect.objectContaining({
capabilities: {
authorizeAccessKey: expect.objectContaining({
limits: [
{
limit: "0x5f5e100",
token: "0x20c0000000000000000000000000000000000000",
},
],
}),
},
}),
],
}),
);
});
});