Skip to content

Commit 04bdddf

Browse files
committed
feat: contract utils for ERC20TransferAmountEnforcer
1 parent a22ecb5 commit 04bdddf

9 files changed

Lines changed: 1188 additions & 5 deletions

File tree

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import * as read from './read';
2+
3+
export { read };
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { ERC20TransferAmountEnforcer } from '@metamask/delegation-abis';
2+
import type { Address, Client, Hex } from 'viem';
3+
import { readContract } from 'viem/actions';
4+
5+
export type ReadGetSpentAmountParameters = {
6+
client: Client;
7+
contractAddress: Address;
8+
delegationManager: Address;
9+
delegationHash: Hex;
10+
};
11+
12+
export const read = async ({
13+
client,
14+
contractAddress,
15+
delegationManager,
16+
delegationHash,
17+
}: ReadGetSpentAmountParameters) => {
18+
const amount = await readContract(client, {
19+
address: contractAddress,
20+
abi: ERC20TransferAmountEnforcer.abi,
21+
functionName: 'spentMap',
22+
args: [delegationManager, delegationHash],
23+
});
24+
25+
return amount;
26+
};
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { ERC20TransferAmountEnforcer } from '@metamask/delegation-abis';
2+
import type { Address, Client, Hex } from 'viem';
3+
import { readContract } from 'viem/actions';
4+
5+
export type ReadGetTermsInfoParameters = {
6+
client: Client;
7+
contractAddress: Address;
8+
terms: Hex;
9+
};
10+
11+
export const read = async ({
12+
client,
13+
contractAddress,
14+
terms,
15+
}: ReadGetTermsInfoParameters) => {
16+
const [allowedContract, maxTokens] = await readContract(client, {
17+
address: contractAddress,
18+
abi: ERC20TransferAmountEnforcer.abi,
19+
functionName: 'getTermsInfo',
20+
args: [terms],
21+
});
22+
23+
return {
24+
allowedContract,
25+
maxTokens,
26+
};
27+
};
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { read as getSpentAmount } from './methods/getSpentAmount';
2+
import { read as getTermsInfo } from './methods/getTermsInfo';
3+
4+
export { getTermsInfo, getSpentAmount };

packages/delegation-toolkit/src/contracts/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import * as EIP712 from '../DelegationFramework/EIP712';
44
import * as EntryPoint from '../DelegationFramework/EntryPoint';
55
import * as ERC20PeriodTransferEnforcer from '../DelegationFramework/ERC20PeriodTransferEnforcer';
66
import * as ERC20StreamingEnforcer from '../DelegationFramework/ERC20StreamingEnforcer';
7+
import * as ERC20TransferAmountEnforcer from '../DelegationFramework/ERC20TransferAmountEnforcer';
78
import * as HybridDeleGator from '../DelegationFramework/HybridDeleGator';
89
import * as MultiSigDeleGator from '../DelegationFramework/MultiSigDeleGator';
910
import * as MultiTokenPeriodEnforcer from '../DelegationFramework/MultiTokenPeriodEnforcer';
@@ -32,9 +33,10 @@ export {
3233
Pausable,
3334
SimpleFactory,
3435
ERC20PeriodTransferEnforcer,
36+
ERC20StreamingEnforcer,
37+
ERC20TransferAmountEnforcer,
3538
MultiTokenPeriodEnforcer,
3639
NativeTokenPeriodTransferEnforcer,
37-
ERC20StreamingEnforcer,
3840
NativeTokenStreamingEnforcer,
3941
};
4042

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
import { createPublicClient, http, type Address, type Hex } from 'viem';
2+
import { sepolia } from 'viem/chains';
3+
import { describe, it, expect, vi, beforeEach } from 'vitest';
4+
5+
import * as ERC20TransferAmountEnforcer from '../../../src/DelegationFramework/ERC20TransferAmountEnforcer';
6+
import { randomAddress, randomBytes } from '../../utils';
7+
8+
// Helper function to generate random bytes32
9+
const randomBytes32 = (): Hex => randomBytes(32);
10+
11+
// Mock the readContract function
12+
vi.mock('viem/actions', () => ({
13+
readContract: vi.fn(),
14+
}));
15+
16+
describe('ERC20TransferAmountEnforcer read functions', () => {
17+
let client: any;
18+
let contractAddress: Address;
19+
let delegationManager: Address;
20+
let delegationHash: Hex;
21+
let terms: Hex;
22+
23+
beforeEach(() => {
24+
client = createPublicClient({
25+
chain: sepolia,
26+
transport: http(),
27+
});
28+
contractAddress = randomAddress();
29+
delegationManager = randomAddress();
30+
delegationHash = randomBytes32();
31+
terms =
32+
'0x1234567890123456789012345678901234567890000000000000000000000000000000000000000000000000000000000001';
33+
34+
vi.clearAllMocks();
35+
});
36+
37+
describe('getTermsInfo', () => {
38+
it('should call readContract with correct parameters and return terms info', async () => {
39+
const mockAllowedContract = randomAddress();
40+
const mockMaxTokens = 1000000n;
41+
42+
const { readContract } = await import('viem/actions');
43+
vi.mocked(readContract).mockResolvedValue([
44+
mockAllowedContract,
45+
mockMaxTokens,
46+
]);
47+
48+
const result = await ERC20TransferAmountEnforcer.read.getTermsInfo({
49+
client,
50+
contractAddress,
51+
terms,
52+
});
53+
54+
expect(readContract).toHaveBeenCalledWith(client, {
55+
address: contractAddress,
56+
abi: expect.any(Array),
57+
functionName: 'getTermsInfo',
58+
args: [terms],
59+
});
60+
61+
expect(result).toEqual({
62+
allowedContract: mockAllowedContract,
63+
maxTokens: mockMaxTokens,
64+
});
65+
});
66+
67+
it('should handle different terms values', async () => {
68+
const mockAllowedContract = randomAddress();
69+
const mockMaxTokens = 500000n;
70+
const differentTerms =
71+
'0xabcdefabcdefabcdefabcdefabcdefabcdefabcd000000000000000000000000000000000000000000000000000000000000002';
72+
73+
const { readContract } = await import('viem/actions');
74+
vi.mocked(readContract).mockResolvedValue([
75+
mockAllowedContract,
76+
mockMaxTokens,
77+
]);
78+
79+
const result = await ERC20TransferAmountEnforcer.read.getTermsInfo({
80+
client,
81+
contractAddress,
82+
terms: differentTerms,
83+
});
84+
85+
expect(readContract).toHaveBeenCalledWith(client, {
86+
address: contractAddress,
87+
abi: expect.any(Array),
88+
functionName: 'getTermsInfo',
89+
args: [differentTerms],
90+
});
91+
92+
expect(result).toEqual({
93+
allowedContract: mockAllowedContract,
94+
maxTokens: mockMaxTokens,
95+
});
96+
});
97+
});
98+
99+
describe('getSpentAmount', () => {
100+
it('should call readContract with correct parameters and return spent amount', async () => {
101+
const mockSpentAmount = 250000n;
102+
103+
const { readContract } = await import('viem/actions');
104+
vi.mocked(readContract).mockResolvedValue(mockSpentAmount);
105+
106+
const result = await ERC20TransferAmountEnforcer.read.getSpentAmount({
107+
client,
108+
contractAddress,
109+
delegationManager,
110+
delegationHash,
111+
});
112+
113+
expect(readContract).toHaveBeenCalledWith(client, {
114+
address: contractAddress,
115+
abi: expect.any(Array),
116+
functionName: 'spentMap',
117+
args: [delegationManager, delegationHash],
118+
});
119+
120+
expect(result).toBe(mockSpentAmount);
121+
});
122+
123+
it('should handle zero spent amount', async () => {
124+
const mockSpentAmount = 0n;
125+
126+
const { readContract } = await import('viem/actions');
127+
vi.mocked(readContract).mockResolvedValue(mockSpentAmount);
128+
129+
const result = await ERC20TransferAmountEnforcer.read.getSpentAmount({
130+
client,
131+
contractAddress,
132+
delegationManager,
133+
delegationHash,
134+
});
135+
136+
expect(readContract).toHaveBeenCalledWith(client, {
137+
address: contractAddress,
138+
abi: expect.any(Array),
139+
functionName: 'spentMap',
140+
args: [delegationManager, delegationHash],
141+
});
142+
143+
expect(result).toBe(mockSpentAmount);
144+
});
145+
146+
it('should handle different delegation parameters', async () => {
147+
const differentDelegationManager = randomAddress();
148+
const differentDelegationHash = randomBytes32();
149+
const mockSpentAmount = 750000n;
150+
151+
const { readContract } = await import('viem/actions');
152+
vi.mocked(readContract).mockResolvedValue(mockSpentAmount);
153+
154+
const result = await ERC20TransferAmountEnforcer.read.getSpentAmount({
155+
client,
156+
contractAddress,
157+
delegationManager: differentDelegationManager,
158+
delegationHash: differentDelegationHash,
159+
});
160+
161+
expect(readContract).toHaveBeenCalledWith(client, {
162+
address: contractAddress,
163+
abi: expect.any(Array),
164+
functionName: 'spentMap',
165+
args: [differentDelegationManager, differentDelegationHash],
166+
});
167+
168+
expect(result).toBe(mockSpentAmount);
169+
});
170+
});
171+
172+
describe('API structure', () => {
173+
it('should export the expected functions', () => {
174+
expect(ERC20TransferAmountEnforcer.read.getTermsInfo).toBeDefined();
175+
expect(ERC20TransferAmountEnforcer.read.getSpentAmount).toBeDefined();
176+
expect(typeof ERC20TransferAmountEnforcer.read.getTermsInfo).toBe(
177+
'function',
178+
);
179+
expect(typeof ERC20TransferAmountEnforcer.read.getSpentAmount).toBe(
180+
'function',
181+
);
182+
});
183+
});
184+
});

packages/delegation-toolkit/test/actions/caveatEnforcerClient.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,10 @@ describe('Caveat Enforcer Client', () => {
8080
},
8181
caveatEnforcers: {
8282
ERC20PeriodTransferEnforcer: randomAddress(),
83+
ERC20StreamingEnforcer: randomAddress(),
84+
ERC20TransferAmountEnforcer: randomAddress(),
8385
MultiTokenPeriodEnforcer: randomAddress(),
8486
NativeTokenPeriodTransferEnforcer: randomAddress(),
85-
ERC20StreamingEnforcer: randomAddress(),
8687
NativeTokenStreamingEnforcer: randomAddress(),
8788
// Add other enforcers as needed
8889
},

0 commit comments

Comments
 (0)