@woof-software/contracts-tools-sdk-ethers 0.0.8 → 0.0.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/lib/contract/base-contract.d.ts +1 -0
- package/lib/contract/base-contract.js +40 -4
- package/lib/errors/contracts-errors.d.ts +1 -0
- package/lib/errors/contracts-errors.js +1 -0
- package/lib/helpers/is-signer.d.ts +2 -2
- package/lib/helpers/is-signer.js +2 -1
- package/lib/helpers/priority-call.d.ts +3 -4
- package/lib/helpers/priority-call.js +15 -9
- package/lib/multicall/multicall-unit.d.ts +3 -0
- package/lib/multicall/multicall-unit.js +69 -29
- package/lib/utils/index.d.ts +0 -1
- package/lib/utils/index.js +0 -1
- package/package.json +3 -2
|
@@ -14,6 +14,7 @@ export declare class BaseContract {
|
|
|
14
14
|
get signer(): Signer | null;
|
|
15
15
|
get interface(): Interface;
|
|
16
16
|
call<T>(method: string, args?: any[], options?: ContractCallOptions): Promise<Awaited<T>>;
|
|
17
|
+
estimate(method: string, args?: any[], options?: ContractCallOptions): Promise<bigint>;
|
|
17
18
|
getCall(methodName: string, args?: any[], callData?: {}): ContractCall;
|
|
18
19
|
listenEvent(eventName: string, listener: Listener): Promise<EthersContract>;
|
|
19
20
|
getLogs(fromBlock: number, eventsNames?: string[], toBlock?: number, options?: ContractGetLogsOptions): Promise<LogDescription[]>;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { Contract as EthersContract,
|
|
1
|
+
import { Contract as EthersContract, WebSocketProvider, } from "ethers";
|
|
2
2
|
import { config } from "../config.js";
|
|
3
3
|
import { CONTRACTS_ERRORS } from "../errors/index.js";
|
|
4
|
-
import { isSigner, isStaticMethod } from "../helpers/index.js";
|
|
4
|
+
import { isSigner, isStaticMethod, priorityCall, priorityCallEstimate, } from "../helpers/index.js";
|
|
5
5
|
import { CallMutability, } from "../types/index.js";
|
|
6
|
-
import { checkSignals, createTimeoutSignal,
|
|
6
|
+
import { checkSignals, createTimeoutSignal, raceWithSignals, waitWithSignals, } from "../utils/index.js";
|
|
7
7
|
import { contractCreateCallName } from "./contract-create-call-name.js";
|
|
8
8
|
export class BaseContract {
|
|
9
9
|
static createAutoClass(abi, address, driver, options) {
|
|
@@ -43,7 +43,7 @@ export class BaseContract {
|
|
|
43
43
|
this.address = address;
|
|
44
44
|
this.driver = driver;
|
|
45
45
|
this.isCallable = !!address && !!driver;
|
|
46
|
-
this.isReadonly = !this.isCallable || !(driver
|
|
46
|
+
this.isReadonly = !this.isCallable || !isSigner(driver);
|
|
47
47
|
this.contract = new EthersContract(address, abi, driver);
|
|
48
48
|
this.contractOptions = {
|
|
49
49
|
staticCallsTimeoutMs: config.contract.staticCalls.timeoutMs,
|
|
@@ -107,6 +107,42 @@ export class BaseContract {
|
|
|
107
107
|
return tx;
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
|
+
async estimate(method, args = [], options = {}) {
|
|
111
|
+
if (!this.isCallable)
|
|
112
|
+
throw CONTRACTS_ERRORS.NON_CALLABLE_CONTRACT_INVOCATION;
|
|
113
|
+
const methodFn = this.contract[method];
|
|
114
|
+
if (!methodFn)
|
|
115
|
+
throw CONTRACTS_ERRORS.METHOD_NOT_DEFINED(method);
|
|
116
|
+
const functionFragment = this.contract.interface.getFunction(method);
|
|
117
|
+
if (!functionFragment)
|
|
118
|
+
throw CONTRACTS_ERRORS.FRAGMENT_NOT_DEFINED(method);
|
|
119
|
+
if (isStaticMethod(functionFragment.stateMutability))
|
|
120
|
+
throw CONTRACTS_ERRORS.ESTIMATE_STATIC_CALL(method);
|
|
121
|
+
const callOptions = {
|
|
122
|
+
highPriorityTx: this.contractOptions.highPriorityTxs,
|
|
123
|
+
priorityOptions: this.contractOptions.priorityOptions,
|
|
124
|
+
...options,
|
|
125
|
+
};
|
|
126
|
+
const localSignals = [];
|
|
127
|
+
if (callOptions.signals)
|
|
128
|
+
localSignals.push(...callOptions.signals);
|
|
129
|
+
if (callOptions.timeoutMs)
|
|
130
|
+
localSignals.push(this.getTimeoutSignal(false, callOptions.timeoutMs));
|
|
131
|
+
if (this.isReadonly)
|
|
132
|
+
throw CONTRACTS_ERRORS.READ_ONLY_CONTRACT_MUTATION;
|
|
133
|
+
let estimate;
|
|
134
|
+
if (callOptions.highPriorityTx) {
|
|
135
|
+
const provider = this.driver?.provider;
|
|
136
|
+
estimate = await raceWithSignals(() => priorityCallEstimate(provider, this.driver, this.contract, method, args, {
|
|
137
|
+
signals: localSignals,
|
|
138
|
+
...options.priorityOptions,
|
|
139
|
+
}), localSignals);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
estimate = await raceWithSignals(() => this.contract[method].estimateGas(...args), localSignals);
|
|
143
|
+
}
|
|
144
|
+
return estimate;
|
|
145
|
+
}
|
|
110
146
|
getCall(methodName, args = [], callData = {}) {
|
|
111
147
|
if (!this.address)
|
|
112
148
|
throw CONTRACTS_ERRORS.NON_CALLABLE_CONTRACT_INVOCATION;
|
|
@@ -6,4 +6,5 @@ export const CONTRACTS_ERRORS = {
|
|
|
6
6
|
FRAGMENT_NOT_DEFINED: (methodName) => new Error(`Fragment for method "${methodName}" was not found on the contract!`),
|
|
7
7
|
MISSING_WEBSOCKET_PROVIDER: new Error("Attempted to listen for contract events, but no WebSocketProvider was provided!"),
|
|
8
8
|
MISSING_PROVIDER: new Error("A provider is required, but none was provided!"),
|
|
9
|
+
ESTIMATE_STATIC_CALL: (methodName) => new Error(`Cannot estimate gas for static (view/pure) method "${methodName}"!`),
|
|
9
10
|
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { Signer } from "ethers";
|
|
2
|
-
export declare const isSigner: (driver: Signer) => boolean;
|
|
1
|
+
import type { Provider, Signer } from "ethers";
|
|
2
|
+
export declare const isSigner: (driver: Signer | Provider) => boolean;
|
package/lib/helpers/is-signer.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type { Contract, Provider,
|
|
1
|
+
import type { Contract, Provider, Signer } from "ethers";
|
|
2
2
|
import type { PriorityCallOptions } from "../types";
|
|
3
|
-
export declare function priorityCall(provider: Provider, signer:
|
|
4
|
-
|
|
5
|
-
}, contract: Contract, method: string, args?: any[], options?: PriorityCallOptions): Promise<any>;
|
|
3
|
+
export declare function priorityCall(provider: Provider, signer: Signer, contract: Contract, method: string, args?: any[], options?: PriorityCallOptions): Promise<any>;
|
|
4
|
+
export declare function priorityCallEstimate(provider: Provider, signer: Signer, contract: Contract, method: string, args?: any[], options?: PriorityCallOptions): Promise<bigint>;
|
|
@@ -1,8 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { config } from "../config.js";
|
|
2
2
|
import { checkSignals, createTimeoutSignal } from "../utils/index.js";
|
|
3
3
|
export async function priorityCall(provider, signer, contract, method, args = [], options = {}) {
|
|
4
|
+
const txn = await formTx(provider, signer, contract, method, args, options);
|
|
5
|
+
return signer.sendTransaction(txn);
|
|
6
|
+
}
|
|
7
|
+
export async function priorityCallEstimate(provider, signer, contract, method, args = [], options = {}) {
|
|
8
|
+
const txn = await formTx(provider, signer, contract, method, args, options);
|
|
9
|
+
return signer.estimateGas(txn);
|
|
10
|
+
}
|
|
11
|
+
async function formTx(provider, signer, contract, method, args = [], options = {}) {
|
|
4
12
|
const localOptions = {
|
|
5
|
-
multiplier:
|
|
13
|
+
multiplier: config.priorityCalls.multiplier,
|
|
6
14
|
...options,
|
|
7
15
|
};
|
|
8
16
|
const localSignals = [];
|
|
@@ -16,9 +24,7 @@ export async function priorityCall(provider, signer, contract, method, args = []
|
|
|
16
24
|
const maxPriorityFeePerGas = Math.ceil(localOptions.multiplier * Number(originalFeeData.maxPriorityFeePerGas));
|
|
17
25
|
const gasLimit = Math.ceil(localOptions.multiplier * Number(originalGasLimit));
|
|
18
26
|
checkSignals(localSignals);
|
|
19
|
-
const txn = await contract
|
|
20
|
-
.getFunction(method)
|
|
21
|
-
.populateTransaction(...args, {
|
|
27
|
+
const txn = await contract.getFunction(method).populateTransaction(...args, {
|
|
22
28
|
gasLimit,
|
|
23
29
|
maxFeePerGas,
|
|
24
30
|
maxPriorityFeePerGas,
|
|
@@ -31,11 +37,11 @@ export async function priorityCall(provider, signer, contract, method, args = []
|
|
|
31
37
|
const network = await provider.getNetwork();
|
|
32
38
|
txn.chainId = network.chainId;
|
|
33
39
|
}
|
|
34
|
-
else
|
|
35
|
-
|
|
40
|
+
else {
|
|
41
|
+
if (localOptions.chainId)
|
|
42
|
+
txn.chainId = localOptions.chainId;
|
|
36
43
|
}
|
|
37
|
-
|
|
38
|
-
return signer.sendTransaction(txn);
|
|
44
|
+
return txn;
|
|
39
45
|
}
|
|
40
46
|
async function gatherOriginalData(provider, contract, method, args = [], asynchronous, signals) {
|
|
41
47
|
let originalFeeData;
|
|
@@ -30,8 +30,11 @@ export declare class MulticallUnit extends BaseContract {
|
|
|
30
30
|
getArray<T>(tags: MulticallTags, deep?: boolean): T | null;
|
|
31
31
|
getArrayOrThrow<T>(tags: MulticallTags, deep?: boolean): T;
|
|
32
32
|
run(options?: Partial<MulticallOptions>): Promise<boolean>;
|
|
33
|
+
estimateRun(options?: MulticallOptions): Promise<bigint[]>;
|
|
34
|
+
private splitCalls;
|
|
33
35
|
private processStaticCalls;
|
|
34
36
|
private processMutableCalls;
|
|
37
|
+
private estimateMutableCallsBatch;
|
|
35
38
|
private saveResponse;
|
|
36
39
|
getOrThrow<T>(tags: MulticallTags, deep?: boolean): T;
|
|
37
40
|
get<T>(tags: MulticallTags, deep?: boolean): T | null;
|
|
@@ -119,35 +119,7 @@ export class MulticallUnit extends BaseContract {
|
|
|
119
119
|
this._response = Array(tags.length).fill([undefined, null]);
|
|
120
120
|
try {
|
|
121
121
|
checkSignals(runOptions.signals);
|
|
122
|
-
|
|
123
|
-
let staticIndexes;
|
|
124
|
-
let mutableCalls;
|
|
125
|
-
let mutableTags;
|
|
126
|
-
let mutableIndexes;
|
|
127
|
-
if (runOptions.forceMutability) {
|
|
128
|
-
if (runOptions.forceMutability === CallMutability.Static) {
|
|
129
|
-
staticCalls = calls;
|
|
130
|
-
staticIndexes = Array.from({ length: calls.length }, (_, i) => i);
|
|
131
|
-
mutableCalls = [];
|
|
132
|
-
mutableTags = [];
|
|
133
|
-
mutableIndexes = [];
|
|
134
|
-
}
|
|
135
|
-
else {
|
|
136
|
-
staticCalls = [];
|
|
137
|
-
staticIndexes = [];
|
|
138
|
-
mutableCalls = calls;
|
|
139
|
-
mutableTags = tags;
|
|
140
|
-
mutableIndexes = Array.from({ length: calls.length }, (_, i) => i);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
else {
|
|
144
|
-
const split = multicallSplitCalls(calls, tags);
|
|
145
|
-
staticCalls = split.staticCalls;
|
|
146
|
-
staticIndexes = split.staticIndexes;
|
|
147
|
-
mutableCalls = split.mutableCalls;
|
|
148
|
-
mutableTags = split.mutableTags;
|
|
149
|
-
mutableIndexes = split.mutableIndexes;
|
|
150
|
-
}
|
|
122
|
+
const { staticCalls, staticIndexes, mutableCalls, mutableTags, mutableIndexes, } = this.splitCalls(calls, tags, options.forceMutability);
|
|
151
123
|
// Process mutable
|
|
152
124
|
for (let i = 0; i < mutableCalls.length; i += runOptions.maxMutableCallsStack) {
|
|
153
125
|
checkSignals(runOptions.signals);
|
|
@@ -180,6 +152,65 @@ export class MulticallUnit extends BaseContract {
|
|
|
180
152
|
}
|
|
181
153
|
return this._lastSuccess ?? false;
|
|
182
154
|
}
|
|
155
|
+
async estimateRun(options = {}) {
|
|
156
|
+
const runOptions = {
|
|
157
|
+
...this._multicallOptions,
|
|
158
|
+
...options,
|
|
159
|
+
};
|
|
160
|
+
const tags = this.tags;
|
|
161
|
+
const calls = this.calls;
|
|
162
|
+
checkSignals(runOptions.signals);
|
|
163
|
+
const { mutableCalls, mutableTags } = this.splitCalls(calls, tags, options.forceMutability);
|
|
164
|
+
const estimates = [];
|
|
165
|
+
// Process mutable
|
|
166
|
+
for (let i = 0; i < mutableCalls.length; i += runOptions.maxMutableCallsStack) {
|
|
167
|
+
checkSignals(runOptions.signals);
|
|
168
|
+
const border = Math.min(i + runOptions.maxMutableCallsStack, mutableCalls.length);
|
|
169
|
+
const iterationCalls = mutableCalls.slice(i, border); // half-opened interval
|
|
170
|
+
const iterationTags = mutableTags.slice(i, border);
|
|
171
|
+
const estimation = await this.estimateMutableCallsBatch(iterationCalls, iterationTags, runOptions);
|
|
172
|
+
estimates.push(estimation);
|
|
173
|
+
}
|
|
174
|
+
return estimates;
|
|
175
|
+
}
|
|
176
|
+
splitCalls(calls, tags, forceMutability) {
|
|
177
|
+
let staticCalls;
|
|
178
|
+
let staticIndexes;
|
|
179
|
+
let mutableCalls;
|
|
180
|
+
let mutableTags;
|
|
181
|
+
let mutableIndexes;
|
|
182
|
+
if (forceMutability) {
|
|
183
|
+
if (forceMutability === CallMutability.Static) {
|
|
184
|
+
staticCalls = calls;
|
|
185
|
+
staticIndexes = Array.from({ length: calls.length }, (_, i) => i);
|
|
186
|
+
mutableCalls = [];
|
|
187
|
+
mutableTags = [];
|
|
188
|
+
mutableIndexes = [];
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
staticCalls = [];
|
|
192
|
+
staticIndexes = [];
|
|
193
|
+
mutableCalls = calls;
|
|
194
|
+
mutableTags = tags;
|
|
195
|
+
mutableIndexes = Array.from({ length: calls.length }, (_, i) => i);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
const split = multicallSplitCalls(calls, tags);
|
|
200
|
+
staticCalls = split.staticCalls;
|
|
201
|
+
staticIndexes = split.staticIndexes;
|
|
202
|
+
mutableCalls = split.mutableCalls;
|
|
203
|
+
mutableTags = split.mutableTags;
|
|
204
|
+
mutableIndexes = split.mutableIndexes;
|
|
205
|
+
}
|
|
206
|
+
return {
|
|
207
|
+
staticCalls,
|
|
208
|
+
staticIndexes,
|
|
209
|
+
mutableCalls,
|
|
210
|
+
mutableTags,
|
|
211
|
+
mutableIndexes,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
183
214
|
async processStaticCalls(iterationCalls, runOptions) {
|
|
184
215
|
const result = await this.call(aggregate3, [iterationCalls], {
|
|
185
216
|
forceMutability: CallMutability.Static,
|
|
@@ -217,6 +248,15 @@ export class MulticallUnit extends BaseContract {
|
|
|
217
248
|
}
|
|
218
249
|
return result;
|
|
219
250
|
}
|
|
251
|
+
async estimateMutableCallsBatch(iterationCalls, iterationTags, runOptions) {
|
|
252
|
+
return this.estimate(aggregate3, [iterationCalls], {
|
|
253
|
+
forceMutability: CallMutability.Mutable,
|
|
254
|
+
highPriorityTx: runOptions.highPriorityTxs,
|
|
255
|
+
priorityOptions: runOptions.priorityOptions,
|
|
256
|
+
signals: runOptions.signals,
|
|
257
|
+
timeoutMs: runOptions.mutableCallsTimeoutMs,
|
|
258
|
+
});
|
|
259
|
+
}
|
|
220
260
|
saveResponse(iterationResponse, iterationIndexes, globalTags) {
|
|
221
261
|
iterationResponse.forEach((el, index) => {
|
|
222
262
|
const [success, data] = el;
|
package/lib/utils/index.d.ts
CHANGED
package/lib/utils/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@woof-software/contracts-tools-sdk-ethers",
|
|
3
3
|
"description": "Module simplify smart contract interactions and multicall3 aggregation on the Ethereum blockchain and other EVM-compatible networks.",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.11",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/woof-compound/sandbox-sdks.git"
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"typescript": "^5.7.2",
|
|
29
|
-
"vitest": "^3.0.
|
|
29
|
+
"vitest": "^3.0.6"
|
|
30
30
|
},
|
|
31
31
|
"publishConfig": {
|
|
32
32
|
"access": "public"
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"scripts": {
|
|
35
35
|
"prepublish": "$npm_execpath build",
|
|
36
36
|
"build": "tsc --build tsconfig.build.json --force && node ../../scripts/compile/post-compile.js",
|
|
37
|
+
"anvil": "cp ./test/local/anvil-data ./anvil-data-tmp && anvil --state ./anvil-data-tmp --block-time 3",
|
|
37
38
|
"test": "vitest run --coverage --config test/vitest-config/vitest.config.unit.ts",
|
|
38
39
|
"test:unit": "vitest run --coverage --config test/vitest-config/vitest.config.unit.ts",
|
|
39
40
|
"test:e2e": "vitest run --coverage --config test/vitest-config/vitest.config.e2e.ts",
|