dp-contract-proxy-kit
Version:
Enable batched transactions and contract account interactions using a unique deterministic Gnosis Safe.
155 lines • 6.41 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());
});
};
import BigNumber from 'bignumber.js';
import { zeroAddress } from '../../utils/constants';
import { HttpMethod, sendRequest } from '../../utils/httpRequests';
import { TransactionManagerNames } from '../TransactionManager';
BigNumber.set({ EXPONENTIAL_AT: [-7, 255] });
class SafeRelayTransactionManager {
/**
* Initializes an instance of the Safe Relay Transaction Manager.
*
* @param options - The URL pointing to the Safe Transaction Service
* @returns The SafeRelayTransactionManager instance
*/
constructor(options) {
const { url } = options;
if (!url) {
throw new Error('url property missing from options');
}
this.url = url;
}
/**
* Returns the configuration of the Safe Relay Transaction Manager.
*
* @returns The name of the TransactionManager in use and the URL of the service
*/
get config() {
return {
name: TransactionManagerNames.SafeTxRelayManager,
url: this.url
};
}
/**
* Executes a list of transactions via the Safe Transaction Relay.
*
* @param options
* @returns The transaction response
*/
execTransactions({ ownerAccount, safeExecTxParams, contractManager, ethLibAdapter, isConnectedToSafe }) {
return __awaiter(this, void 0, void 0, function* () {
const { contract } = contractManager;
if (!contract) {
throw new Error('CPK Proxy contract uninitialized');
}
if (isConnectedToSafe) {
throw new Error('The use of the relay service is not supported when the CPK is connected to a Gnosis Safe');
}
const relayEstimations = yield this.getTransactionEstimations({
safe: contract.address,
to: safeExecTxParams.to,
value: new BigNumber(safeExecTxParams.value).toString(10),
data: safeExecTxParams.data,
operation: safeExecTxParams.operation
});
// TO-DO: dataGas will be obsolete. Check again when this endpoint is updated to v2
const tx = {
to: safeExecTxParams.to,
value: new BigNumber(safeExecTxParams.value).toString(10),
data: safeExecTxParams.data,
operation: safeExecTxParams.operation,
safeTxGas: relayEstimations.safeTxGas,
dataGas: relayEstimations.baseGas,
gasPrice: relayEstimations.gasPrice,
gasToken: relayEstimations.gasToken,
refundReceiver: zeroAddress,
nonce: relayEstimations.lastUsedNonce + 1
};
const txHash = yield contract.call('getTransactionHash', [
tx.to,
tx.value,
tx.data,
tx.operation,
tx.safeTxGas,
tx.dataGas,
tx.gasPrice,
tx.gasToken,
tx.refundReceiver,
tx.nonce
]);
const rsvSignature = yield this.signTransactionHash(ethLibAdapter, ownerAccount, txHash);
return this.sendTransactionToRelay({
url: this.url,
safe: contract.address,
tx,
signatures: [rsvSignature],
ethLibAdapter
});
});
}
getTransactionEstimations({ safe, to, value, data, operation, gasToken }) {
return __awaiter(this, void 0, void 0, function* () {
const body = {
safe,
to,
value,
data,
operation
};
if (gasToken) {
body.gasToken = gasToken;
}
const jsonResponse = yield sendRequest({
url: `${this.url}/api/v1/safes/${safe}/transactions/estimate/`,
method: HttpMethod.POST,
body: JSON.stringify(body),
expectedHttpCodeResponse: 200
});
return jsonResponse;
});
}
sendTransactionToRelay({ tx, safe, signatures, ethLibAdapter }) {
return __awaiter(this, void 0, void 0, function* () {
const jsonResponse = yield sendRequest({
url: `${this.url}/api/v1/safes/${safe}/transactions/`,
method: HttpMethod.POST,
body: JSON.stringify(Object.assign(Object.assign({ safe }, tx), { signatures })),
expectedHttpCodeResponse: 201
});
return ethLibAdapter.toSafeRelayTxResult(jsonResponse.txHash, jsonResponse.ethereumTx);
});
}
signTransactionHash(ethLibAdapter, ownerAccount, txHash) {
return __awaiter(this, void 0, void 0, function* () {
let sig = yield ethLibAdapter.signMessage(txHash, ownerAccount);
let sigV = parseInt(sig.slice(-2), 16);
switch (sigV) {
case 0:
case 1:
sigV += 31;
break;
case 27:
case 28:
sigV += 4;
break;
default:
throw new Error('Invalid signature');
}
sig = sig.slice(0, -2) + sigV.toString(16);
return {
r: new BigNumber('0x' + sig.slice(2, 66)).toString(10),
s: new BigNumber('0x' + sig.slice(66, 130)).toString(10),
v: new BigNumber('0x' + sig.slice(130, 132)).toString(10)
};
});
}
}
export default SafeRelayTransactionManager;
//# sourceMappingURL=index.js.map