@rabby-wallet/gnosis-sdk 1.0.0 → 1.0.1
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/dist/api.d.ts +54 -0
- package/dist/api.js +39 -0
- package/dist/constants.d.ts +3 -0
- package/dist/constants.js +3 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +161 -0
- package/dist/type.d.ts +19 -0
- package/dist/type.js +1 -0
- package/dist/utils.d.ts +15 -0
- package/dist/utils.js +161 -0
- package/package.json +2 -2
- package/src/api.ts +1 -1
- package/tsconfig.json +2 -1
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Axios } from "axios";
|
|
2
|
+
export interface SafeInfo {
|
|
3
|
+
address: string;
|
|
4
|
+
fallbackHandler: string;
|
|
5
|
+
guard: string;
|
|
6
|
+
masterCopy: string;
|
|
7
|
+
modules: string[];
|
|
8
|
+
nonce: number;
|
|
9
|
+
owners: string[];
|
|
10
|
+
threshold: number;
|
|
11
|
+
version: string;
|
|
12
|
+
}
|
|
13
|
+
interface ConfirmationItem {
|
|
14
|
+
owner: string;
|
|
15
|
+
submissionDate: string;
|
|
16
|
+
transactionHash: string | null;
|
|
17
|
+
signature: string;
|
|
18
|
+
signatureType: string;
|
|
19
|
+
}
|
|
20
|
+
export interface SafeTransactionItem {
|
|
21
|
+
safe: string;
|
|
22
|
+
to: string;
|
|
23
|
+
value: string;
|
|
24
|
+
data: string | null;
|
|
25
|
+
operation: number;
|
|
26
|
+
gasToken: string;
|
|
27
|
+
safeTxGas: number;
|
|
28
|
+
baseGas: number;
|
|
29
|
+
gasPrice: string;
|
|
30
|
+
refundReceiver: string;
|
|
31
|
+
nonce: number;
|
|
32
|
+
executionDate: string | null;
|
|
33
|
+
submissionDate: string;
|
|
34
|
+
modified: string;
|
|
35
|
+
blockNumber: number | null;
|
|
36
|
+
transactionHash: string | null;
|
|
37
|
+
safeTxHash: string;
|
|
38
|
+
executor: string | null;
|
|
39
|
+
isExecuted: boolean;
|
|
40
|
+
confirmations: ConfirmationItem[];
|
|
41
|
+
signatures: string | null;
|
|
42
|
+
}
|
|
43
|
+
export default class RequestProvider {
|
|
44
|
+
host: string;
|
|
45
|
+
request: Axios;
|
|
46
|
+
constructor(networkId: string);
|
|
47
|
+
getPendingTransactions(safeAddress: string, nonce: number): Promise<{
|
|
48
|
+
results: SafeTransactionItem[];
|
|
49
|
+
}>;
|
|
50
|
+
postTransactions(safeAddres: string, data: any): Promise<void>;
|
|
51
|
+
getSafeInfo(safeAddress: string): Promise<SafeInfo>;
|
|
52
|
+
confirmTransaction(hash: string, data: any): Promise<void>;
|
|
53
|
+
}
|
|
54
|
+
export {};
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { toChecksumAddress } from "web3-utils";
|
|
3
|
+
const HOST_MAP = {
|
|
4
|
+
'1': "https://safe-transaction.gnosis.io/api/v1",
|
|
5
|
+
'137': 'https://safe-transaction.polygon.gnosis.io/api/v1',
|
|
6
|
+
'56': 'https://safe-transaction.bsc.gnosis.io/api/v1',
|
|
7
|
+
'100': 'https://safe-transaction.xdai.gnosis.io/api/v1'
|
|
8
|
+
};
|
|
9
|
+
export default class RequestProvider {
|
|
10
|
+
constructor(networkId) {
|
|
11
|
+
if (!(networkId in HOST_MAP)) {
|
|
12
|
+
throw new Error('Wrong networkId');
|
|
13
|
+
}
|
|
14
|
+
this.host = HOST_MAP[networkId];
|
|
15
|
+
this.request = axios.create({
|
|
16
|
+
baseURL: this.host
|
|
17
|
+
});
|
|
18
|
+
this.request.interceptors.response.use((response) => {
|
|
19
|
+
return response.data;
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
getPendingTransactions(safeAddress, nonce) {
|
|
23
|
+
return this.request.get(`/safes/${safeAddress}/multisig-transactions/`, {
|
|
24
|
+
params: {
|
|
25
|
+
executed: false,
|
|
26
|
+
nonce__gte: nonce,
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
postTransactions(safeAddres, data) {
|
|
31
|
+
return this.request.post(`/safes/${toChecksumAddress(safeAddres)}/multisig-transactions/`, data);
|
|
32
|
+
}
|
|
33
|
+
getSafeInfo(safeAddress) {
|
|
34
|
+
return this.request.get(`/safes/${safeAddress}/`);
|
|
35
|
+
}
|
|
36
|
+
confirmTransaction(hash, data) {
|
|
37
|
+
return this.request.post(`/multisig-transactions/${hash}/confirmations/`, data);
|
|
38
|
+
}
|
|
39
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Contract } from "ethers";
|
|
2
|
+
import { BigNumber } from "@ethersproject/bignumber";
|
|
3
|
+
import { providers } from "ethers";
|
|
4
|
+
import { SafeTransactionDataPartial, SafeSignature } from "@gnosis.pm/safe-core-sdk-types";
|
|
5
|
+
import { TransactionResult, TransactionOptions } from "@gnosis.pm/safe-core-sdk/dist/src/utils/transactions/types";
|
|
6
|
+
import SafeTransaction from "@gnosis.pm/safe-core-sdk/dist/src/utils/transactions/SafeTransaction";
|
|
7
|
+
import RequestProvider, { SafeInfo } from "./api";
|
|
8
|
+
declare class Safe {
|
|
9
|
+
contract: Contract;
|
|
10
|
+
safeAddress: string;
|
|
11
|
+
owners: string[];
|
|
12
|
+
version: string;
|
|
13
|
+
provider: providers.Web3Provider;
|
|
14
|
+
safeInfo: SafeInfo | null;
|
|
15
|
+
request: RequestProvider;
|
|
16
|
+
network: string;
|
|
17
|
+
constructor(safeAddress: string, version: string, provider: providers.Web3Provider, network?: string);
|
|
18
|
+
static getSafeInfo(safeAddress: string, network: string): Promise<SafeInfo>;
|
|
19
|
+
init(): Promise<void>;
|
|
20
|
+
getOwners(): Promise<string[]>;
|
|
21
|
+
getThreshold(): Promise<any>;
|
|
22
|
+
getNonce(): Promise<number>;
|
|
23
|
+
getPendingTransactions(): Promise<{
|
|
24
|
+
results: import("./api").SafeTransactionItem[];
|
|
25
|
+
}>;
|
|
26
|
+
buildTransaction(data: SafeTransactionDataPartial): Promise<SafeTransaction>;
|
|
27
|
+
getTransactionHash(transaction: SafeTransaction): Promise<any>;
|
|
28
|
+
signTransactionHash(hash: string): Promise<SafeSignature>;
|
|
29
|
+
signTransaction(transaction: SafeTransaction): Promise<void>;
|
|
30
|
+
getOwnersWhoApprovedTx(txHash: string): Promise<string[]>;
|
|
31
|
+
postTransaction(transaction: SafeTransaction, hash: string): Promise<void>;
|
|
32
|
+
confirmTransaction(safeTransaction: SafeTransaction): Promise<void>;
|
|
33
|
+
getBalance(): Promise<BigNumber>;
|
|
34
|
+
executeTransaction(safeTransaction: SafeTransaction, options?: TransactionOptions): Promise<TransactionResult>;
|
|
35
|
+
}
|
|
36
|
+
export default Safe;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { Contract } from "ethers";
|
|
2
|
+
import { BigNumber } from "@ethersproject/bignumber";
|
|
3
|
+
import { getSafeSingletonDeployment } from "@gnosis.pm/safe-deployments";
|
|
4
|
+
import { toChecksumAddress } from "web3-utils";
|
|
5
|
+
import SafeTransaction from "@gnosis.pm/safe-core-sdk/dist/src/utils/transactions/SafeTransaction";
|
|
6
|
+
import RequestProvider from "./api";
|
|
7
|
+
import { standardizeSafeTransactionData, sameString, generateSignature, generatePreValidatedSignature, estimateGasForTransactionExecution, toTxResult, } from "./utils";
|
|
8
|
+
class Safe {
|
|
9
|
+
constructor(safeAddress, version, provider, network = "1") {
|
|
10
|
+
this.owners = [];
|
|
11
|
+
this.safeInfo = null;
|
|
12
|
+
const contract = getSafeSingletonDeployment({
|
|
13
|
+
version,
|
|
14
|
+
network,
|
|
15
|
+
});
|
|
16
|
+
if (!contract) {
|
|
17
|
+
throw new Error("Wrong version or network");
|
|
18
|
+
}
|
|
19
|
+
this.provider = provider;
|
|
20
|
+
this.contract = new Contract(safeAddress, contract.abi, this.provider);
|
|
21
|
+
this.version = version;
|
|
22
|
+
this.safeAddress = safeAddress;
|
|
23
|
+
this.network = network;
|
|
24
|
+
this.request = new RequestProvider(network);
|
|
25
|
+
this.init();
|
|
26
|
+
}
|
|
27
|
+
static getSafeInfo(safeAddress, network) {
|
|
28
|
+
const request = new RequestProvider(network);
|
|
29
|
+
return request.getSafeInfo(safeAddress);
|
|
30
|
+
}
|
|
31
|
+
async init() {
|
|
32
|
+
const safeInfo = await Safe.getSafeInfo(this.safeAddress, this.network);
|
|
33
|
+
this.safeInfo = safeInfo;
|
|
34
|
+
if (this.version !== safeInfo.version) {
|
|
35
|
+
throw new Error(`Current version ${this.version} not matched address version ${safeInfo.version}`);
|
|
36
|
+
}
|
|
37
|
+
this.version = safeInfo.version;
|
|
38
|
+
this.owners = safeInfo.owners;
|
|
39
|
+
}
|
|
40
|
+
async getOwners() {
|
|
41
|
+
const owners = await this.contract.getOwners();
|
|
42
|
+
return owners;
|
|
43
|
+
}
|
|
44
|
+
async getThreshold() {
|
|
45
|
+
const threshold = await this.contract.getThreshold();
|
|
46
|
+
return threshold.toNumber();
|
|
47
|
+
}
|
|
48
|
+
async getNonce() {
|
|
49
|
+
const nonce = await this.contract.nonce();
|
|
50
|
+
return nonce.toNumber();
|
|
51
|
+
}
|
|
52
|
+
async getPendingTransactions() {
|
|
53
|
+
const nonce = await this.getNonce();
|
|
54
|
+
const transactions = await this.request.getPendingTransactions(this.safeAddress, nonce);
|
|
55
|
+
return transactions;
|
|
56
|
+
}
|
|
57
|
+
async buildTransaction(data) {
|
|
58
|
+
const transaction = await standardizeSafeTransactionData(this.safeAddress, this.contract, this.provider, data);
|
|
59
|
+
return new SafeTransaction(transaction);
|
|
60
|
+
}
|
|
61
|
+
async getTransactionHash(transaction) {
|
|
62
|
+
const transactionData = transaction.data;
|
|
63
|
+
return this.contract.getTransactionHash(transactionData.to, transactionData.value, transactionData.data, transactionData.operation, transactionData.safeTxGas, transactionData.baseGas, transactionData.gasPrice, transactionData.gasToken, transactionData.refundReceiver, transactionData.nonce);
|
|
64
|
+
}
|
|
65
|
+
async signTransactionHash(hash) {
|
|
66
|
+
const owners = await this.getOwners();
|
|
67
|
+
const signer = await this.provider.getSigner(0);
|
|
68
|
+
const signerAddress = await signer.getAddress();
|
|
69
|
+
const addressIsOwner = owners.find((owner) => signerAddress && sameString(owner, signerAddress));
|
|
70
|
+
if (!addressIsOwner) {
|
|
71
|
+
throw new Error("Transactions can only be signed by Safe owners");
|
|
72
|
+
}
|
|
73
|
+
return generateSignature(this.provider, hash);
|
|
74
|
+
}
|
|
75
|
+
async signTransaction(transaction) {
|
|
76
|
+
const hash = await this.getTransactionHash(transaction);
|
|
77
|
+
const sig = await this.signTransactionHash(hash);
|
|
78
|
+
transaction.addSignature(sig);
|
|
79
|
+
}
|
|
80
|
+
async getOwnersWhoApprovedTx(txHash) {
|
|
81
|
+
const owners = await this.getOwners();
|
|
82
|
+
let ownersWhoApproved = [];
|
|
83
|
+
for (const owner of owners) {
|
|
84
|
+
const approved = await this.contract.approvedHashes(owner, txHash);
|
|
85
|
+
if (approved.gt(0)) {
|
|
86
|
+
ownersWhoApproved.push(owner);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return ownersWhoApproved;
|
|
90
|
+
}
|
|
91
|
+
async postTransaction(transaction, hash) {
|
|
92
|
+
const signer = this.provider.getSigner(0);
|
|
93
|
+
const signerAddress = await signer.getAddress();
|
|
94
|
+
const safeAddress = toChecksumAddress(this.safeAddress);
|
|
95
|
+
await this.request.postTransactions(this.safeAddress, {
|
|
96
|
+
safe: safeAddress,
|
|
97
|
+
to: toChecksumAddress(transaction.data.to),
|
|
98
|
+
value: Number(transaction.data.value),
|
|
99
|
+
data: transaction.data.data,
|
|
100
|
+
operation: transaction.data.operation,
|
|
101
|
+
gasToken: transaction.data.gasToken,
|
|
102
|
+
safeTxGas: transaction.data.safeTxGas,
|
|
103
|
+
baseGas: transaction.data.baseGas,
|
|
104
|
+
gasPrice: transaction.data.gasPrice,
|
|
105
|
+
refundReceiver: transaction.data.refundReceiver,
|
|
106
|
+
nonce: transaction.data.nonce,
|
|
107
|
+
contractTransactionHash: hash,
|
|
108
|
+
sender: toChecksumAddress(signerAddress),
|
|
109
|
+
signature: transaction.encodedSignatures(),
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
async confirmTransaction(safeTransaction) {
|
|
113
|
+
const hash = await this.getTransactionHash(safeTransaction);
|
|
114
|
+
const signature = await this.signTransactionHash(hash);
|
|
115
|
+
safeTransaction.addSignature(signature);
|
|
116
|
+
const signer = await this.provider.getSigner(0);
|
|
117
|
+
const signerAddress = await signer.getAddress();
|
|
118
|
+
const sig = safeTransaction.signatures.get(signerAddress === null || signerAddress === void 0 ? void 0 : signerAddress.toLowerCase());
|
|
119
|
+
if (sig) {
|
|
120
|
+
await this.request.confirmTransaction(hash, { signature: sig.data });
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
async getBalance() {
|
|
124
|
+
return this.provider.getBalance(this.safeAddress);
|
|
125
|
+
}
|
|
126
|
+
async executeTransaction(safeTransaction, options) {
|
|
127
|
+
const txHash = await this.getTransactionHash(safeTransaction);
|
|
128
|
+
const ownersWhoApprovedTx = await this.getOwnersWhoApprovedTx(txHash);
|
|
129
|
+
for (const owner of ownersWhoApprovedTx) {
|
|
130
|
+
safeTransaction.addSignature(generatePreValidatedSignature(owner));
|
|
131
|
+
}
|
|
132
|
+
const owners = await this.getOwners();
|
|
133
|
+
const signer = await this.provider.getSigner(0);
|
|
134
|
+
const contract = this.contract.connect(signer);
|
|
135
|
+
const signerAddress = await signer.getAddress();
|
|
136
|
+
if (owners.includes(signerAddress)) {
|
|
137
|
+
safeTransaction.addSignature(generatePreValidatedSignature(signerAddress));
|
|
138
|
+
}
|
|
139
|
+
const threshold = await this.getThreshold();
|
|
140
|
+
if (threshold > safeTransaction.signatures.size) {
|
|
141
|
+
const signaturesMissing = threshold - safeTransaction.signatures.size;
|
|
142
|
+
throw new Error(`There ${signaturesMissing > 1 ? "are" : "is"} ${signaturesMissing} signature${signaturesMissing > 1 ? "s" : ""} missing`);
|
|
143
|
+
}
|
|
144
|
+
const value = BigNumber.from(safeTransaction.data.value);
|
|
145
|
+
if (!value.isZero()) {
|
|
146
|
+
const balance = await this.getBalance();
|
|
147
|
+
if (value.gt(BigNumber.from(balance))) {
|
|
148
|
+
throw new Error("Not enough Ether funds");
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const gasLimit = await estimateGasForTransactionExecution(contract, signerAddress, safeTransaction);
|
|
152
|
+
const executionOptions = {
|
|
153
|
+
gasLimit,
|
|
154
|
+
gasPrice: options === null || options === void 0 ? void 0 : options.gasPrice,
|
|
155
|
+
from: signerAddress,
|
|
156
|
+
};
|
|
157
|
+
const txResponse = await contract.execTransaction(safeTransaction.data.to, safeTransaction.data.value, safeTransaction.data.data, safeTransaction.data.operation, safeTransaction.data.safeTxGas, safeTransaction.data.baseGas, safeTransaction.data.gasPrice, safeTransaction.data.gasToken, safeTransaction.data.refundReceiver, safeTransaction.encodedSignatures(), executionOptions);
|
|
158
|
+
return toTxResult(txResponse, executionOptions);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
export default Safe;
|
package/dist/type.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { BigNumber } from "@ethersproject/bignumber";
|
|
2
|
+
import { SafeTransaction, SafeTransactionData } from "@gnosis.pm/safe-core-sdk-types";
|
|
3
|
+
import { TransactionOptions, TransactionResult } from "@gnosis.pm/safe-core-sdk/dist/src/utils/transactions/types";
|
|
4
|
+
export interface GnosisSafeContract {
|
|
5
|
+
getVersion(): Promise<string>;
|
|
6
|
+
getAddress(): string;
|
|
7
|
+
getNonce(): Promise<number>;
|
|
8
|
+
getThreshold(): Promise<number>;
|
|
9
|
+
getOwners(): Promise<string[]>;
|
|
10
|
+
isOwner(address: string): Promise<boolean>;
|
|
11
|
+
getTransactionHash(safeTransactionData: SafeTransactionData): Promise<string>;
|
|
12
|
+
approvedHashes(ownerAddress: string, hash: string): Promise<BigNumber>;
|
|
13
|
+
approveHash(hash: string, options?: TransactionOptions): Promise<TransactionResult>;
|
|
14
|
+
getModules(): Promise<string[]>;
|
|
15
|
+
isModuleEnabled(moduleAddress: string): Promise<boolean>;
|
|
16
|
+
execTransaction(safeTransaction: SafeTransaction, options?: TransactionOptions): Promise<TransactionResult>;
|
|
17
|
+
encode(methodName: any, params: any): string;
|
|
18
|
+
estimateGas(methodName: string, params: any[], options: TransactionOptions): Promise<number>;
|
|
19
|
+
}
|
package/dist/type.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Contract, providers } from "ethers";
|
|
2
|
+
import { OperationType, SafeTransactionData, SafeTransactionDataPartial, SafeSignature, SafeTransaction } from "@gnosis.pm/safe-core-sdk-types";
|
|
3
|
+
import { TransactionOptions, Web3TransactionResult } from "@gnosis.pm/safe-core-sdk/dist/src/utils/transactions/types";
|
|
4
|
+
import { PromiEvent, TransactionReceipt } from "web3-core/types";
|
|
5
|
+
import EthSignSignature from "@gnosis.pm/safe-core-sdk/dist/src/utils/signatures/SafeSignature";
|
|
6
|
+
export declare function sameString(str1: string, str2: string): boolean;
|
|
7
|
+
export declare function isRestrictedAddress(address: string): boolean;
|
|
8
|
+
export declare function estimateTxGas(safeAddress: string, safeContract: Contract, provider: providers.Web3Provider, to: string, valueInWei: string, data: string, operation: OperationType): Promise<number>;
|
|
9
|
+
export declare function standardizeSafeTransactionData(safeAddress: string, safeContract: Contract, provider: any, tx: SafeTransactionDataPartial): Promise<SafeTransactionData>;
|
|
10
|
+
export declare function generatePreValidatedSignature(ownerAddress: string): SafeSignature;
|
|
11
|
+
export declare function isTxHashSignedWithPrefix(txHash: string, signature: string, ownerAddress: string): boolean;
|
|
12
|
+
export declare function adjustVInSignature(signature: string, hasPrefix: boolean): string;
|
|
13
|
+
export declare function generateSignature(provider: providers.Web3Provider, hash: string): Promise<EthSignSignature>;
|
|
14
|
+
export declare function estimateGasForTransactionExecution(safeContract: Contract, from: string, tx: SafeTransaction): Promise<number>;
|
|
15
|
+
export declare function toTxResult(promiEvent: PromiEvent<TransactionReceipt>, options?: TransactionOptions): Promise<Web3TransactionResult>;
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { BigNumber } from "@ethersproject/bignumber";
|
|
2
|
+
import { OperationType, } from "@gnosis.pm/safe-core-sdk-types";
|
|
3
|
+
import { bufferToHex, ecrecover, pubToAddress } from "ethereumjs-util";
|
|
4
|
+
import { ZERO_ADDRESS, SENTINEL_ADDRESS } from "./constants";
|
|
5
|
+
import EthSignSignature from "@gnosis.pm/safe-core-sdk/dist/src/utils/signatures/SafeSignature";
|
|
6
|
+
function estimateDataGasCosts(data) {
|
|
7
|
+
const reducer = (accumulator, currentValue) => {
|
|
8
|
+
if (currentValue === "0x") {
|
|
9
|
+
return accumulator + 0;
|
|
10
|
+
}
|
|
11
|
+
if (currentValue === "00") {
|
|
12
|
+
return accumulator + 4;
|
|
13
|
+
}
|
|
14
|
+
return accumulator + 16;
|
|
15
|
+
};
|
|
16
|
+
return data.match(/.{2}/g).reduce(reducer, 0);
|
|
17
|
+
}
|
|
18
|
+
export function sameString(str1, str2) {
|
|
19
|
+
return str1.toLowerCase() === str2.toLowerCase();
|
|
20
|
+
}
|
|
21
|
+
function isZeroAddress(address) {
|
|
22
|
+
return address === ZERO_ADDRESS;
|
|
23
|
+
}
|
|
24
|
+
function isSentinelAddress(address) {
|
|
25
|
+
return address === SENTINEL_ADDRESS;
|
|
26
|
+
}
|
|
27
|
+
export function isRestrictedAddress(address) {
|
|
28
|
+
return isZeroAddress(address) || isSentinelAddress(address);
|
|
29
|
+
}
|
|
30
|
+
export async function estimateTxGas(safeAddress, safeContract, provider, to, valueInWei, data, operation) {
|
|
31
|
+
let txGasEstimation = 0;
|
|
32
|
+
const estimateData = safeContract.interface.encodeFunctionData("requiredTxGas", [to, valueInWei, data, operation]);
|
|
33
|
+
try {
|
|
34
|
+
const estimateResponse = (await provider.estimateGas({
|
|
35
|
+
to: safeAddress,
|
|
36
|
+
from: safeAddress,
|
|
37
|
+
data: estimateData,
|
|
38
|
+
})).toString();
|
|
39
|
+
txGasEstimation =
|
|
40
|
+
BigNumber.from("0x" + estimateResponse.substring(138)).toNumber() + 10000;
|
|
41
|
+
}
|
|
42
|
+
catch (error) { }
|
|
43
|
+
if (txGasEstimation > 0) {
|
|
44
|
+
const dataGasEstimation = estimateDataGasCosts(estimateData);
|
|
45
|
+
let additionalGas = 10000;
|
|
46
|
+
for (let i = 0; i < 10; i++) {
|
|
47
|
+
try {
|
|
48
|
+
const estimateResponse = await provider.call({
|
|
49
|
+
to: safeAddress,
|
|
50
|
+
from: safeAddress,
|
|
51
|
+
data: estimateData,
|
|
52
|
+
gasPrice: 0,
|
|
53
|
+
gasLimit: txGasEstimation + dataGasEstimation + additionalGas,
|
|
54
|
+
});
|
|
55
|
+
if (estimateResponse !== "0x") {
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch (error) { }
|
|
60
|
+
txGasEstimation += additionalGas;
|
|
61
|
+
additionalGas *= 2;
|
|
62
|
+
}
|
|
63
|
+
return txGasEstimation + additionalGas;
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
const estimateGas = await provider.estimateGas({
|
|
67
|
+
to,
|
|
68
|
+
from: safeAddress,
|
|
69
|
+
value: valueInWei,
|
|
70
|
+
data,
|
|
71
|
+
});
|
|
72
|
+
return estimateGas.toNumber();
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
if (operation === OperationType.DelegateCall) {
|
|
76
|
+
return 0;
|
|
77
|
+
}
|
|
78
|
+
return Promise.reject(error);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export async function standardizeSafeTransactionData(safeAddress, safeContract, provider, tx) {
|
|
82
|
+
var _a, _b, _c, _d, _e;
|
|
83
|
+
const standardizedTxs = {
|
|
84
|
+
to: tx.to,
|
|
85
|
+
value: tx.value,
|
|
86
|
+
data: tx.data,
|
|
87
|
+
operation: (_a = tx.operation) !== null && _a !== void 0 ? _a : OperationType.Call,
|
|
88
|
+
baseGas: (_b = tx.baseGas) !== null && _b !== void 0 ? _b : 0,
|
|
89
|
+
gasPrice: (_c = tx.gasPrice) !== null && _c !== void 0 ? _c : 0,
|
|
90
|
+
gasToken: tx.gasToken || ZERO_ADDRESS,
|
|
91
|
+
refundReceiver: tx.refundReceiver || ZERO_ADDRESS,
|
|
92
|
+
nonce: (_d = tx.nonce) !== null && _d !== void 0 ? _d : (await safeContract.nonce()).toNumber(),
|
|
93
|
+
};
|
|
94
|
+
const safeTxGas = (_e = tx.safeTxGas) !== null && _e !== void 0 ? _e : (await estimateTxGas(safeAddress, safeContract, provider, standardizedTxs.to, standardizedTxs.value, standardizedTxs.data, standardizedTxs.operation));
|
|
95
|
+
return {
|
|
96
|
+
...standardizedTxs,
|
|
97
|
+
safeTxGas,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
export function generatePreValidatedSignature(ownerAddress) {
|
|
101
|
+
const signature = "0x000000000000000000000000" +
|
|
102
|
+
ownerAddress.slice(2) +
|
|
103
|
+
"0000000000000000000000000000000000000000000000000000000000000000" +
|
|
104
|
+
"01";
|
|
105
|
+
return new EthSignSignature(ownerAddress, signature);
|
|
106
|
+
}
|
|
107
|
+
export function isTxHashSignedWithPrefix(txHash, signature, ownerAddress) {
|
|
108
|
+
let hasPrefix;
|
|
109
|
+
try {
|
|
110
|
+
const rsvSig = {
|
|
111
|
+
r: Buffer.from(signature.slice(2, 66), "hex"),
|
|
112
|
+
s: Buffer.from(signature.slice(66, 130), "hex"),
|
|
113
|
+
v: parseInt(signature.slice(130, 132), 16),
|
|
114
|
+
};
|
|
115
|
+
const recoveredData = ecrecover(Buffer.from(txHash.slice(2), "hex"), rsvSig.v, rsvSig.r, rsvSig.s);
|
|
116
|
+
const recoveredAddress = bufferToHex(pubToAddress(recoveredData));
|
|
117
|
+
hasPrefix = !sameString(recoveredAddress, ownerAddress);
|
|
118
|
+
}
|
|
119
|
+
catch (e) {
|
|
120
|
+
hasPrefix = true;
|
|
121
|
+
}
|
|
122
|
+
return hasPrefix;
|
|
123
|
+
}
|
|
124
|
+
export function adjustVInSignature(signature, hasPrefix) {
|
|
125
|
+
const V_VALUES = [0, 1, 27, 28];
|
|
126
|
+
const MIN_VALID_V_VALUE = 27;
|
|
127
|
+
let signatureV = parseInt(signature.slice(-2), 16);
|
|
128
|
+
if (!V_VALUES.includes(signatureV)) {
|
|
129
|
+
throw new Error("Invalid signature");
|
|
130
|
+
}
|
|
131
|
+
if (signatureV < MIN_VALID_V_VALUE) {
|
|
132
|
+
signatureV += MIN_VALID_V_VALUE;
|
|
133
|
+
}
|
|
134
|
+
if (hasPrefix) {
|
|
135
|
+
signatureV += 4;
|
|
136
|
+
}
|
|
137
|
+
signature = signature.slice(0, -2) + signatureV.toString(16);
|
|
138
|
+
return signature;
|
|
139
|
+
}
|
|
140
|
+
export async function generateSignature(provider, hash) {
|
|
141
|
+
const signer = await provider.getSigner(0);
|
|
142
|
+
const signerAddress = await signer.getAddress();
|
|
143
|
+
let signature = await provider.send("personal_sign", [hash, signerAddress]);
|
|
144
|
+
const hasPrefix = isTxHashSignedWithPrefix(hash, signature, signerAddress);
|
|
145
|
+
signature = adjustVInSignature(signature, hasPrefix);
|
|
146
|
+
return new EthSignSignature(signerAddress, signature);
|
|
147
|
+
}
|
|
148
|
+
export async function estimateGasForTransactionExecution(safeContract, from, tx) {
|
|
149
|
+
try {
|
|
150
|
+
const gas = await safeContract.estimateGas.execTransaction(tx.data.to, tx.data.value, tx.data.data, tx.data.operation, tx.data.safeTxGas, tx.data.baseGas, tx.data.gasPrice, tx.data.gasToken, tx.data.refundReceiver, tx.encodedSignatures(), { from });
|
|
151
|
+
return gas.toNumber();
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
return Promise.reject(error);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
export function toTxResult(promiEvent, options) {
|
|
158
|
+
return new Promise((resolve, reject) => promiEvent
|
|
159
|
+
.once("transactionHash", (hash) => resolve({ hash, promiEvent, options }))
|
|
160
|
+
.catch(reject));
|
|
161
|
+
}
|
package/package.json
CHANGED
package/src/api.ts
CHANGED
package/tsconfig.json
CHANGED
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
"allowSyntheticDefaultImports": true,
|
|
13
13
|
"allowJs": true,
|
|
14
14
|
"plugins": [{ "transform": "typescript-transform-paths", "afterDeclarations": true }],
|
|
15
|
-
"outDir": "./dist"
|
|
15
|
+
"outDir": "./dist",
|
|
16
|
+
"declaration": true
|
|
16
17
|
},
|
|
17
18
|
"exclude": ["./node_modules"],
|
|
18
19
|
"include": ["src", "__tests__"]
|