Skip to content
Merged
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
56 changes: 28 additions & 28 deletions .gas-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -12,45 +12,45 @@ ZodiacModuleTest:testInitialState() (gas: 22128)
ZodiacModuleTest:testNonAvatarCannotAddGuard() (gas: 208)
ZodiacModuleTest:testNonAvatarCannotChangeTarget() (gas: 17739)
ZodiacModuleTest:testRawStorage() (gas: 67713)
BudgetTest:testAllowanceChain() (gas: 567409)
BudgetTest:testAllowanceIsKeptTrackOfOnMulti() (gas: 248829)
BudgetTest:testAllowanceIsKeptTrackOfOnSingle() (gas: 244802)
BudgetTest:testAllowanceSpenderWithRoleFlags() (gas: 213584)
BudgetTest:testBadTimeshiftsRevert() (gas: 29139)
BudgetTest:testCannotReinit() (gas: 20184)
BudgetTest:testCantExecuteIfNotAuthorized() (gas: 134431)
BudgetTest:testAllowanceChain() (gas: 567229)
BudgetTest:testAllowanceIsKeptTrackOfOnMulti() (gas: 248718)
BudgetTest:testAllowanceIsKeptTrackOfOnSingle() (gas: 244757)
BudgetTest:testAllowanceSpenderWithRoleFlags() (gas: 213539)
BudgetTest:testBadTimeshiftsRevert() (gas: 29094)
BudgetTest:testCannotReinit() (gas: 20228)
BudgetTest:testCantExecuteIfNotAuthorized() (gas: 134386)
BudgetTest:testCantExecuteInexistentAllowance() (gas: 18902)
BudgetTest:testCantExecuteMultiIfNotAuthorized() (gas: 136362)
BudgetTest:testCreateAllowance():(uint256) (gas: 134161)
BudgetTest:testCreateSuballowance():(uint256,uint256) (gas: 320221)
BudgetTest:testCreateSuballowanceWithInheritedRecurrency() (gas: 289735)
BudgetTest:testDisablingAllowanceBreaksChain() (gas: 577557)
BudgetTest:testCantExecuteMultiIfNotAuthorized() (gas: 136295)
BudgetTest:testCreateAllowance():(uint256) (gas: 134116)
BudgetTest:testCreateSuballowance():(uint256,uint256) (gas: 320131)
BudgetTest:testCreateSuballowanceWithInheritedRecurrency() (gas: 289645)
BudgetTest:testDisablingAllowanceBreaksChain() (gas: 577377)
BudgetTest:testInitialState() (gas: 23348)
BudgetTest:testInvalidSpenderReverts() (gas: 37311)
BudgetTest:testMultipleAllowances() (gas: 379002)
BudgetTest:testNonAdminCannotUpdateAllowanceParams() (gas: 340916)
BudgetTest:testNotOwnerCannotCreateTopLevelAllowance() (gas: 21545)
BudgetTest:testOnlyParentAdminCanDisableAllowance() (gas: 574509)
BudgetTest:testRevertOnBadInputToMultiPayment() (gas: 135782)
BudgetTest:testUpdateAllowanceParams() (gas: 150927)
TimeShiftLibEncodingTest:testDecodingGas() (gas: 498)
BudgetTest:testInvalidSpenderReverts() (gas: 37266)
BudgetTest:testMultipleAllowances() (gas: 378912)
BudgetTest:testNonAdminCannotUpdateAllowanceParams() (gas: 340826)
BudgetTest:testNotOwnerCannotCreateTopLevelAllowance() (gas: 21500)
BudgetTest:testOnlyParentAdminCanDisableAllowance() (gas: 574329)
BudgetTest:testRevertOnBadInputToMultiPayment() (gas: 135715)
BudgetTest:testUpdateAllowanceParams() (gas: 150882)
TimeShiftLibEncodingTest:testDecodingGas() (gas: 531)
TimeShiftLibEncodingTest:testEncodingGas() (gas: 634)
TimeShiftLibEncodingTest:testRoundtrips() (gas: 3364)
TimeShiftLibShiftTest:testDaily() (gas: 90945)
TimeShiftLibEncodingTest:testRoundtrips() (gas: 3319)
TimeShiftLibShiftTest:testDaily() (gas: 90942)
TimeShiftLibShiftTest:testGasWorstCase() (gas: 11413)
TimeShiftLibShiftTest:testMonthly() (gas: 53376)
TimeShiftLibShiftTest:testOffsets() (gas: 37146)
TimeShiftLibShiftTest:testQuarterly() (gas: 108287)
TimeShiftLibShiftTest:testSemiyearly() (gas: 106929)
TimeShiftLibShiftTest:testQuarterly() (gas: 108284)
TimeShiftLibShiftTest:testSemiyearly() (gas: 106926)
TimeShiftLibShiftTest:testWeekly() (gas: 55702)
TimeShiftLibShiftTest:testYearly() (gas: 35843)
RolesAuthTest:testExplicitAddrIsAuthorized() (gas: 9273)
RolesAuthTest:testRoleFlags(uint8) (runs: 256, μ: 43795, ~: 43795)
RolesAuthTest:testRoleFlagsEdgeCases() (gas: 48677)
FirmFactoryIntegrationTest:testExecutingPaymentsFromBudget() (gas: 1182442)
FirmFactoryIntegrationTest:testFactoryGas() (gas: 622323)
FirmFactoryIntegrationTest:testInitialState() (gas: 632679)
FirmFactoryIntegrationTest:testModuleUpgrades() (gas: 1700210)
FirmFactoryIntegrationTest:testExecutingPaymentsFromBudget() (gas: 1182419)
FirmFactoryIntegrationTest:testFactoryGas() (gas: 622412)
FirmFactoryIntegrationTest:testInitialState() (gas: 632768)
FirmFactoryIntegrationTest:testModuleUpgrades() (gas: 1700299)
UpgradeableModuleProxyFactoryTest:testReturnData() (gas: 10183)
UpgradeableModuleProxyFactoryTest:testRevert() (gas: 13344)
UpgradeableModuleProxyFactoryTest:testUpgrade() (gas: 127999)
Expand Down
28 changes: 14 additions & 14 deletions src/budget/Budget.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {TimeShiftLib, EncodedTimeShift} from "./TimeShiftLib.sol";

address constant NATIVE_ASSET = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
uint256 constant NO_PARENT_ID = 0;
uint64 constant INHERITED_RESET_TIME = 0;
uint40 constant INHERITED_RESET_TIME = 0;
address constant IMPL_INIT_ADDRESS = address(1);

/**
Expand All @@ -24,7 +24,7 @@ contract Budget is FirmBase, ZodiacModule, RolesAuth {
string public constant moduleId = "org.firm.budget";
uint256 public constant moduleVersion = 1;

using TimeShiftLib for uint64;
using TimeShiftLib for uint40;

////////////////////////////////////////////////////////////////////////////////
// INITIALIZATION
Expand All @@ -49,7 +49,7 @@ contract Budget is FirmBase, ZodiacModule, RolesAuth {
uint256 amount;
uint256 spent;
address token;
uint64 nextResetTime;
uint40 nextResetTime;
address spender;
EncodedTimeShift recurrency;
bool isDisabled;
Expand All @@ -65,7 +65,7 @@ contract Budget is FirmBase, ZodiacModule, RolesAuth {
address token,
uint256 amount,
EncodedTimeShift recurrency,
uint64 nextResetTime,
uint40 nextResetTime,
string name
);
event AllowanceStateChanged(uint256 indexed allowanceId, bool isEnabled);
Expand All @@ -78,7 +78,7 @@ contract Budget is FirmBase, ZodiacModule, RolesAuth {
address token,
address indexed to,
uint256 amount,
uint64 nextResetTime,
uint40 nextResetTime,
string description
);
event MultiPaymentExecuted(
Expand All @@ -87,7 +87,7 @@ contract Budget is FirmBase, ZodiacModule, RolesAuth {
address token,
address[] tos,
uint256[] amounts,
uint64 nextResetTime,
uint40 nextResetTime,
string description
);

Expand Down Expand Up @@ -122,7 +122,7 @@ contract Budget is FirmBase, ZodiacModule, RolesAuth {
EncodedTimeShift recurrency,
string memory name
) public returns (uint256 allowanceId) {
uint64 nextResetTime;
uint40 nextResetTime;

if (parentAllowanceId == NO_PARENT_ID) {
// Top-level allowances can only be created by the Safe
Expand All @@ -133,7 +133,7 @@ contract Budget is FirmBase, ZodiacModule, RolesAuth {
// For top-level allowances, recurrency needs to be set and cannot be zero
// applyShift reverts with InvalidTimeShift if recurrency is unspecified
// Therefore, nextResetTime is always greater than the current time
nextResetTime = uint64(block.timestamp).applyShift(recurrency);
nextResetTime = uint40(block.timestamp).applyShift(recurrency);
} else {
Allowance storage parentAllowance = _getAllowance(parentAllowanceId);

Expand All @@ -153,7 +153,7 @@ contract Budget is FirmBase, ZodiacModule, RolesAuth {
// Recurrency can be zero in sub-allowances and is inherited from the parent
if (!recurrency.isInherited()) {
// Will revert with InvalidTimeShift if recurrency is invalid
nextResetTime = uint64(block.timestamp).applyShift(recurrency);
nextResetTime = uint40(block.timestamp).applyShift(recurrency);
}
}

Expand Down Expand Up @@ -250,7 +250,7 @@ contract Budget is FirmBase, ZodiacModule, RolesAuth {
address token = allowance.token;

// Make sure the payment is within budget all the way up to its top-level budget
(uint64 nextResetTime,) = _checkAndUpdateAllowanceChain(allowanceId, amount);
(uint40 nextResetTime,) = _checkAndUpdateAllowanceChain(allowanceId, amount);

if (!_performTransfer(token, to, amount)) {
revert PaymentExecutionFailed(allowanceId, token, to, amount);
Expand Down Expand Up @@ -292,7 +292,7 @@ contract Budget is FirmBase, ZodiacModule, RolesAuth {
}
}

(uint64 nextResetTime,) = _checkAndUpdateAllowanceChain(allowanceId, totalAmount);
(uint40 nextResetTime,) = _checkAndUpdateAllowanceChain(allowanceId, totalAmount);

address token = allowance.token;
if (!_performMultiTransfer(token, tos, amounts)) {
Expand Down Expand Up @@ -386,7 +386,7 @@ contract Budget is FirmBase, ZodiacModule, RolesAuth {

function _checkAndUpdateAllowanceChain(uint256 allowanceId, uint256 amount)
internal
returns (uint64 nextResetTime, bool allowanceResets)
returns (uint40 nextResetTime, bool allowanceResets)
{
Allowance storage allowance = allowances[allowanceId]; // allowanceId always points to an existing allowance

Expand All @@ -401,9 +401,9 @@ contract Budget is FirmBase, ZodiacModule, RolesAuth {
} else {
nextResetTime = allowance.nextResetTime;

if (uint64(block.timestamp) >= nextResetTime) {
if (uint40(block.timestamp) >= nextResetTime) {
allowanceResets = true;
nextResetTime = uint64(block.timestamp).applyShift(allowance.recurrency);
nextResetTime = uint40(block.timestamp).applyShift(allowance.recurrency);
}

if (allowanceResets) {
Expand Down
26 changes: 13 additions & 13 deletions src/budget/TimeShiftLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,25 @@ pragma solidity 0.8.16;
// Formal verification for library and formula: https://twitter.com/Zellic_io/status/1510341868021854209
import {BokkyPooBahsDateTimeLibrary as DateTimeLib} from "datetime/BokkyPooBahsDateTimeLibrary.sol";

type EncodedTimeShift is bytes9;
type EncodedTimeShift is bytes6;

struct TimeShift {
TimeShiftLib.TimeUnit unit; // in the special case of seconds, offset doesn't apply
int64 offset;
int40 offset;
}

function encode(TimeShift memory shift) pure returns (EncodedTimeShift) {
return EncodedTimeShift.wrap(bytes9(abi.encodePacked(uint8(shift.unit), shift.offset)));
return EncodedTimeShift.wrap(bytes6(abi.encodePacked(uint8(shift.unit), shift.offset)));
}

function decode(EncodedTimeShift encoded) pure returns (TimeShiftLib.TimeUnit unit, int64 offset) {
uint72 encodedValue = uint72(EncodedTimeShift.unwrap(encoded));
unit = TimeShiftLib.TimeUnit(uint8(encodedValue >> 64));
offset = int64(uint64(uint72(encodedValue)));
function decode(EncodedTimeShift encoded) pure returns (TimeShiftLib.TimeUnit unit, int40 offset) {
uint48 encodedValue = uint48(EncodedTimeShift.unwrap(encoded));
unit = TimeShiftLib.TimeUnit(uint8(encodedValue >> 40));
offset = int40(uint40(uint48(encodedValue)));
}

function isInherited(EncodedTimeShift encoded) pure returns (bool) {
return EncodedTimeShift.unwrap(encoded) == bytes9(0);
return EncodedTimeShift.unwrap(encoded) == bytes6(0);
}

using {decode, isInherited} for EncodedTimeShift global;
Expand All @@ -43,10 +43,10 @@ library TimeShiftLib {

error InvalidTimeShift();

function applyShift(uint64 time, EncodedTimeShift shift) internal pure returns (uint64) {
(TimeUnit unit, int64 offset) = shift.decode();
function applyShift(uint40 time, EncodedTimeShift shift) internal pure returns (uint40) {
(TimeUnit unit, int40 offset) = shift.decode();

uint64 realTime = uint64(int64(time) + offset);
uint40 realTime = uint40(int40(time) + offset);
(uint256 y, uint256 m, uint256 d) = realTime.toDate();

if (unit == TimeUnit.Daily) {
Expand All @@ -66,7 +66,7 @@ library TimeShiftLib {
}

uint256 shiftedTs = DateTimeLib.timestampFromDateTime(y, m, d, 0, 0, 0);
return uint64(int64(uint64(shiftedTs)) - offset);
return uint40(int40(uint40(shiftedTs)) - offset);
}

/**
Expand All @@ -83,7 +83,7 @@ library TimeShiftLib {
return d2 <= daysInMonth ? (y, m, d2) : m < 12 ? (y, m + 1, d2 - daysInMonth) : (y + 1, 1, d2 - daysInMonth);
}

function toDate(uint64 timestamp) internal pure returns (uint256 y, uint256 m, uint256 d) {
function toDate(uint40 timestamp) internal pure returns (uint256 y, uint256 m, uint256 d) {
return DateTimeLib._daysToDate(timestamp / 1 days);
}
}
24 changes: 12 additions & 12 deletions src/budget/test/Budget.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ contract BudgetTest is FirmTest {
uint256 amount,
uint256 spent,
address token,
uint64 nextResetTime,
uint40 nextResetTime,
address spender,
EncodedTimeShift recurrency,
bool isDisabled
Expand All @@ -73,7 +73,7 @@ contract BudgetTest is FirmTest {
budget.setAllowanceAmount(allowanceId, 1);
budget.setAllowanceName(allowanceId, "new name");

(, uint256 amount,,, uint64 nextResetTime, address spender,,) = budget.allowances(allowanceId);
(, uint256 amount,,, uint40 nextResetTime, address spender,,) = budget.allowances(allowanceId);

assertEq(amount, 1);
assertEq(nextResetTime, 1 days);
Expand Down Expand Up @@ -103,7 +103,7 @@ contract BudgetTest is FirmTest {
}

function testAllowanceIsKeptTrackOfOnSingle() public {
uint64 initialTime = uint64(DateTimeLib.timestampFromDateTime(2022, 1, 1, 0, 0, 0));
uint40 initialTime = uint40(DateTimeLib.timestampFromDateTime(2022, 1, 1, 0, 0, 0));
uint256 allowanceId = 1;

vm.prank(address(avatar));
Expand All @@ -122,7 +122,7 @@ contract BudgetTest is FirmTest {
}

function testAllowanceIsKeptTrackOfOnMulti() public {
uint64 initialTime = uint64(DateTimeLib.timestampFromDateTime(2022, 1, 1, 0, 0, 0));
uint40 initialTime = uint40(DateTimeLib.timestampFromDateTime(2022, 1, 1, 0, 0, 0));
uint256 allowanceId = 1;

vm.prank(address(avatar));
Expand Down Expand Up @@ -152,7 +152,7 @@ contract BudgetTest is FirmTest {
}

function testMultipleAllowances() public {
uint64 initialTime = uint64(DateTimeLib.timestampFromDateTime(2022, 1, 1, 0, 0, 0));
uint40 initialTime = uint40(DateTimeLib.timestampFromDateTime(2022, 1, 1, 0, 0, 0));

uint256 firstAllowanceId = 1;
uint256 secondAllowanceId = 2;
Expand All @@ -172,7 +172,7 @@ contract BudgetTest is FirmTest {
}

function testCreateSuballowance() public returns (uint256 topLevelAllowance, uint256 subAllowance) {
uint64 initialTime = uint64(DateTimeLib.timestampFromDateTime(2022, 1, 1, 0, 0, 0));
uint40 initialTime = uint40(DateTimeLib.timestampFromDateTime(2022, 1, 1, 0, 0, 0));
vm.warp(initialTime);

vm.prank(address(avatar));
Expand Down Expand Up @@ -218,7 +218,7 @@ contract BudgetTest is FirmTest {
}

function testCreateSuballowanceWithInheritedRecurrency() public {
uint64 initialTime = uint64(DateTimeLib.timestampFromDateTime(2022, 1, 1, 0, 0, 0));
uint40 initialTime = uint40(DateTimeLib.timestampFromDateTime(2022, 1, 1, 0, 0, 0));
vm.warp(initialTime);

vm.prank(address(avatar));
Expand All @@ -234,7 +234,7 @@ contract BudgetTest is FirmTest {
}

function testAllowanceChain() public {
uint64 initialTime = uint64(DateTimeLib.timestampFromDateTime(2022, 1, 1, 0, 0, 0));
uint40 initialTime = uint40(DateTimeLib.timestampFromDateTime(2022, 1, 1, 0, 0, 0));
vm.warp(initialTime);

vm.prank(address(avatar));
Expand Down Expand Up @@ -381,7 +381,7 @@ contract BudgetTest is FirmTest {
address token,
address indexed to,
uint256 amount,
uint64 nextResetTime,
uint40 nextResetTime,
string descrition
);

Expand All @@ -390,9 +390,9 @@ contract BudgetTest is FirmTest {
uint256 allowanceId,
address to,
uint256 amount,
uint64 expectedNextResetTime
uint40 expectedNextResetTime
) public {
(,, uint256 initialSpent, address token, uint64 initialNextReset,, EncodedTimeShift shift,) =
(,, uint256 initialSpent, address token, uint40 initialNextReset,, EncodedTimeShift shift,) =
budget.allowances(allowanceId);

if (block.timestamp >= initialNextReset) {
Expand All @@ -404,7 +404,7 @@ contract BudgetTest is FirmTest {
emit PaymentExecuted(allowanceId, actor, token, to, amount, expectedNextResetTime, "");
budget.executePayment(allowanceId, to, amount, "");

(,, uint256 spent,, uint64 nextResetTime,,,) = budget.allowances(allowanceId);
(,, uint256 spent,, uint40 nextResetTime,,,) = budget.allowances(allowanceId);

assertEq(spent, initialSpent + amount);
assertEq(nextResetTime, shift.isInherited() ? 0 : expectedNextResetTime);
Expand Down
Loading