Skip to content

Commit

Permalink
Change all input types to objects (no single values) (#40)
Browse files Browse the repository at this point in the history
* Change all input types to objects (no single values)

* Add utilities for working with object types

* Add new types to exports

* Update types and util usage in binding packages

* Add changeset

* Update util info in readme
  • Loading branch information
ryangoree authored Feb 16, 2024
1 parent 7a7ce5e commit cc17b3c
Show file tree
Hide file tree
Showing 21 changed files with 395 additions and 208 deletions.
7 changes: 7 additions & 0 deletions .changeset/long-boats-hide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@delvtech/evm-client-ethers": minor
"@delvtech/evm-client-viem": minor
"@delvtech/evm-client": minor
---

Changed the type of all inputs to objects. This means that functions with a single argument (e.g., `balanceOf` will now expect ```{ owner: `0x${string}` }```, not ``` `0x${string}` ```). Outputs remain the "Friendly" type which deconstructs to a single primitive type for single outputs values (e.g., `symbol` will return a `string`, not `{ "0": string }`) since many single output return values are unnamed
19 changes: 10 additions & 9 deletions packages/evm-client-ethers/src/contract/createReadContract.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import {
AbiEntryNotFoundError,
arrayToFriendly,
DecodedFunctionData,
friendlyToArray,
FunctionName,
ReadContract,
ReadWriteContract,
arrayToFriendly,
arrayToObject,
objectToArray,
} from '@delvtech/evm-client';
import { Abi } from 'abitype';
import { Contract, EventLog, InterfaceAbi, Provider, Signer } from 'ethers';
Expand Down Expand Up @@ -52,7 +53,7 @@ export function createReadContract<TAbi extends Abi = Abi>({
},

async read(functionName, args, options) {
const argsArray: any[] = friendlyToArray({
const argsArray: any[] = objectToArray({
abi: abi as Abi,
type: 'function',
name: functionName,
Expand Down Expand Up @@ -81,7 +82,7 @@ export function createReadContract<TAbi extends Abi = Abi>({
},

async simulateWrite(functionName, args, options) {
const argsArray: any[] = friendlyToArray({
const argsArray: any[] = objectToArray({
abi: abi as Abi,
type: 'function',
name: functionName,
Expand Down Expand Up @@ -110,7 +111,7 @@ export function createReadContract<TAbi extends Abi = Abi>({
},

async getEvents(eventName, options) {
const filterValues = friendlyToArray({
const filterValues = objectToArray({
// Cast to allow any array type for values
abi: abi as Abi,
type: 'event',
Expand All @@ -127,7 +128,7 @@ export function createReadContract<TAbi extends Abi = Abi>({
)) as EventLog[];

return events.map(({ blockNumber, data, transactionHash, args }) => {
const friendlyArgs = arrayToFriendly({
const objectArgs = arrayToObject({
// Cast to allow any array type for values
abi: abi as Abi,
type: 'event',
Expand All @@ -137,7 +138,7 @@ export function createReadContract<TAbi extends Abi = Abi>({
});

return {
args: friendlyArgs,
args: objectArgs,
blockNumber: BigInt(blockNumber),
data: data as `0x${string}`,
eventName,
Expand All @@ -147,7 +148,7 @@ export function createReadContract<TAbi extends Abi = Abi>({
},

encodeFunctionData(functionName, args) {
const arrayArgs = friendlyToArray({
const arrayArgs = objectToArray({
// Cast to allow any array type for values
abi: abi as Abi,
type: 'function',
Expand Down Expand Up @@ -183,7 +184,7 @@ export function createReadContract<TAbi extends Abi = Abi>({
}

return {
args: arrayToFriendly({
args: arrayToObject({
abi: abi as Abi,
type: 'function',
name: parsed.name,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
friendlyToArray,
objectToArray,
ReadContract,
ReadWriteContract,
} from '@delvtech/evm-client';
Expand Down Expand Up @@ -41,7 +41,7 @@ export function createReadWriteContract<TAbi extends Abi = Abi>({
},

async write(functionName, args, options) {
const argsArray = friendlyToArray({
const argsArray = objectToArray({
abi: abi as Abi,
type: 'function',
name: functionName,
Expand Down
5 changes: 4 additions & 1 deletion packages/evm-client-ethers/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,17 @@ export * from '@delvtech/evm-client/cache';

export {
arrayToFriendly,
friendlyToArray,
arrayToObject,
getAbiEntry,
objectToArray,
} from '@delvtech/evm-client/contract';
export type {
AbiArrayType,
AbiEntry,
AbiEntryName,
AbiFriendlyType,
AbiObjectType,
AbiParameters,
CachedReadContract,
CachedReadWriteContract,
ContractDecodeFunctionDataArgs,
Expand Down
26 changes: 13 additions & 13 deletions packages/evm-client-viem/src/contract/createReadContract.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import {
AbiObjectType,
DecodedFunctionData,
Event,
FunctionName,
FunctionReturn,
ReadContract,
ReadWriteContract,
arrayToFriendly,
friendlyToArray,
arrayToObject,
objectToArray,
} from '@delvtech/evm-client';
import { createSimulateContractParameters } from 'src/contract/utils/createSimulateContractParameters';
import {
Expand Down Expand Up @@ -58,7 +58,7 @@ export function createReadContract<TAbi extends Abi = Abi>({
},

async read(functionName, args, options) {
const argsArray = friendlyToArray({
const argsArray = objectToArray({
abi,
type: 'function',
name: functionName,
Expand All @@ -82,7 +82,7 @@ export function createReadContract<TAbi extends Abi = Abi>({
},

async simulateWrite(functionName, args, options) {
const argsArray = friendlyToArray({
const argsArray = objectToArray({
abi,
type: 'function',
name: functionName,
Expand Down Expand Up @@ -118,28 +118,28 @@ export function createReadContract<TAbi extends Abi = Abi>({
const events = await publicClient.getFilterLogs({ filter });

return events.map(({ args, blockNumber, data, transactionHash }) => {
const friendlyArgs = Array.isArray(args)
? arrayToFriendly({
const objectArgs = Array.isArray(args)
? arrayToObject({
abi: abi as Abi,
type: 'event',
name: eventName,
kind: 'inputs',
values: args as readonly unknown[],
values: args,
})
: args;
: (args as AbiObjectType<TAbi, 'event', typeof eventName, 'inputs'>);

return {
args: friendlyArgs,
args: objectArgs,
blockNumber: blockNumber ?? undefined,
data,
eventName,
transactionHash: transactionHash ?? undefined,
};
}) as Event<TAbi, typeof eventName>[];
});
},

encodeFunctionData(functionName, args) {
const arrayArgs = friendlyToArray({
const arrayArgs = objectToArray({
abi: abi,
type: 'function',
name: functionName,
Expand All @@ -165,7 +165,7 @@ export function createReadContract<TAbi extends Abi = Abi>({
const arrayArgs = Array.isArray(args) ? args : [args];

return {
args: arrayToFriendly({
args: arrayToObject({
// Cast to allow any array type for values
abi: abi as Abi,
type: 'function',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
friendlyToArray,
objectToArray,
ReadContract,
ReadWriteContract,
} from '@delvtech/evm-client';
Expand Down Expand Up @@ -50,7 +50,7 @@ export function createReadWriteContract<TAbi extends Abi = Abi>({
async write(functionName, args, options) {
const [account] = await walletClient.getAddresses();

const arrayArgs = friendlyToArray({
const arrayArgs = objectToArray({
abi: abi,
type: 'function',
name: functionName,
Expand Down
5 changes: 4 additions & 1 deletion packages/evm-client-viem/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,17 @@ export * from '@delvtech/evm-client/cache';

export {
arrayToFriendly,
friendlyToArray,
arrayToObject,
getAbiEntry,
objectToArray,
} from '@delvtech/evm-client/contract';
export type {
AbiArrayType,
AbiEntry,
AbiEntryName,
AbiFriendlyType,
AbiObjectType,
AbiParameters,
CachedReadContract,
CachedReadWriteContract,
ContractDecodeFunctionDataArgs,
Expand Down
27 changes: 15 additions & 12 deletions packages/evm-client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,22 +62,25 @@ The API is meant to be easy to both read and write.

#### Utils

- **[`objectToArray`](./src/contract/utils/friendlyToArray.ts):** A function
that takes an object of inputs (function and event arguments) and converts it
into an array, ensuring parameters are properly ordered and the correct number
of parameters are present.

- **[`arrayToObject`](./src/contract/utils/arrayToFriendly.ts):** The opposite
of `objectToArray`. A function to transform contract input and output arrays
into objects.

- **[`arrayToFriendly`](./src/contract/utils/arrayToFriendly.ts):** A function
to transform contract input and output arrays into "Friendly" types. The
friendly type of an input/output array depends on the number of input/output
parameters:
to transform contract output arrays into "Friendly" types. The friendly type
of an output array depends on the number of output parameters:

- Multiple parameters: An object with the argument names as keys (or their
index if no name is found in the ABI) and the primitive type of the
parameters as values.
- Single parameters: The primitive type of the single parameter.
- No parameters: `undefined`

- **[`friendlyToArray`](./src/contract/utils/friendlyToArray.ts):** The opposite
of `arrayToFriendly`. A function that takes a "Friendly" type and converts it
into an array, ensuring parameters are properly ordered and the correct number
of parameters are present.

#### Factories

- **[`createCachedReadContract`](./src/contract/factories/createCachedReadContract.ts):**
Expand All @@ -103,13 +106,13 @@ information like blocks and transactions.

#### Stubs

- **[`NetworkStub`](./src/network/stubs/NetworkStub.ts):** A stub of a
`Network` for use in tests.
- **[`NetworkStub`](./src/network/stubs/NetworkStub.ts):** A stub of a `Network`
for use in tests.

### SimpleCache

A simple cache abstraction providing a minimal interface for facilitating contract
caching.
A simple cache abstraction providing a minimal interface for facilitating
contract caching.

#### Types

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,14 @@ describe('createCachedReadContract', () => {
const stubbedValue = 100n;
contract.stubRead({ functionName: 'balanceOf', value: stubbedValue });

const value = await cachedContract.read('balanceOf', '0x123abc');
const value = await cachedContract.read('balanceOf', { owner: '0x123abc' });
expect(value).toBe(stubbedValue);

cachedContract.deleteRead('balanceOf', '0x123abc');
cachedContract.deleteRead('balanceOf', { owner: '0x123abc' });

const value2 = await cachedContract.read('balanceOf', '0x123abc');
const value2 = await cachedContract.read('balanceOf', {
owner: '0x123abc',
});
expect(value2).toBe(stubbedValue);

const stub = contract.getReadStub('balanceOf');
Expand All @@ -81,18 +83,17 @@ describe('createCachedReadContract', () => {
const cachedContract = createCachedReadContract({ contract });

contract.stubRead({ functionName: 'balanceOf', value: 100n });
const stubbedValue = '0x123abc';
contract.stubRead({
functionName: 'name',
value: stubbedValue,
value: 'Base Token',
});
2;
await cachedContract.read('balanceOf', '0x123abc');

await cachedContract.read('balanceOf', { owner: '0x123abc' });
await cachedContract.read('name');

cachedContract.clearCache();

await cachedContract.read('balanceOf', '0x123abc');
await cachedContract.read('balanceOf', { owner: '0x123abc' });
await cachedContract.read('name');

const stubA = contract.getReadStub('balanceOf');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ export function createCachedReadWriteContract<TAbi extends Abi = Abi>({
};
}

function isCached(
contract: ReadWriteContract | CachedReadWriteContract,
): contract is CachedReadWriteContract {
function isCached<TAbi extends Abi>(
contract: ReadWriteContract<TAbi> | CachedReadWriteContract<TAbi>,
): contract is CachedReadWriteContract<TAbi> {
return 'clearCache' in contract;
}
18 changes: 10 additions & 8 deletions packages/evm-client/src/contract/stubs/ReadContractStub.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,30 +38,32 @@ describe('ReadContractStub', () => {
it('stubs the read function', async () => {
const contract = new ReadContractStub(IERC20.abi);

expect(contract.read('balanceOf', NANCY)).rejects.toThrowError();
expect(contract.read('balanceOf', { owner: NANCY })).rejects.toThrowError();

// Stub bob and alice's balances first
const bobValue = 10n;
contract.stubRead({
functionName: 'balanceOf',
args: BOB,
args: { owner: BOB },
value: bobValue,
});

const aliceValue = 20n;
contract.stubRead({
functionName: 'balanceOf',
args: ALICE,
args: { owner: ALICE },
value: aliceValue,
// options can be specfied as well
options: { blockNumber: 10n },
});

// Now try and read them based on their args
const bobResult = await contract.read('balanceOf', BOB);
const aliceResult = await contract.read('balanceOf', ALICE, {
blockNumber: 10n,
});
const bobResult = await contract.read('balanceOf', { owner: BOB });
const aliceResult = await contract.read(
'balanceOf',
{ owner: ALICE },
{ blockNumber: 10n },
);
expect(bobResult).toBe(bobValue);
expect(aliceResult).toBe(aliceValue);

Expand All @@ -71,7 +73,7 @@ describe('ReadContractStub', () => {
functionName: 'balanceOf',
value: defaultValue,
});
const defaultResult = await contract.read('balanceOf', NANCY);
const defaultResult = await contract.read('balanceOf', { owner: NANCY });
expect(defaultResult).toBe(defaultValue);

const stub = contract.getReadStub('balanceOf');
Expand Down
Loading

0 comments on commit cc17b3c

Please sign in to comment.