navskit
Version:
Deploy TypeScript logic on Ethereum. Includes core library, CLI tools, and utilities.
109 lines (108 loc) • 3.98 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Tools = void 0;
exports.percentToWad = percentToWad;
const viem_1 = require("viem");
const deploy_1 = require("./deploy");
const protocol_kit_1 = __importDefault(require("@safe-global/protocol-kit"));
const deploy_2 = require("./deploy");
/**
* Convert a percentage (e.g. 50.3 for 50.3 %) to an 18-decimal “wad”.
*
* EigenLayer’s slashing code treats 1 wad = 1 × 10⁻¹⁸ of “one”.
* Because 100 % == 1.0, the conversion is:
*
* wad = (percent / 100) × 1e18 = percent × 1e16
*
* The result must be an integer, so we use BigInt.
*
* @param {number|string} pct A number in the range 0 … 100 (inclusive).
* @returns {bigint} The wad-scaled integer (0 … 1e18).
*/
function percentToWad(pct) {
const n = Number(pct);
if (!Number.isFinite(n))
throw new TypeError('percent must be a finite number');
if (n < 0 || n > 100)
throw new RangeError('percent must be between 0 and 100');
// n * 1e16 may exceed 2⁵³-1, so run the math in strings -> BigInt
const wad = BigInt(Math.round(n * 1e14)) * 10000n; // 1e14 × 1e4 = 1e18
return wad;
}
class Tools {
toolsParams;
calls = [];
constructor(toolsParams) {
this.toolsParams = toolsParams;
this.calls = [];
}
slash(operator, percentageOutOf100, description) {
if (percentageOutOf100 < 0 || percentageOutOf100 > 100) {
throw new Error('invalid percentage.');
}
this.calls.push({
to: process.env.ALLOCATION_MANAGER,
value: (0, viem_1.toHex)(0),
data: (0, viem_1.encodeFunctionData)({
abi: deploy_1.AllocationManagerAbi,
functionName: 'slashOperator',
args: [
this.toolsParams.avs,
{ operator, operatorSetId: deploy_2.DEFAULT_OPERATOR_SET_ID, strategies: [deploy_1.Addresses['11155111'].Strategy], wadsToSlash: [percentToWad(percentageOutOf100)], description }
]
})
});
}
eject(operator) {
this.calls.push({
to: process.env.ALLOCATION_MANAGER,
value: (0, viem_1.toHex)(0),
data: (0, viem_1.encodeFunctionData)({
abi: deploy_1.AllocationManagerAbi,
functionName: 'deregisterFromOperatorSets',
args: [
{ operator, avs: this.toolsParams.avs, operatorSetIds: [deploy_2.DEFAULT_OPERATOR_SET_ID] }
]
})
});
}
async getTransaction() {
if (this.calls.length === 0) {
return;
}
const mapCall = (call) => {
return {
target: call.to,
allowFailure: false,
callData: call.data
};
};
const to = deploy_1.Addresses[11155111].NavsMultisig;
const value = (0, viem_1.toHex)(0);
const data = (0, viem_1.encodeFunctionData)({
abi: deploy_1.Multicall3Abi,
functionName: 'aggregate3',
args: [
[
mapCall(this.calls[0]),
...this.calls.map(mapCall)
]
]
});
// TODO: what happens if gas parameters are different across different runs?...
const safe = await protocol_kit_1.default.init({
provider: process.env.NODE_SEPOLIA ?? 'https://rpc.sepolia.ethpandaops.io',
safeAddress: deploy_1.Addresses[11155111].NavsMultisig
});
const txn = await safe.createTransaction({
transactions: [
{ to, value, data }
]
});
return [txn, await safe.getTransactionHash(txn)];
}
}
exports.Tools = Tools;