dp-contract-proxy-kit
Version:
Enable batched transactions and contract account interactions using a unique deterministic Gnosis Safe.
271 lines • 10.2 kB
JavaScript
;
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