Skip to content

Commit

Permalink
feat: community-proposal
Browse files Browse the repository at this point in the history
  • Loading branch information
prathmeshkhandelwal1 committed Dec 22, 2023
1 parent f60605a commit 077b734
Show file tree
Hide file tree
Showing 2 changed files with 296 additions and 0 deletions.
93 changes: 93 additions & 0 deletions community-transactions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
{
"version": "1.0",
"chainId": "1",
"createdAt": 1703230404439,
"meta": {
"name": "Transactions Batch",
"description": "",
"txBuilderVersion": "1.16.3",
"createdFromSafeAddress": "0xf2964cCcB7CDA9e808aaBe8DB0DDDAF7890dd378",
"createdFromOwnerAddress": "",
"checksum": "0xbc303aae88a7c32de7c047b809fe56f676640630255cce46ad7d783348f452e7"
},
"transactions": [
{
"to": "0xFE67A4450907459c3e1FFf623aA927dD4e28c67a",
"value": "0",
"data": "0x095ea7b300000000000000000000000022f424bca11fe154c403c277b5f8dab54a4ba29b00000000000000000000000000000000000000000000fe1c215e8f838e000000",
"contractMethod": {
"inputs": [
{ "internalType": "address", "name": "spender", "type": "address" },
{ "internalType": "uint256", "name": "amount", "type": "uint256" }
],
"name": "approve",
"payable": false
},
"contractInputsValues": {
"spender": "0x22f424Bca11FE154c403c277b5F8dAb54a4bA29b",
"amount": "1200000000000000000000000"
}
},
{
"to": "0x22f424Bca11FE154c403c277b5F8dAb54a4bA29b",
"value": "0",
"data": "0xb6b55f2500000000000000000000000000000000000000000000fe1c215e8f838e000000",
"contractMethod": {
"inputs": [
{ "internalType": "uint256", "name": "_amount", "type": "uint256" }
],
"name": "deposit",
"payable": false
},
"contractInputsValues": { "_amount": "1200000000000000000000000" }
},
{
"to": "0x58b9cB810A68a7f3e1E4f8Cb45D1B9B3c79705E8",
"value": "0",
"data": "0x095ea7b30000000000000000000000008898b472c54c31894e3b9bb83cea802a5d0e63c600000000000000000000000000000000000000000000fe1c215e8f838e000000",
"contractMethod": {
"inputs": [
{ "internalType": "address", "name": "spender", "type": "address" },
{ "internalType": "uint256", "name": "amount", "type": "uint256" }
],
"name": "approve",
"payable": false
},
"contractInputsValues": {
"spender": "0x8898B472C54c31894e3B9bb83cEA802a5d0e63C6",
"amount": "1200000000000000000000000"
}
},
{
"to": "0x8898B472C54c31894e3B9bb83cEA802a5d0e63C6",
"value": "0",
"data": "0x8aac16ba00000000000000000000000000000000000000000000000000000000706f6c79000000000000000000000000b369f4d4b8b5e33516418ddd877194530812aa7100000000000000000000000058b9cb810a68a7f3e1e4f8cb45d1b9b3c79705e8000000000000000000000000b369f4d4b8b5e33516418ddd877194530812aa7100000000000000000000000000000000000000000000fe1c215e8f838e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000",
"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": "1886350457",
"_to": "0xb369f4d4B8b5e33516418dDd877194530812AA71",
"_asset": "0x58b9cB810A68a7f3e1E4f8Cb45D1B9B3c79705E8",
"_delegate": "0xb369f4d4B8b5e33516418dDd877194530812AA71",
"_amount": "1200000000000000000000000",
"_slippage": "0",
"_callData": "0x"
}
}
]
}
203 changes: 203 additions & 0 deletions test/CommunityProposal.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
// 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 - Polygon NEXT token (token0 on arb vault)

// --------

contract CommunityProposal 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 = "/community-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 POL multisig
uint256 public LIQUIDITY_AMOUNT_POLYGON = 1200000 ether; // used in transactions

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

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

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 Polygon fork
vm.selectFork(FORK_HELPER.forkIdsByChain(137));
caller = AddressLookup.getConnext(137);
vm.makePersistent(caller);

// Process polygon xcall for `approval` by transferring to `to`
address to = utils_getXCallTo(3);
address asset = AddressLookup.getNEXTAddress(137);
vm.startPrank(caller);
uint256 initialbalance = IERC20(AddressLookup.getNEXTAddress(137))
.balanceOf(to);

// Mint on NEXT to caller
IXERC20(asset).mint(to, LIQUIDITY_AMOUNT_POLYGON);
// No calldata on the xcall
vm.stopPrank();

// Ensure the polygon balance increased
uint256 balance = IERC20(AddressLookup.getNEXTAddress(137)).balanceOf(
to
);
assertEq(
balance,
LIQUIDITY_AMOUNT_POLYGON + initialbalance,
"!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_POLYGON,
"!balance"
);
}
}

0 comments on commit 077b734

Please sign in to comment.