@rabby-wallet/gnosis-sdk 1.0.0

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/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@rabby-wallet/gnosis-sdk",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "build": "tsc"
8
+ },
9
+ "author": "",
10
+ "license": "ISC",
11
+ "dependencies": {
12
+ "@ethersproject/bignumber": "^5.5.0",
13
+ "@ethersproject/bytes": "^5.5.0",
14
+ "@ethersproject/contracts": "^5.5.0",
15
+ "@ethersproject/providers": "^5.5.0",
16
+ "@ethersproject/solidity": "^5.5.0",
17
+ "@gnosis.pm/safe-core-sdk": "^1.0.0",
18
+ "@gnosis.pm/safe-core-sdk-types": "^0.1.1",
19
+ "@gnosis.pm/safe-deployments": "^1.4.0",
20
+ "axios": "^0.24.0",
21
+ "ethereumjs-util": "^7.1.3",
22
+ "ethers": "^5.5.1",
23
+ "typescript": "^4.4.4",
24
+ "web3-core": "^1.6.0"
25
+ }
26
+ }
package/src/api.ts ADDED
@@ -0,0 +1,101 @@
1
+ import axios, { Axios } from "axios";
2
+ import { toChecksumAddress } from "web3-utils";
3
+
4
+ export interface SafeInfo {
5
+ address: string;
6
+ fallbackHandler: string;
7
+ guard: string;
8
+ masterCopy: string;
9
+ modules: string[];
10
+ nonce: number;
11
+ owners: string[];
12
+ threshold: number;
13
+ version: string;
14
+ }
15
+
16
+ interface ConfirmationItem {
17
+ owner: string;
18
+ submissionDate: string;
19
+ transactionHash: string | null;
20
+ signature: string;
21
+ signatureType: string;
22
+ }
23
+
24
+ interface SafeTransactionItem {
25
+ safe: string;
26
+ to: string;
27
+ value: string;
28
+ data: string | null;
29
+ operation: number;
30
+ gasToken: string;
31
+ safeTxGas: number;
32
+ baseGas: number;
33
+ gasPrice: string;
34
+ refundReceiver: string;
35
+ nonce: number;
36
+ executionDate: string | null;
37
+ submissionDate: string;
38
+ modified: string;
39
+ blockNumber: number | null;
40
+ transactionHash: string | null;
41
+ safeTxHash: string;
42
+ executor: string | null;
43
+ isExecuted: boolean;
44
+ confirmations: ConfirmationItem[];
45
+ signatures: string | null;
46
+ }
47
+
48
+ const HOST_MAP = {
49
+ '1': "https://safe-transaction.gnosis.io/api/v1",
50
+ '137': 'https://safe-transaction.polygon.gnosis.io/api/v1',
51
+ '56': 'https://safe-transaction.bsc.gnosis.io/api/v1',
52
+ '100': 'https://safe-transaction.xdai.gnosis.io/api/v1'
53
+ }
54
+
55
+ export default class RequestProvider {
56
+ host: string
57
+ request: Axios
58
+
59
+ constructor(networkId: string) {
60
+ if (!(networkId in HOST_MAP)) {
61
+ throw new Error('Wrong networkId')
62
+ }
63
+
64
+ this.host = HOST_MAP[networkId]
65
+
66
+ this.request = axios.create({
67
+ baseURL: this.host
68
+ })
69
+
70
+ this.request.interceptors.response.use((response) => {
71
+ return response.data;
72
+ });
73
+ }
74
+
75
+ getPendingTransactions(
76
+ safeAddress: string,
77
+ nonce: number
78
+ ): Promise<{ results: SafeTransactionItem[] }> {
79
+ return this.request.get(`/safes/${safeAddress}/multisig-transactions/`, {
80
+ params: {
81
+ executed: false,
82
+ nonce__gte: nonce,
83
+ },
84
+ });
85
+ }
86
+
87
+ postTransactions(safeAddres: string, data): Promise<void> {
88
+ return this.request.post(
89
+ `/safes/${toChecksumAddress(safeAddres)}/multisig-transactions/`,
90
+ data
91
+ );
92
+ }
93
+
94
+ getSafeInfo(safeAddress: string): Promise<SafeInfo> {
95
+ return this.request.get(`/safes/${safeAddress}/`);
96
+ }
97
+
98
+ confirmTransaction(hash: string, data): Promise<void> {
99
+ return this.request.post(`/multisig-transactions/${hash}/confirmations/`, data);
100
+ }
101
+ }
@@ -0,0 +1,3 @@
1
+ export const ZERO_ADDRESS = `0x${"0".repeat(40)}`;
2
+ export const EMPTY_DATA = "0x";
3
+ export const SENTINEL_ADDRESS = "0x0000000000000000000000000000000000000001";
package/src/index.ts ADDED
@@ -0,0 +1,260 @@
1
+ import { Contract } from "ethers";
2
+ import { BigNumber } from "@ethersproject/bignumber";
3
+ import { getSafeSingletonDeployment } from "@gnosis.pm/safe-deployments";
4
+ import { providers } from "ethers";
5
+ import { toChecksumAddress } from "web3-utils";
6
+ import {
7
+ SafeTransactionDataPartial,
8
+ SafeSignature,
9
+ } from "@gnosis.pm/safe-core-sdk-types";
10
+ import {
11
+ TransactionResult,
12
+ TransactionOptions,
13
+ } from "@gnosis.pm/safe-core-sdk/dist/src/utils/transactions/types";
14
+ import SafeTransaction from "@gnosis.pm/safe-core-sdk/dist/src/utils/transactions/SafeTransaction";
15
+ import RequestProvider, { SafeInfo } from "./api";
16
+ import {
17
+ standardizeSafeTransactionData,
18
+ sameString,
19
+ generateSignature,
20
+ generatePreValidatedSignature,
21
+ estimateGasForTransactionExecution,
22
+ toTxResult,
23
+ } from "./utils";
24
+
25
+ class Safe {
26
+ contract: Contract;
27
+ safeAddress: string;
28
+ owners: string[] = [];
29
+ version: string;
30
+ provider: providers.Web3Provider;
31
+ safeInfo: SafeInfo | null = null;
32
+ request: RequestProvider;
33
+ network: string;
34
+
35
+ constructor(
36
+ safeAddress: string,
37
+ version: string,
38
+ provider: providers.Web3Provider,
39
+ network = "1"
40
+ ) {
41
+ const contract = getSafeSingletonDeployment({
42
+ version,
43
+ network,
44
+ });
45
+ if (!contract) {
46
+ throw new Error("Wrong version or network");
47
+ }
48
+ this.provider = provider;
49
+ this.contract = new Contract(safeAddress, contract.abi, this.provider);
50
+ this.version = version;
51
+ this.safeAddress = safeAddress;
52
+ this.network = network;
53
+ this.request = new RequestProvider(network);
54
+ this.init();
55
+ }
56
+
57
+ static getSafeInfo(safeAddress: string, network: string) {
58
+ const request = new RequestProvider(network);
59
+ return request.getSafeInfo(safeAddress);
60
+ }
61
+
62
+ async init() {
63
+ const safeInfo = await Safe.getSafeInfo(this.safeAddress, this.network);
64
+ this.safeInfo = safeInfo;
65
+ if (this.version !== safeInfo.version) {
66
+ throw new Error(
67
+ `Current version ${this.version} not matched address version ${safeInfo.version}`
68
+ );
69
+ }
70
+ this.version = safeInfo.version;
71
+ this.owners = safeInfo.owners;
72
+ }
73
+
74
+ async getOwners(): Promise<string[]> {
75
+ const owners = await this.contract.getOwners();
76
+
77
+ return owners;
78
+ }
79
+
80
+ async getThreshold() {
81
+ const threshold = await this.contract.getThreshold();
82
+ return threshold.toNumber();
83
+ }
84
+
85
+ async getNonce(): Promise<number> {
86
+ const nonce = await this.contract.nonce();
87
+ return nonce.toNumber();
88
+ }
89
+
90
+ async getPendingTransactions() {
91
+ const nonce = await this.getNonce();
92
+ const transactions = await this.request.getPendingTransactions(this.safeAddress, nonce);
93
+
94
+ return transactions;
95
+ }
96
+
97
+ async buildTransaction(data: SafeTransactionDataPartial) {
98
+ const transaction = await standardizeSafeTransactionData(
99
+ this.safeAddress,
100
+ this.contract,
101
+ this.provider,
102
+ data
103
+ );
104
+ return new SafeTransaction(transaction);
105
+ }
106
+
107
+ async getTransactionHash(transaction: SafeTransaction) {
108
+ const transactionData = transaction.data;
109
+ return this.contract.getTransactionHash(
110
+ transactionData.to,
111
+ transactionData.value,
112
+ transactionData.data,
113
+ transactionData.operation,
114
+ transactionData.safeTxGas,
115
+ transactionData.baseGas,
116
+ transactionData.gasPrice,
117
+ transactionData.gasToken,
118
+ transactionData.refundReceiver,
119
+ transactionData.nonce
120
+ );
121
+ }
122
+
123
+ async signTransactionHash(hash: string): Promise<SafeSignature> {
124
+ const owners = await this.getOwners();
125
+ const signer = await this.provider.getSigner(0);
126
+ const signerAddress = await signer.getAddress();
127
+ const addressIsOwner = owners.find(
128
+ (owner: string) => signerAddress && sameString(owner, signerAddress)
129
+ );
130
+ if (!addressIsOwner) {
131
+ throw new Error("Transactions can only be signed by Safe owners");
132
+ }
133
+ return generateSignature(this.provider, hash);
134
+ }
135
+
136
+ async signTransaction(transaction: SafeTransaction) {
137
+ const hash = await this.getTransactionHash(transaction);
138
+ const sig = await this.signTransactionHash(hash);
139
+ transaction.addSignature(sig);
140
+ }
141
+
142
+ async getOwnersWhoApprovedTx(txHash: string): Promise<string[]> {
143
+ const owners = await this.getOwners();
144
+ let ownersWhoApproved: string[] = [];
145
+ for (const owner of owners) {
146
+ const approved = await this.contract.approvedHashes(owner, txHash);
147
+ if (approved.gt(0)) {
148
+ ownersWhoApproved.push(owner);
149
+ }
150
+ }
151
+ return ownersWhoApproved;
152
+ }
153
+
154
+ async postTransaction(transaction: SafeTransaction, hash: string) {
155
+ const signer = this.provider.getSigner(0);
156
+ const signerAddress = await signer.getAddress();
157
+ const safeAddress = toChecksumAddress(this.safeAddress);
158
+ await this.request.postTransactions(this.safeAddress, {
159
+ safe: safeAddress,
160
+ to: toChecksumAddress(transaction.data.to),
161
+ value: Number(transaction.data.value),
162
+ data: transaction.data.data,
163
+ operation: transaction.data.operation,
164
+ gasToken: transaction.data.gasToken,
165
+ safeTxGas: transaction.data.safeTxGas,
166
+ baseGas: transaction.data.baseGas,
167
+ gasPrice: transaction.data.gasPrice,
168
+ refundReceiver: transaction.data.refundReceiver,
169
+ nonce: transaction.data.nonce,
170
+ contractTransactionHash: hash,
171
+ sender: toChecksumAddress(signerAddress),
172
+ signature: transaction.encodedSignatures(),
173
+ });
174
+ }
175
+
176
+ async confirmTransaction(safeTransaction: SafeTransaction) {
177
+ const hash = await this.getTransactionHash(safeTransaction);
178
+ const signature = await this.signTransactionHash(hash);
179
+ safeTransaction.addSignature(signature);
180
+ const signer = await this.provider.getSigner(0);
181
+ const signerAddress = await signer.getAddress();
182
+ const sig = safeTransaction.signatures.get(signerAddress?.toLowerCase());
183
+ if (sig) {
184
+ await this.request.confirmTransaction(hash, { signature: sig.data });
185
+ }
186
+ }
187
+
188
+ async getBalance(): Promise<BigNumber> {
189
+ return this.provider.getBalance(this.safeAddress);
190
+ }
191
+
192
+ async executeTransaction(
193
+ safeTransaction: SafeTransaction,
194
+ options?: TransactionOptions
195
+ ): Promise<TransactionResult> {
196
+ const txHash = await this.getTransactionHash(safeTransaction);
197
+ const ownersWhoApprovedTx = await this.getOwnersWhoApprovedTx(txHash);
198
+ for (const owner of ownersWhoApprovedTx) {
199
+ safeTransaction.addSignature(generatePreValidatedSignature(owner));
200
+ }
201
+ const owners = await this.getOwners();
202
+ const signer = await this.provider.getSigner(0);
203
+ const contract = this.contract.connect(signer);
204
+ const signerAddress = await signer.getAddress();
205
+ if (owners.includes(signerAddress)) {
206
+ safeTransaction.addSignature(
207
+ generatePreValidatedSignature(signerAddress)
208
+ );
209
+ }
210
+
211
+ const threshold = await this.getThreshold();
212
+ if (threshold > safeTransaction.signatures.size) {
213
+ const signaturesMissing = threshold - safeTransaction.signatures.size;
214
+ throw new Error(
215
+ `There ${
216
+ signaturesMissing > 1 ? "are" : "is"
217
+ } ${signaturesMissing} signature${
218
+ signaturesMissing > 1 ? "s" : ""
219
+ } missing`
220
+ );
221
+ }
222
+
223
+ const value = BigNumber.from(safeTransaction.data.value);
224
+ if (!value.isZero()) {
225
+ const balance = await this.getBalance();
226
+ if (value.gt(BigNumber.from(balance))) {
227
+ throw new Error("Not enough Ether funds");
228
+ }
229
+ }
230
+
231
+ const gasLimit = await estimateGasForTransactionExecution(
232
+ contract,
233
+ signerAddress,
234
+ safeTransaction
235
+ );
236
+ const executionOptions: TransactionOptions = {
237
+ gasLimit,
238
+ gasPrice: options?.gasPrice,
239
+ from: signerAddress,
240
+ };
241
+
242
+ const txResponse = await contract.execTransaction(
243
+ safeTransaction.data.to,
244
+ safeTransaction.data.value,
245
+ safeTransaction.data.data,
246
+ safeTransaction.data.operation,
247
+ safeTransaction.data.safeTxGas,
248
+ safeTransaction.data.baseGas,
249
+ safeTransaction.data.gasPrice,
250
+ safeTransaction.data.gasToken,
251
+ safeTransaction.data.refundReceiver,
252
+ safeTransaction.encodedSignatures(),
253
+ executionOptions
254
+ )
255
+
256
+ return toTxResult(txResponse, executionOptions);
257
+ }
258
+ }
259
+
260
+ export default Safe;
package/src/type.ts ADDED
@@ -0,0 +1,36 @@
1
+ import { BigNumber } from "@ethersproject/bignumber";
2
+ import {
3
+ SafeTransaction,
4
+ SafeTransactionData,
5
+ } from "@gnosis.pm/safe-core-sdk-types";
6
+ import {
7
+ TransactionOptions,
8
+ TransactionResult,
9
+ } from "@gnosis.pm/safe-core-sdk/dist/src/utils/transactions/types";
10
+
11
+ export interface GnosisSafeContract {
12
+ getVersion(): Promise<string>;
13
+ getAddress(): string;
14
+ getNonce(): Promise<number>;
15
+ getThreshold(): Promise<number>;
16
+ getOwners(): Promise<string[]>;
17
+ isOwner(address: string): Promise<boolean>;
18
+ getTransactionHash(safeTransactionData: SafeTransactionData): Promise<string>;
19
+ approvedHashes(ownerAddress: string, hash: string): Promise<BigNumber>;
20
+ approveHash(
21
+ hash: string,
22
+ options?: TransactionOptions
23
+ ): Promise<TransactionResult>;
24
+ getModules(): Promise<string[]>;
25
+ isModuleEnabled(moduleAddress: string): Promise<boolean>;
26
+ execTransaction(
27
+ safeTransaction: SafeTransaction,
28
+ options?: TransactionOptions
29
+ ): Promise<TransactionResult>;
30
+ encode(methodName: any, params: any): string;
31
+ estimateGas(
32
+ methodName: string,
33
+ params: any[],
34
+ options: TransactionOptions
35
+ ): Promise<number>;
36
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,251 @@
1
+ import { BigNumber } from "@ethersproject/bignumber";
2
+ import { Contract, providers } from "ethers";
3
+ import {
4
+ MetaTransactionData,
5
+ OperationType,
6
+ SafeTransactionData,
7
+ SafeTransactionDataPartial,
8
+ SafeSignature,
9
+ SafeTransaction,
10
+ } from "@gnosis.pm/safe-core-sdk-types";
11
+ import {
12
+ TransactionOptions,
13
+ Web3TransactionResult,
14
+ } from "@gnosis.pm/safe-core-sdk/dist/src/utils/transactions/types";
15
+ import { PromiEvent, TransactionReceipt } from "web3-core/types";
16
+ import { bufferToHex, ecrecover, pubToAddress } from "ethereumjs-util";
17
+ import { ZERO_ADDRESS, SENTINEL_ADDRESS } from "./constants";
18
+ import EthSignSignature from "@gnosis.pm/safe-core-sdk/dist/src/utils/signatures/SafeSignature";
19
+
20
+ function estimateDataGasCosts(data: string): number {
21
+ const reducer = (accumulator: number, currentValue: string) => {
22
+ if (currentValue === "0x") {
23
+ return accumulator + 0;
24
+ }
25
+ if (currentValue === "00") {
26
+ return accumulator + 4;
27
+ }
28
+ return accumulator + 16;
29
+ };
30
+ return (data.match(/.{2}/g) as string[]).reduce(reducer, 0);
31
+ }
32
+
33
+ export function sameString(str1: string, str2: string): boolean {
34
+ return str1.toLowerCase() === str2.toLowerCase();
35
+ }
36
+
37
+ function isZeroAddress(address: string): boolean {
38
+ return address === ZERO_ADDRESS;
39
+ }
40
+
41
+ function isSentinelAddress(address: string): boolean {
42
+ return address === SENTINEL_ADDRESS;
43
+ }
44
+
45
+ export function isRestrictedAddress(address: string): boolean {
46
+ return isZeroAddress(address) || isSentinelAddress(address);
47
+ }
48
+
49
+ export async function estimateTxGas(
50
+ safeAddress: string,
51
+ safeContract: Contract,
52
+ provider: providers.Web3Provider,
53
+ to: string,
54
+ valueInWei: string,
55
+ data: string,
56
+ operation: OperationType
57
+ ): Promise<number> {
58
+ let txGasEstimation = 0;
59
+
60
+ const estimateData: string = safeContract.interface.encodeFunctionData(
61
+ "requiredTxGas",
62
+ [to, valueInWei, data, operation]
63
+ );
64
+ try {
65
+ const estimateResponse = (
66
+ await provider.estimateGas({
67
+ to: safeAddress,
68
+ from: safeAddress,
69
+ data: estimateData,
70
+ })
71
+ ).toString();
72
+ txGasEstimation =
73
+ BigNumber.from("0x" + estimateResponse.substring(138)).toNumber() + 10000;
74
+ } catch (error) {}
75
+
76
+ if (txGasEstimation > 0) {
77
+ const dataGasEstimation = estimateDataGasCosts(estimateData);
78
+ let additionalGas = 10000;
79
+ for (let i = 0; i < 10; i++) {
80
+ try {
81
+ const estimateResponse = await provider.call({
82
+ to: safeAddress,
83
+ from: safeAddress,
84
+ data: estimateData,
85
+ gasPrice: 0,
86
+ gasLimit: txGasEstimation + dataGasEstimation + additionalGas,
87
+ });
88
+ if (estimateResponse !== "0x") {
89
+ break;
90
+ }
91
+ } catch (error) {}
92
+ txGasEstimation += additionalGas;
93
+ additionalGas *= 2;
94
+ }
95
+ return txGasEstimation + additionalGas;
96
+ }
97
+
98
+ try {
99
+ const estimateGas = await provider.estimateGas({
100
+ to,
101
+ from: safeAddress,
102
+ value: valueInWei,
103
+ data,
104
+ });
105
+ return estimateGas.toNumber();
106
+ } catch (error) {
107
+ if (operation === OperationType.DelegateCall) {
108
+ return 0;
109
+ }
110
+ return Promise.reject(error);
111
+ }
112
+ }
113
+
114
+ export async function standardizeSafeTransactionData(
115
+ safeAddress: string,
116
+ safeContract: Contract,
117
+ provider: any,
118
+ tx: SafeTransactionDataPartial
119
+ ): Promise<SafeTransactionData> {
120
+ const standardizedTxs = {
121
+ to: tx.to,
122
+ value: tx.value,
123
+ data: tx.data,
124
+ operation: tx.operation ?? OperationType.Call,
125
+ baseGas: tx.baseGas ?? 0,
126
+ gasPrice: tx.gasPrice ?? 0,
127
+ gasToken: tx.gasToken || ZERO_ADDRESS,
128
+ refundReceiver: tx.refundReceiver || ZERO_ADDRESS,
129
+ nonce: tx.nonce ?? (await safeContract.nonce()).toNumber(),
130
+ };
131
+ const safeTxGas =
132
+ tx.safeTxGas ??
133
+ (await estimateTxGas(
134
+ safeAddress,
135
+ safeContract,
136
+ provider,
137
+ standardizedTxs.to,
138
+ standardizedTxs.value,
139
+ standardizedTxs.data,
140
+ standardizedTxs.operation
141
+ ));
142
+ return {
143
+ ...standardizedTxs,
144
+ safeTxGas,
145
+ };
146
+ }
147
+
148
+ export function generatePreValidatedSignature(
149
+ ownerAddress: string
150
+ ): SafeSignature {
151
+ const signature =
152
+ "0x000000000000000000000000" +
153
+ ownerAddress.slice(2) +
154
+ "0000000000000000000000000000000000000000000000000000000000000000" +
155
+ "01";
156
+
157
+ return new EthSignSignature(ownerAddress, signature);
158
+ }
159
+
160
+ export function isTxHashSignedWithPrefix(
161
+ txHash: string,
162
+ signature: string,
163
+ ownerAddress: string
164
+ ): boolean {
165
+ let hasPrefix;
166
+ try {
167
+ const rsvSig = {
168
+ r: Buffer.from(signature.slice(2, 66), "hex"),
169
+ s: Buffer.from(signature.slice(66, 130), "hex"),
170
+ v: parseInt(signature.slice(130, 132), 16),
171
+ };
172
+ const recoveredData = ecrecover(
173
+ Buffer.from(txHash.slice(2), "hex"),
174
+ rsvSig.v,
175
+ rsvSig.r,
176
+ rsvSig.s
177
+ );
178
+ const recoveredAddress = bufferToHex(pubToAddress(recoveredData));
179
+ hasPrefix = !sameString(recoveredAddress, ownerAddress);
180
+ } catch (e) {
181
+ hasPrefix = true;
182
+ }
183
+ return hasPrefix;
184
+ }
185
+
186
+ export function adjustVInSignature(signature: string, hasPrefix: boolean) {
187
+ const V_VALUES = [0, 1, 27, 28];
188
+ const MIN_VALID_V_VALUE = 27;
189
+ let signatureV = parseInt(signature.slice(-2), 16);
190
+ if (!V_VALUES.includes(signatureV)) {
191
+ throw new Error("Invalid signature");
192
+ }
193
+ if (signatureV < MIN_VALID_V_VALUE) {
194
+ signatureV += MIN_VALID_V_VALUE;
195
+ }
196
+ if (hasPrefix) {
197
+ signatureV += 4;
198
+ }
199
+ signature = signature.slice(0, -2) + signatureV.toString(16);
200
+ return signature;
201
+ }
202
+
203
+ export async function generateSignature(
204
+ provider: providers.Web3Provider,
205
+ hash: string
206
+ ): Promise<EthSignSignature> {
207
+ const signer = await provider.getSigner(0);
208
+ const signerAddress = await signer.getAddress();
209
+ let signature = await provider.send("personal_sign", [hash, signerAddress]);
210
+ const hasPrefix = isTxHashSignedWithPrefix(hash, signature, signerAddress);
211
+ signature = adjustVInSignature(signature, hasPrefix);
212
+ return new EthSignSignature(signerAddress, signature);
213
+ }
214
+
215
+ export async function estimateGasForTransactionExecution(
216
+ safeContract: Contract,
217
+ from: string,
218
+ tx: SafeTransaction
219
+ ): Promise<number> {
220
+ try {
221
+ const gas = await safeContract.estimateGas.execTransaction(
222
+ tx.data.to,
223
+ tx.data.value,
224
+ tx.data.data,
225
+ tx.data.operation,
226
+ tx.data.safeTxGas,
227
+ tx.data.baseGas,
228
+ tx.data.gasPrice,
229
+ tx.data.gasToken,
230
+ tx.data.refundReceiver,
231
+ tx.encodedSignatures(),
232
+ { from }
233
+ );
234
+ return gas.toNumber();
235
+ } catch (error) {
236
+ return Promise.reject(error);
237
+ }
238
+ }
239
+
240
+ export function toTxResult(
241
+ promiEvent: PromiEvent<TransactionReceipt>,
242
+ options?: TransactionOptions
243
+ ): Promise<Web3TransactionResult> {
244
+ return new Promise((resolve, reject) =>
245
+ promiEvent
246
+ .once("transactionHash", (hash: string) =>
247
+ resolve({ hash, promiEvent, options })
248
+ )
249
+ .catch(reject)
250
+ );
251
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "baseUrl": "./",
4
+ "experimentalDecorators": true,
5
+ "emitDecoratorMetadata": true,
6
+ "module": "ESNext",
7
+ "target": "es2019",
8
+ "moduleResolution": "node",
9
+ "noImplicitAny": false,
10
+ "strict": true,
11
+ "esModuleInterop": true,
12
+ "allowSyntheticDefaultImports": true,
13
+ "allowJs": true,
14
+ "plugins": [{ "transform": "typescript-transform-paths", "afterDeclarations": true }],
15
+ "outDir": "./dist"
16
+ },
17
+ "exclude": ["./node_modules"],
18
+ "include": ["src", "__tests__"]
19
+ }