Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: added velodrome test #2

Merged
merged 13 commits into from
Dec 14, 2023
93 changes: 93 additions & 0 deletions Velodrome-transactions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
{
"version": "1.0",
"chainId": "1",
"createdAt": 1698843459112,
"meta": {
"name": "Transactions Batch",
"description": "",
"txBuilderVersion": "1.16.3",
"createdFromSafeAddress": "0xf2964cCcB7CDA9e808aaBe8DB0DDDAF7890dd378",
"createdFromOwnerAddress": "",
"checksum": "0xdbc08311d3e010b14c94e185b27ff37d6413e31bf0677df5803dbad54fa3128a"
},
"transactions": [
{
"to": "0xFE67A4450907459c3e1FFf623aA927dD4e28c67a",
"value": "0",
"data": "0x095ea7b300000000000000000000000022f424bca11fe154c403c277b5f8dab54a4ba29b000000000000000000000000000000000000000000013da329b6336471800000",
"contractMethod": {
"inputs": [
{ "name": "spender", "type": "address", "internalType": "address" },
{ "name": "amount", "type": "uint256", "internalType": "uint256" }
],
"name": "approve",
"payable": false
},
"contractInputsValues": {
"spender": "0x22f424Bca11FE154c403c277b5F8dAb54a4bA29b",
"amount": "1500000000000000000000000"
}
},
{
"to": "0x22f424Bca11FE154c403c277b5F8dAb54a4bA29b",
"value": "0",
"data": "0xb6b55f25000000000000000000000000000000000000000000013da329b6336471800000",
"contractMethod": {
"inputs": [
{ "internalType": "uint256", "name": "_amount", "type": "uint256" }
],
"name": "deposit",
"payable": false
},
"contractInputsValues": { "_amount": "1500000000000000000000000" }
},
{
"to": "0x58b9cB810A68a7f3e1E4f8Cb45D1B9B3c79705E8",
"value": "0",
"data": "0x095ea7b30000000000000000000000008898b472c54c31894e3b9bb83cea802a5d0e63c6000000000000000000000000000000000000000000013da329b6336471800000",
"contractMethod": {
"inputs": [
{ "internalType": "address", "name": "spender", "type": "address" },
{ "internalType": "uint256", "name": "amount", "type": "uint256" }
],
"name": "approve",
"payable": false
},
"contractInputsValues": {
"spender": "0x8898B472C54c31894e3B9bb83cEA802a5d0e63C6",
"amount": "1500000000000000000000000"
}
},
{
"to": "0x8898B472C54c31894e3B9bb83cEA802a5d0e63C6",
"value": "0",
"data": "0x8aac16ba000000000000000000000000000000000000000000000000000000006f7074690000000000000000000000001a3c9dc0c3fb2d9bc435e28479b3d9b3e334334700000000000000000000000058b9cb810a68a7f3e1e4f8cb45d1b9b3c79705e80000000000000000000000001a3c9dc0c3fb2d9bc435e28479b3d9b3e3343347000000000000000000000000000000000000000000013da329b6336471800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000000",
"contractMethod": {
"inputs": [
{
"internalType": "uint32",
"name": "_destination",
"type": "uint32"
},
{ "internalType": "address", "name": "_to", "type": "address" },
{ "internalType": "address", "name": "_asset", "type": "address" },
{ "internalType": "address", "name": "_delegate", "type": "address" },
{ "internalType": "uint256", "name": "_amount", "type": "uint256" },
{ "internalType": "uint256", "name": "_slippage", "type": "uint256" },
{ "internalType": "bytes", "name": "_callData", "type": "bytes" }
],
"name": "xcall",
"payable": true
},
"contractInputsValues": {
"_destination": "1869640809",
"_to": "0x1a3C9dC0c3fb2D9bC435e28479b3d9b3e3343347",
"_asset": "0x58b9cB810A68a7f3e1E4f8Cb45D1B9B3c79705E8",
"_delegate": "0x1a3C9dC0c3fb2D9bC435e28479b3d9b3e3343347",
"_amount": "1500000000000000000000000",
"_slippage": "0",
"_callData": "0x"
}
}
]
}
194 changes: 194 additions & 0 deletions test/VelodromeProposal.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {Strings} from "@openzeppelin/utils/Strings.sol";
import {Ownable} from "@openzeppelin/access/Ownable.sol";
import {IERC20} from "@openzeppelin/token/ERC20/IERC20.sol";

import {MultiSendCallOnly} from "safe-contracts/libraries/MultiSendCallOnly.sol";

import {IXReceiver} from "@connext/interfaces/core/IXReceiver.sol";

import {IXERC20} from "./interfaces/IXERC20.sol";

import {ForgeHelper} from "./utils/ForgeHelper.sol";
import {ForkHelper} from "./utils/ForkHelper.sol";
import {AddressLookup} from "./utils/AddressLookup.sol";
import {ChainLookup} from "./utils/ChainLookup.sol";

import "forge-std/StdJson.sol";
import "forge-std/console.sol";

// Addresses ----

// 0xFE67A4450907459c3e1FFf623aA927dD4e28c67a - mainnet NEXT token (token1 on mainnet vault)

// 0x58b9cb810a68a7f3e1e4f8cb45d1b9b3c79705e8 - Optimism NEXT token (token0 on arb vault)

// --------

contract VelodromeProposal is ForgeHelper {
enum Operation {
Call,
DelegateCall
}

struct Transaction {
address to;
uint256 value;
bytes data;
Operation operation;
}

// ================== Libraries ==================
using stdJson for string;
using Strings for string;
using Strings for uint256;

// ================== Events ==================

// ================== Structs ==================

// ================== Storage ==================

// Fork management utilities
ForkHelper public FORK_HELPER;

// Transactions path
string public TRANSACTIONS_PATH = "/Velodrome-transactions.json";

// Number of transactions to execute in multisend data:
// 1. mainnet approval of NEXT to lockbox
// 2. mainnet deposit on lockbox
// 3. mainnet approval of xNEXT to connext
// 4. xcall xNEXT into connext

uint256 public NUMBER_TRANSACTIONS = 4;

// Amount to bridge into OP multisig
uint256 public LIQUIDITY_AMOUNT_OPTIMISM = 1500000 ether; // used in transactions

// ================== Setup ==================

function setUp() public {
// Create the fork helper for mainnet and optimism
uint256[] memory chains = new uint256[](2);
chains[0] = 1;
chains[1] = 10;

uint256[] memory blocks = new uint256[](2);

FORK_HELPER = new ForkHelper(chains, blocks);
vm.makePersistent(address(FORK_HELPER));

// Create the forks
FORK_HELPER.utils_createForks();
assertEq(FORK_HELPER.utils_getNetworksCount(), 2, "!forks");
}

function utils_generateTransactions()
public
view
returns (Transaction[] memory _transactions)
{
// Generate executable from `Velodrome-transactions.json`
string memory path = string.concat(vm.projectRoot(), TRANSACTIONS_PATH);

string memory json = vm.readFile(path);

// Generate the bytes of the multisend transactions
_transactions = new Transaction[](NUMBER_TRANSACTIONS);
for (uint256 i; i < NUMBER_TRANSACTIONS; i++) {
string memory baseJsonPath = string.concat(
".transactions[",
i.toString(),
"]"
);
address to = json.readAddress(string.concat(baseJsonPath, ".to"));
uint256 value = json.readUint(
string.concat(baseJsonPath, ".value")
);
// No way to check if data is null in json, this will revert if data is null
// TODO: add support to automatically generate data if its null
bytes memory data = json.readBytes(
string.concat(baseJsonPath, ".data")
);

// Add to transactions
_transactions[i] = Transaction({
to: to,
value: value,
data: data,
operation: Operation.Call
});
}
}

function utils_getXCallTo(
uint256 transactionIdx
) public view returns (address _to) {
// Generate executable from `Velodrome-transactions.json`
string memory path = string.concat(vm.projectRoot(), TRANSACTIONS_PATH);

string memory json = vm.readFile(path);
string memory jsonPath = string.concat(
".transactions[",
transactionIdx.toString(),
"].contractInputsValues._to"
);
_to = json.readAddress(jsonPath);
}

// ================== Tests ==================
function test_executableShouldPass() public {
// Generate the multisend transactions
// bytes memory transactions = utils_generateMultisendTransactions();
Transaction[] memory transactions = utils_generateTransactions();

// Select and prep mainnet fork
vm.selectFork(FORK_HELPER.forkIdsByChain(1));
address caller = AddressLookup.getConnextDao(1);
uint256 initial = IERC20(AddressLookup.getNEXTAddress(1)).balanceOf(
caller
);
vm.makePersistent(caller);

// Submit the transactions
// NOTE: This assumes signatures will be valid, and the batching of these transactions
// will be valid. Simply pranks and calls each function in a loop as DAO.
for (uint256 i; i < transactions.length; i++) {
// Send tx
vm.prank(caller);
(bool success, ) = transactions[i].to.call(transactions[i].data);
assertTrue(success, string.concat("!success @ ", i.toString()));
}

// Select and prep Optimism fork
vm.selectFork(FORK_HELPER.forkIdsByChain(10));
caller = AddressLookup.getConnext(10);
vm.makePersistent(caller);

// Process optimism xcall for `approval` by transferring to `to`
address to = utils_getXCallTo(3);
address asset = AddressLookup.getNEXTAddress(10);
vm.startPrank(caller);
// Mint on NEXT to caller
IXERC20(asset).mint(to, LIQUIDITY_AMOUNT_OPTIMISM);
// No calldata on the xcall
vm.stopPrank();

// Ensure the optimism balance increased
uint256 balance = IERC20(AddressLookup.getNEXTAddress(10)).balanceOf(
to
);
assertEq(balance, LIQUIDITY_AMOUNT_OPTIMISM, "!balance");

// Ensure the connext mainnet balance decreased
vm.selectFork(FORK_HELPER.forkIdsByChain(1));
assertEq(
IERC20(AddressLookup.getNEXTAddress(1)).balanceOf(AddressLookup.getConnextDao(1)),
initial - LIQUIDITY_AMOUNT_OPTIMISM,
"!balance"
);
}
}
7 changes: 6 additions & 1 deletion test/utils/ForkHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,12 @@ contract ForkHelper is ForgeHelper {
require(NETWORK_IDS.length > 0, "!networks");
for (uint256 i; i < NETWORK_IDS.length; i++) {
// create the fork
uint256 forkId = vm.createSelectFork(vm.envString(ChainLookup.getRpcEnvName(NETWORK_IDS[i])), FORK_BLOCKS[i]);
uint256 forkId;
if (FORK_BLOCKS[i] == 0) {
forkId = vm.createSelectFork(vm.envString(ChainLookup.getRpcEnvName(NETWORK_IDS[i])));
} else {
forkId = vm.createSelectFork(vm.envString(ChainLookup.getRpcEnvName(NETWORK_IDS[i])), FORK_BLOCKS[i]);
}
// update the mappings
forkIdsByChain[block.chainid] = forkId;
chainsByForkId[forkId] = block.chainid;
Expand Down
Loading