Skip to content

Commit

Permalink
feat: block-based lockup for pension
Browse files Browse the repository at this point in the history
  • Loading branch information
idea404 committed Nov 12, 2023
1 parent 1867551 commit a163f26
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 16 deletions.
36 changes: 30 additions & 6 deletions contracts/PensionAccount.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ contract PensionAccount is IAccount {
address public SHIB;
address public BTC;

// Expiry timestamp
uint256 public expiryTimestamp;
// Expiry block number
uint256 public expiryBlockNumber;

// State variables to track investments
uint256 public totalEthReceived;
Expand All @@ -40,13 +40,37 @@ contract PensionAccount is IAccount {
SHIB = _shib;
BTC = _btc;

// Set the expiry timestamp to 3 minutes from now
expiryTimestamp = block.timestamp + 3 minutes;
// Set the expiry to 10 blocks from the current block
expiryBlockNumber = block.number + 10;
}

// Modifier to check if the time lock has expired
// Helper function to convert uint256 to string
function uintToString(uint256 _i) internal pure returns (string memory) {
if (_i == 0) {
return "0";
}
uint256 j = _i;
uint256 length;
while (j != 0) {
length++;
j /= 10;
}
bytes memory bstr = new bytes(length);
uint256 k = length;
j = _i;
while (j != 0) {
bstr[--k] = bytes1(uint8(48 + j % 10));
j /= 10;
}
return string(bstr);
}

// Modifier to check if the block lock has expired
modifier afterExpiry() {
require(block.timestamp >= expiryTimestamp, "Action locked until expiry time");
require(block.number >= expiryBlockNumber,
string(abi.encodePacked("Current block: ", uintToString(block.number),
", Expiry block: ", uintToString(expiryBlockNumber),
". Action locked until expiry block.")));
_;
}

Expand Down
3 changes: 0 additions & 3 deletions demo/pension-fund-eth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,8 @@

import { ethers } from "ethers";
import { Provider, Wallet } from "zksync-web3";
import dotenv from "dotenv";
import { getDeployedContractDetailsFromVars, config } from "../deploy/utils";

dotenv.config();

const NETWORK = "zkSyncLocalnet";
const RPC_URL = config.L2RpcUrl;
const PRIVATE_KEY = config.firstWalletPrivateKey;
Expand Down
66 changes: 66 additions & 0 deletions demo/pension-send-eth.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,68 @@
// This script will:
// 1. Attempt to send ETH from the pension contract to another address.

import { ethers } from "ethers";
import { Provider, Wallet, utils, types } from "zksync-web3";
import { getDeployedContractDetailsFromVars, config } from "../deploy/utils";

const NETWORK = "zkSyncLocalnet";
const RPC_URL = config.L2RpcUrl;
const PRIVATE_KEY = config.firstWalletPrivateKey;

// Temporary wallet for testing - that is accepting one private key - and signs the transaction with it.
export class PensionWallet extends Wallet {
readonly accountAddress: string;

// accountAddress - is the account abstraction address for which, we'll use the private key to sign transactions.
constructor(
accountAddress: string,
privateKey: string,
providerL2: Provider,
) {
super(privateKey, providerL2);
this.accountAddress = accountAddress;
}

getAddress(): Promise<string> {
return Promise.resolve(this.accountAddress);
}

async signTransaction(transaction: types.TransactionRequest) {
const sig1 = await this.eip712.sign(transaction);
if (transaction.customData === undefined) {
throw new Error("Transaction customData is undefined");
}
transaction.customData.customSignature = sig1;
// @ts-ignore
return (0, utils.serialize)(transaction);
}
}

async function main() {
const provider = new Provider(RPC_URL);
const mainWallet = new Wallet(PRIVATE_KEY, provider);

// Load the contract address from vars.json
const paAddress = getDeployedContractDetailsFromVars(NETWORK, "PensionAccount").address;
// construct the wallet class for the pension account
const paWallet = new PensionWallet(paAddress, config.secondWalletPrivateKey, provider);
// send 10 ETH from the paAddress to the mainWallet
const balance = await provider.getBalance(mainWallet.address);
const tx = await paWallet.transfer({
to: mainWallet.address,
amount: ethers.utils.parseUnits("10", 18),
overrides: { type: 113 },
});
await tx.wait();
const newBalance = await provider.getBalance(mainWallet.address);
console.log(`Sent 10 ETH from Pension Account to Main Wallet`);
console.log(`Main Wallet balance before: ${ethers.utils.formatEther(balance)} ETH`);
console.log(`Main Wallet balance after: ${ethers.utils.formatEther(newBalance)} ETH`);
}

main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
4 changes: 2 additions & 2 deletions deploy/vars.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
"deployed": [
{
"name": "PensionAccountFactory",
"address": "0x4B5DF730c2e6b28E17013A1485E5d9BC41Efe021"
"address": "0x111C3E89Ce80e62EE88318C2804920D4c96f92bb"
},
{
"name": "PensionAccount",
"address": "0x7A4C681d869bfF421d752B696cd8eC6eD6c7EfBa"
"address": "0x58d487F21a6baCDAC552dfce6EE09365bD52dbE1"
}
]
}
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
"deploy:multisig": "hardhat deploy-zksync --script deploy-multisig.ts --network zkSyncLocalnet",
"deploy:pafactory": "hardhat deploy-zksync --script deploy-pafactory.ts --network zkSyncLocalnet",
"deploy:pension": "hardhat deploy-zksync --script deploy-pension.ts --network zkSyncLocalnet",
"demo:fund-pension": "ts-node demo/pension-fund-eth.ts",
"demo:withdraw-pension": "ts-node demo/pension-send-eth.ts"
"demo:pension-setup": "yarn deploy:pafactory && yarn deploy:pension && yarn demo:pension-fund",
"demo:pension-fund": "ts-node demo/pension-fund-eth.ts",
"demo:pension-withdraw": "ts-node demo/pension-send-eth.ts"
},
"devDependencies": {
"@types/chai": "^4.3.10",
Expand Down
2 changes: 1 addition & 1 deletion test/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ describe("Account Abstraction Tests", function () {
// expect to fail
expect(true).to.be.false;
} catch (e) {
expect(e.message).to.contains("execution reverted: Failed to pay for the transaction: Action locked until expiry time");
expect(e.message).to.contains("execution reverted: Failed to pay for the transaction: Action locked until expiry block");
}
const balance = (await provider.getBalance(ownerWallet.address)).toBigInt();
const difference = balanceBefore - balance;
Expand Down
13 changes: 11 additions & 2 deletions test/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { utils, Wallet, Provider, types, EIP712Signer } from "zksync-web3";
import { utils, Wallet, Provider, types } from "zksync-web3";
import * as hre from "hardhat";
import { ethers } from "ethers";
import { Deployer } from "@matterlabs/hardhat-zksync-deploy";
Expand Down Expand Up @@ -122,7 +122,6 @@ export class MultiSigWallet extends Wallet {
// Temporary wallet for testing - that is accepting one private key - and signs the transaction with it.
export class PensionWallet extends Wallet {
readonly accountAddress: string;
otherWallet: Wallet;

// accountAddress - is the account abstraction address for which, we'll use the private key to sign transactions.
constructor(
Expand Down Expand Up @@ -174,3 +173,13 @@ function createMockAddress(base: string) {
const paddingLength = 40 - baseHex.length; // Calculate padding length
return '0x' + baseHex + '0'.repeat(paddingLength);
}

// Helper function to advance the blockchain by a specified number of blocks
export async function advanceBlocks(numberOfBlocks) {
for (let i = 0; i < numberOfBlocks; i++) {
await hre.network.provider.request({
method: "evm_mine",
params: [],
});
}
}

0 comments on commit a163f26

Please sign in to comment.