Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion contracts/core/Vault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ contract Vault is ReentrancyGuard, IVault {
getMaxPrice(_token);
}

function clearTokenConfig(address _token) external {
function clearTokenConfig(address _token) external override {
_onlyGov();
_validate(whitelistedTokens[_token], 13);
totalTokenWeights = totalTokenWeights.sub(tokenWeights[_token]);
Expand Down
1 change: 1 addition & 0 deletions contracts/core/interfaces/IVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ interface IVault {
bool _isStable,
bool _isShortable
) external;
function clearTokenConfig(address _token) external;

function setPriceFeed(address _priceFeed) external;
function withdrawFees(address _token, address _receiver) external returns (uint256);
Expand Down
15 changes: 15 additions & 0 deletions contracts/peripherals/Timelock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ contract Timelock is ITimelock, BasicMulticall {
bool isStable,
bool isShortable
);
event SignalRemoveTokenFromWhitelist(address token);
event ClearAction(bytes32 action);

modifier onlyAdmin() {
Expand Down Expand Up @@ -594,6 +595,20 @@ contract Timelock is ITimelock, BasicMulticall {
IMintable(glp).burn(account, amount);
}

function signalRemoveTokenFromWhitelist(address vault, address token) external onlyAdmin {
bytes32 action = keccak256(abi.encodePacked("removeTokenFromWhitelist", vault, token));
_setPendingAction(action);
emit SignalRemoveTokenFromWhitelist(token);
}

function removeTokenFromWhitelist(address vault, address token) external onlyAdmin {
bytes32 action = keccak256(abi.encodePacked("removeTokenFromWhitelist", vault, token));
_validateAction(action);
_clearAction(action);

IVault(vault).clearTokenConfig(token);
}

function cancelAction(bytes32 _action) external onlyAdmin {
_clearAction(_action);
}
Expand Down
14 changes: 9 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,14 @@
"typechain": "^5.1.2"
},
"dependencies": {
"@layerzerolabs/devtools": "~0.4.8",
"@layerzerolabs/lz-v2-utilities": "^3.0.86",
"@layerzerolabs/metadata-tools": "^0.4.1",
"@nomicfoundation/hardhat-verify": "^2",
"@safe-global/api-kit": "^5.0.1",
"@safe-global/protocol-kit": "^8.0.1",
"@safe-global/sdk-starter-kit": "^4.1.1",
"@safe-global/types-kit": "^4.0.1",
"@synapseprotocol/sdk": "^0.94.1",
"@uniswap/sdk-core": "^3.0.1",
"@uniswap/v3-sdk": "^3.8.2",
Expand All @@ -39,10 +46,7 @@
"csv-parse": "^4.16.3",
"express": "^4.19.2",
"get-all-files": "^4.1.0",
"solidity-code-metrics": "^0.0.25",
"@layerzerolabs/devtools": "~0.4.8",
"@layerzerolabs/lz-v2-utilities": "^3.0.86",
"@layerzerolabs/metadata-tools": "^0.4.1",
"googleapis": "171.4.0"
"googleapis": "171.4.0",
"solidity-code-metrics": "^0.0.25"
}
}
1 change: 1 addition & 0 deletions scripts/peripherals/deployTimelock.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ async function main() {
"0xfb481D70f8d987c1AE3ADc90B7046e39eb6Ad64B", // kr
"0x6091646D0354b03DD1e9697D33A7341d8C93a6F5", // xhiroz
"0xD2A54e7AFC36f6D09627c7c9ABdd033Ac3800613", // Tano
"0xeAA5600595a64a23480b2DF5FCA35A2867c912Ea", // Sol
]

const timelock = await deployContract("Timelock", [
Expand Down
75 changes: 75 additions & 0 deletions scripts/peripherals/removeTokenFromVaultWhitelist.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
const { contractAt } = require("../shared/helpers")
const { createSafeClient } = require("@safe-global/sdk-starter-kit")
const { proposeSafeTransaction } = require("../shared/safeHelpers")

const hre = require("hardhat");
const network = hre.network.name;
const tokens = require('../core/tokens')[network];

async function getArbValues() {
const vault = await contractAt("Vault", "0x489ee077994B6658eAfA855C308275EAd8097C4A")
const timelock = await contractAt("Timelock", "0x718507c37AA801A4b37aAE70dF8B2cB0bd4674b5")
return { vault, timelock }
}

async function getAvaxValues() {
const vault = await contractAt("Vault", "0x9ab2De34A33fB459b538c43f251eB825645e8595")
const timelock = await contractAt("Timelock", "0xe089F0eDc8efB1172Dae20CEa041eB4B9dc7d468")
return { vault, timelock }
}

async function getValues() {
if (network === "arbitrum") {
return getArbValues()
}

if (network === "avax") {
return getAvaxValues()
}
}

async function main() {
const signerPK = process.env.SIGNER_KEY;
const safeApiKey = process.env.SAFE_API_KEY;

const action = process.env.ACTION || "signal";

const { vault, timelock } = await getValues()
let rawTx;
if (action === 'signal') {
rawTx = await timelock.populateTransaction.signalRemoveTokenFromWhitelist(vault.address, tokens.mim.address)
} else if (action === 'finalize') {
rawTx = await timelock.populateTransaction.removeTokenFromWhitelist(vault.address, tokens.mim.address)
} else {
throw new Error("Unknown action type")
}

const safeClient = await createSafeClient({
provider: hre.network.config.url,
signer: signerPK,
safeAddress: '0x58F582455b54d7c83d03BCeed95FAf72B37fdDD7',
apiKey: safeApiKey
})

const transactions = [{
to: rawTx.to,
data: rawTx.data,
value: '0',
}]

const nonce = process.env.SAFE_NONCE ? parseInt(process.env.SAFE_NONCE, 10) : undefined

const { safeTxHash, nonce: usedNonce } = await proposeSafeTransaction(safeClient, {
transactions,
nonce,
})

console.log(`Tx proposed with nonce ${usedNonce}, hash: ${safeTxHash}`);
}

main()
.then(() => process.exit(0))
.catch(error => {
console.error(error)
process.exit(1)
})
25 changes: 17 additions & 8 deletions scripts/peripherals/setGov.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ const network = (process.env.HARDHAT_NETWORK || 'mainnet');

async function getArbValues(signer) {
const target = await contractAt("Vault", "0x489ee077994B6658eAfA855C308275EAd8097C4A")
const nextTimelock = await contractAt("Timelock", "0x6A9215C9c148ca68E11aA8534A413B099fd6798f", signer)
const nextTimelock = await contractAt("Timelock", "0x718507c37AA801A4b37aAE70dF8B2cB0bd4674b5")
return { target, nextTimelock }
}

async function getAvaxValues(signer) {
const target = await contractAt("Vault", "0x9ab2De34A33fB459b538c43f251eB825645e8595")
const nextTimelock = await contractAt("Timelock", "0x5aeCDD22cDA7D2010631D71b268D5479e1d2B8f4", signer)
const nextTimelock = await contractAt("Timelock", "0xe089F0eDc8efB1172Dae20CEa041eB4B9dc7d468", signer)
return { target, nextTimelock }
}

Expand All @@ -26,13 +26,22 @@ async function getValues(signer) {
}

async function main() {
const signer = await getFrameSigner()

const { target, nextTimelock } = await getValues(signer)
const prevTimelock = await contractAt("Timelock", await target.gov(), signer)
const { target, nextTimelock } = await getValues(null)
const prevTimelock = await contractAt("Timelock", await target.gov())

// await sendTxn(prevTimelock.signalSetGov(target.address, nextTimelock.address), "prevTimelock.signalSetGov(nextTimelock)")
// await sendTxn(nextTimelock.signalSetGov(target.address, prevTimelock.address), "nextTimelock.signalSetGov(prevTimelock)")
const action = process.env.ACTION || "signal";
let rawTx;
if (action === 'signal') {
rawTx = await prevTimelock.populateTransaction.signalSetGov(target.address, nextTimelock.address)
} else if (action === 'finalize') {
rawTx = await nextTimelock.populateTransaction.acceptGov(target.address)
} else {
throw new Error("Unknown action type")
}

await sendTxn(prevTimelock.signalSetGov(target.address, nextTimelock.address), "prevTimelock.signalSetGov(nextTimelock)")
await sendTxn(nextTimelock.signalSetGov(target.address, prevTimelock.address), "nextTimelock.signalSetGov(prevTimelock)")
console.log(rawTx);
}

main()
Expand Down
196 changes: 196 additions & 0 deletions scripts/peripherals/signSafeDeleteRequest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
const { ArgumentParser } = require("argparse");
const { ethers } = require("ethers");
const hre = require("hardhat");

const {
ARBITRUM_DEPLOY_KEY,
AVAX_DEPLOY_KEY,
MAINNET_DEPLOY_KEY,
} = require("../../env.json");

const DEPLOY_KEYS = {
arbitrum: ARBITRUM_DEPLOY_KEY,
avax: AVAX_DEPLOY_KEY,
mainnet: MAINNET_DEPLOY_KEY,
};

function getTotp(unixSeconds) {
const timestamp = unixSeconds !== undefined
? unixSeconds
: Math.floor(Date.now() / 1000);
return Math.floor(timestamp / 3600);
}

function buildDeleteRequestTypedData({ chainId, safeAddress, safeTxHash, totp }) {
return {
types: {
EIP712Domain: [
{ name: "name", type: "string" },
{ name: "version", type: "string" },
{ name: "chainId", type: "uint256" },
{ name: "verifyingContract", type: "address" },
],
DeleteRequest: [
{ name: "safeTxHash", type: "bytes32" },
{ name: "totp", type: "uint256" },
],
},
primaryType: "DeleteRequest",
domain: {
name: "Safe Transaction Service",
version: "1.0",
chainId,
verifyingContract: safeAddress,
},
message: {
safeTxHash,
totp,
},
};
}

function getSignerPrivateKey(network) {
if (process.env.SIGNER_KEY) {
return process.env.SIGNER_KEY;
}

const deployKey = DEPLOY_KEYS[network];
if (!deployKey) {
throw new Error(
`No signer key found. Set SIGNER_KEY or use a supported network deploy key (${Object.keys(DEPLOY_KEYS).join(", ")})`
);
}

return deployKey;
}

async function signDeleteRequest({
chainId,
safeAddress,
safeTxHash,
totp,
privateKey,
}) {
const typedData = buildDeleteRequestTypedData({
chainId,
safeAddress,
safeTxHash,
totp,
});

const signer = new ethers.Wallet(privateKey);
const signature = await signer._signTypedData(
typedData.domain,
{ DeleteRequest: typedData.types.DeleteRequest },
typedData.message
);

const recoveredAddress = ethers.utils.verifyTypedData(
typedData.domain,
{ DeleteRequest: typedData.types.DeleteRequest },
typedData.message,
signature
);

if (recoveredAddress.toLowerCase() !== signer.address.toLowerCase()) {
throw new Error("Signature verification failed");
}

return {
typedData,
signature,
signer: signer.address,
totp,
};
}

function getArg(name, envName) {
if (process.env[envName]) {
return process.env[envName];
}

return undefined;
}

async function main() {
const parser = new ArgumentParser({
description: "Sign a Safe Transaction Service DeleteRequest with an EOA",
});
parser.add_argument("--safe-tx-hash", {
help: "Safe transaction hash (bytes32). Env: SAFE_TX_HASH",
default: getArg("safe-tx-hash", "SAFE_TX_HASH"),
});
parser.add_argument("--safe-address", {
help: "Safe address used as verifyingContract. Env: SAFE_ADDRESS",
default: getArg("safe-address", "SAFE_ADDRESS"),
});
parser.add_argument("--chain-id", {
help: "Chain ID (defaults to Hardhat network chainId). Env: CHAIN_ID",
type: "int",
default: process.env.CHAIN_ID ? parseInt(process.env.CHAIN_ID, 10) : undefined,
});
parser.add_argument("--totp", {
help: "TOTP value (defaults to floor(unix_timestamp / 3600)). Env: TOTP",
type: "int",
default: process.env.TOTP ? parseInt(process.env.TOTP, 10) : undefined,
});
parser.add_argument("--unix-timestamp", {
help: "Unix timestamp in seconds used to compute TOTP. Env: UNIX_TIMESTAMP",
type: "int",
default: process.env.UNIX_TIMESTAMP ? parseInt(process.env.UNIX_TIMESTAMP, 10) : undefined,
});

const args = parser.parse_args();
const network = hre.network.name;
const chainId = args.chain_id !== undefined ? args.chain_id : hre.network.config.chainId;

if (!args.safe_tx_hash) {
throw new Error("safe-tx-hash is required (pass --safe-tx-hash or set SAFE_TX_HASH)");
}

if (!args.safe_address) {
throw new Error("safe-address is required (pass --safe-address or set SAFE_ADDRESS)");
}

if (!chainId) {
throw new Error("Chain ID is required. Pass --chain-id, set CHAIN_ID, or run with --network");
}

const totp = args.totp !== undefined
? args.totp
: getTotp(args.unix_timestamp !== undefined ? args.unix_timestamp : undefined);

const privateKey = getSignerPrivateKey(network);
const result = await signDeleteRequest({
chainId,
safeAddress: ethers.utils.getAddress(args.safe_address),
safeTxHash: args.safe_tx_hash,
totp,
privateKey,
});

console.log(JSON.stringify({
chainId,
safeAddress: result.typedData.domain.verifyingContract,
safeTxHash: result.typedData.message.safeTxHash,
totp: result.totp,
signer: result.signer,
signature: result.signature,
typedData: result.typedData,
}, null, 2));
}

if (require.main === module) {
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
}

module.exports = {
getTotp,
buildDeleteRequestTypedData,
signDeleteRequest,
};
Loading