UNPKG

dp-contract-proxy-kit

Version:

Enable batched transactions and contract account interactions using a unique deterministic Gnosis Safe.

271 lines 10.2 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const bignumber_js_1 = __importDefault(require("bignumber.js")); const constants_1 = require("../../utils/constants"); const transactions_1 = require("../../utils/transactions"); const EthLibAdapter_1 = __importDefault(require("../EthLibAdapter")); const EthersV4ContractAdapter_1 = __importDefault(require("./EthersV4ContractAdapter")); const EthersV5ContractAdapter_1 = __importDefault(require("./EthersV5ContractAdapter")); class EthersAdapter extends EthLibAdapter_1.default { /** * Creates an instance of EthersAdapter * * @param options - EthersAdapter configuration * @returns The EthersAdapter instance */ constructor({ ethers, signer }) { super(); if (!ethers) { throw new Error('ethers property missing from options'); } if (!signer) { throw new Error('signer property missing from options'); } this.ethers = ethers; this.signer = signer; } /** * Returns the current provider * * @returns The current provider */ getProvider() { // eslint-disable-next-line no-underscore-dangle return this.signer.provider.provider || this.signer.provider._web3Provider; } /** * Sends a network request via JSON-RPC. * * @param method - JSON-RPC method * @param params - Params * @returns The request response */ providerSend(method, params) { return this.signer.provider.send(method, params); } /** * Signs data using a specific account. * * @param message - Data to sign * @param ownerAccount - Address to sign the data with * @returns The signature */ signMessage(message) { const messageArray = this.ethers.utils.arrayify(message); return this.signer.signMessage(messageArray); } /** * Returns the current network ID. * * @returns The network ID */ getNetworkId() { return __awaiter(this, void 0, void 0, function* () { return (yield this.signer.provider.getNetwork()).chainId; }); } /** * Returns the default account used as the default "from" property. * * @returns The default account address */ getAccount() { return __awaiter(this, void 0, void 0, function* () { return this.signer.getAddress(); }); } /** * Returns the balance of an address. * * @param address - The desired address * @returns The balance of the address */ getBalance(address) { return __awaiter(this, void 0, void 0, function* () { const balance = yield this.signer.provider.getBalance(address); return new bignumber_js_1.default(balance.toString()); }); } /** * Returns the keccak256 hash of the data. * * @param data - Desired data * @returns The keccak256 of the data */ keccak256(data) { return this.ethers.utils.keccak256(data); } /** * Encodes a function parameters based on its JSON interface object. * * @param types - An array with the types or a JSON interface of a function * @param values - The parameters to encode * @returns The ABI encoded parameters */ abiEncode(types, values) { return this.ethers.utils.defaultAbiCoder.encode(types, values); } /** * Decodes ABI encoded parameters to is JavaScript types. * * @param types - An array with the types or a JSON interface outputs array * @param data - The ABI byte code to decode * @returns The ABI encoded parameters */ abiDecode(types, data) { return this.ethers.utils.defaultAbiCoder.decode(types, data); } /** * Returns an instance of a contract. * * @param abi - ABI of the desired contract * @param address - Contract address * @returns The contract instance */ getContract(abi, address) { const contract = new this.ethers.Contract(address || constants_1.zeroAddress, abi, this.signer); const ethersVersion = this.ethers.version; // TO-DO: Use semver comparison if (ethersVersion.split('.')[0] === '4') { return new EthersV4ContractAdapter_1.default(contract, this); } if (ethersVersion.split('.')[0] === 'ethers/5') { return new EthersV5ContractAdapter_1.default(contract, this); } throw new Error(`ethers version ${ethersVersion} not supported`); } /** * Deterministically returns the address where a contract will be deployed. * * @param deployer - Account that deploys the contract * @param salt - Salt * @param initCode - Code to be deployed * @returns The address where the contract will be deployed */ calcCreate2Address(deployer, salt, initCode) { return this.ethers.utils.getAddress(this.ethers.utils .solidityKeccak256(['bytes', 'address', 'bytes32', 'bytes32'], ['0xff', deployer, salt, this.keccak256(initCode)]) .slice(-40)); } /** * Returns the code at a specific address. * * @param address - The desired address * @returns The code of the contract */ getCode(address) { return this.signer.provider.getCode(address); } /** * Returns a block matching the block number or block hash. * * @param blockHashOrBlockNumber - The block number or block hash * @returns The block object */ getBlock(blockHashOrBlockNumber) { return this.signer.provider.getBlock(blockHashOrBlockNumber); } /** * Returns the revert reason when a call fails. * * @param tx - Transaction to execute * @param block - Block number * @returns The revert data when the call fails */ getCallRevertData(tx, block) { return __awaiter(this, void 0, void 0, function* () { try { // Handle old Geth/Ganache --noVMErrorsOnRPCResponse revert data return yield this.ethCall(tx, block); } catch (e) { if (typeof e.data === 'string') { if (e.data.startsWith('Reverted 0x')) // handle OpenEthereum revert data format return e.data.slice(9); if (e.data.startsWith('0x')) // handle new Geth format return e.data; } // handle Ganache revert data format const txHash = Object.getOwnPropertyNames(e.data).filter((k) => k.startsWith('0x'))[0]; return e.data[txHash].return; } }); } ethCall(tx, block) { // This is to workaround https://github.com/ethers-io/ethers.js/issues/819 return this.providerSend('eth_call', [transactions_1.formatCallTx(tx), block]); } checkFromAddress(from) { return __awaiter(this, void 0, void 0, function* () { const { getAddress } = this.ethers.utils; const expectedFrom = yield this.getAccount(); if (getAddress(from) !== expectedFrom) { throw new Error(`want from ${expectedFrom} but got from ${from}`); } }); } /** * Sends a transaction to the network. * * @param tx - Transaction to send * @returns The transaction response */ ethSendTransaction(tx) { return __awaiter(this, void 0, void 0, function* () { const _a = transactions_1.normalizeGasLimit(tx), { from, gas } = _a, sendTx = __rest(_a, ["from", "gas"]); yield this.checkFromAddress(from); const transactionResponse = yield this.signer.sendTransaction(Object.assign({ gasLimit: gas }, sendTx)); return { transactionResponse, hash: transactionResponse.hash }; }); } /** * Formats transaction result depending on the current provider. * * @param txHash - Transaction hash * @param tx - Transaction response * @returns The formatted transaction response */ toSafeRelayTxResult(txHash, tx) { tx['hash'] = tx['txHash']; delete tx['txHash']; return new Promise((resolve, reject) => resolve({ transactionResponse: new Promise((resolve, reject) => resolve(tx)), hash: txHash })); } toRocksideRelayTxResult(tx) { tx['hash'] = tx['transaction_hash']; delete tx['transaction_hash']; return new Promise((resolve, reject) => resolve({ transactionResponse: new Promise((resolve, reject) => resolve(tx)), hash: tx['hash'] })); } } exports.default = EthersAdapter; //# sourceMappingURL=index.js.map