Skip to content
Draft
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
23 changes: 20 additions & 3 deletions packages/evm-wallet-experiment/src/lib/delegation-twin.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
import {
matches,
getInterfaceGuardPayload,
getMethodGuardPayload,
} from '@endo/patterns';
import type { InterfaceGuard, MethodGuard, Pattern } from '@endo/patterns';
import { describe, expect, it, vi } from 'vitest';

import type {
Expand Down Expand Up @@ -122,11 +128,11 @@ describe('makeDelegationTwin', () => {
});

describe('transferFungible twin', () => {
it('normalizes checksummed token address to lowercase in section.token', () => {
it('normalizes checksummed token address to lowercase in transferFungible guard', () => {
const CHECKSUMMED_TOKEN =
'0xAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAaAa' as Address;
const redeemFn = vi.fn().mockResolvedValue(TX_HASH);
const section = makeDelegationTwin({
makeDelegationTwin({
grant: {
method: 'transferFungible',
token: CHECKSUMMED_TOKEN,
Expand All @@ -135,7 +141,18 @@ describe('makeDelegationTwin', () => {
},
redeemFn,
});
expect(section.token).toBe(CHECKSUMMED_TOKEN.toLowerCase());
// The guard's first arg for transferFungible is M.eq(token.toLowerCase()).
// If the token is not normalized here, incoming calls with the canonical
// lowercase address would be rejected by the guard at dispatch time.
const ifacePayload = getInterfaceGuardPayload(
lastInterfaceGuard as InterfaceGuard,
) as unknown as { methodGuards: Record<string, MethodGuard> };
const methodPayload = getMethodGuardPayload(
ifacePayload.methodGuards.transferFungible,
) as unknown as { argGuards: Pattern[] };
const tokenGuard = methodPayload.argGuards[0];
expect(matches(CHECKSUMMED_TOKEN.toLowerCase(), tokenGuard)).toBe(true);
expect(matches(CHECKSUMMED_TOKEN, tokenGuard)).toBe(false);
});

it('exposes transferFungible method', () => {
Expand Down
22 changes: 14 additions & 8 deletions packages/evm-wallet-experiment/src/lib/delegation-twin.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
import { M } from '@endo/patterns';
import { makeDiscoverableExo } from '@metamask/kernel-utils/discoverable';
import { constant } from '@metamask/kernel-utils/sheaf';
import type { PresheafSection } from '@metamask/kernel-utils/sheaf';

import { encodeTransfer } from './erc20.ts';
import { METHOD_CATALOG } from './method-catalog.ts';
import type { Address, DelegationGrant, Execution, Hex } from '../types.ts';

export type DelegationSection =
| { exo: object; method: 'transferNative' }
| { exo: object; method: 'transferFungible'; token: Address };
type AwayMetadata = { mode: string; delegationId?: string };

type DelegationTwinOptions = {
grant: DelegationGrant;
redeemFn: (execution: Execution) => Promise<Hex>;
};

/**
* Build a DelegationSection for a delegation grant.
* Build a PresheafSection for a delegation grant.
* The resulting exo exposes the method covered by the grant, enforcing
* local guards and (for transferFungible) a local budget tracker.
*
* @param options - Twin construction options.
* @param options.grant - The semantic delegation grant to wrap.
* @param options.redeemFn - Submits an Execution to the delegation framework.
* @returns A DelegationSection wrapping the delegation exo.
* @returns A PresheafSection with constant delegation metadata.
*/
export function makeDelegationTwin(
options: DelegationTwinOptions,
): DelegationSection {
): PresheafSection<AwayMetadata> {
const { grant, redeemFn } = options;
const { delegation } = grant;
const idPrefix = delegation.id.slice(0, 12);
Expand Down Expand Up @@ -90,7 +90,10 @@ export function makeDelegationTwin(
interfaceGuard,
);

return { exo, method: 'transferNative' };
return {
exo,
metadata: constant({ mode: 'delegation', delegationId: delegation.id }),
};
}

// transferFungible — normalize token address to lowercase for consistent matching.
Expand Down Expand Up @@ -152,5 +155,8 @@ export function makeDelegationTwin(
interfaceGuard,
);

return { exo, method: 'transferFungible', token };
return {
exo,
metadata: constant({ mode: 'delegation', delegationId: delegation.id }),
};
}
Loading
Loading