Skip to content

Commit

Permalink
Aggregated improvements of solidity semantic money library (#1928)
Browse files Browse the repository at this point in the history
* fix a big of shift_flow2a(BP, BP)

* SemanticMoney: replace inv() with unary '-' operator

* build ethereum-contracts when solidity library updated

* SemanticMoney: complete set of user defined operators
  • Loading branch information
hellwolf authored Apr 18, 2024
1 parent b6ae680 commit cdaff8d
Show file tree
Hide file tree
Showing 6 changed files with 191 additions and 41 deletions.
84 changes: 64 additions & 20 deletions packages/solidity-semantic-money/src/SemanticMoney.sol
Original file line number Diff line number Diff line change
Expand Up @@ -68,42 +68,67 @@ pragma solidity ^0.8.19;
*/
type Time is uint32;
function mt_t_eq(Time a, Time b) pure returns (bool) { return Time.unwrap(a) == Time.unwrap(b); }
function mt_t_neq(Time a, Time b) pure returns (bool) { return Time.unwrap(a) != Time.unwrap(b); }
function mt_t_le(Time a, Time b) pure returns (bool) { return Time.unwrap(a) < Time.unwrap(b); }
function mt_t_lte(Time a, Time b) pure returns (bool) { return Time.unwrap(a) <= Time.unwrap(b); }
function mt_t_gt(Time a, Time b) pure returns (bool) { return Time.unwrap(a) > Time.unwrap(b); }
function mt_t_gte(Time a, Time b) pure returns (bool) { return Time.unwrap(a) >= Time.unwrap(b); }
function mt_t_add_t(Time a, Time b) pure returns (Time) { return Time.wrap(Time.unwrap(a) + Time.unwrap(b)); }
function mt_t_sub_t(Time a, Time b) pure returns (Time) { return Time.wrap(Time.unwrap(a) - Time.unwrap(b)); }
using { mt_t_eq as ==, mt_t_add_t as +, mt_t_sub_t as - } for Time global;
using { mt_t_eq as ==, mt_t_neq as !=, mt_t_le as <, mt_t_lte as <=, mt_t_gt as >, mt_t_gte as >=,
mt_t_add_t as +, mt_t_sub_t as - } for Time global;

/**
* @title Unit value of monetary value represented with 256bits of signed integer.
*/
type Value is int256;
function mt_v_eq(Value a, Value b) pure returns (bool) { return Value.unwrap(a) == Value.unwrap(b); }
function mt_v_neq(Value a, Value b) pure returns (bool) { return Value.unwrap(a) != Value.unwrap(b); }
function mt_v_le(Value a, Value b) pure returns (bool) { return Value.unwrap(a) < Value.unwrap(b); }
function mt_v_lte(Value a, Value b) pure returns (bool) { return Value.unwrap(a) <= Value.unwrap(b); }
function mt_v_gt(Value a, Value b) pure returns (bool) { return Value.unwrap(a) > Value.unwrap(b); }
function mt_v_gte(Value a, Value b) pure returns (bool) { return Value.unwrap(a) >= Value.unwrap(b); }
function mt_v_add_v(Value a, Value b) pure returns (Value) { return Value.wrap(Value.unwrap(a) + Value.unwrap(b)); }
function mt_v_sub_v(Value a, Value b) pure returns (Value) { return Value.wrap(Value.unwrap(a) - Value.unwrap(b)); }
function mt_v_inv(Value a) pure returns (Value) { return Value.wrap(-Value.unwrap(a)); }
using { mt_v_eq as ==, mt_v_add_v as +, mt_v_sub_v as -, mt_v_inv as - } for Value global;
using { mt_v_eq as ==, mt_v_neq as !=, mt_v_le as <, mt_v_lte as <=, mt_v_gt as >, mt_v_gte as >=,
mt_v_add_v as +, mt_v_sub_v as -, mt_v_inv as - } for Value global;

/**
* @title Number of units represented with half the size of `Value`.
*/
type Unit is int128;
function mt_u_eq(Unit a, Unit b) pure returns (bool) { return Unit.unwrap(a) == Unit.unwrap(b); }
function mt_u_neq(Unit a, Unit b) pure returns (bool) { return Unit.unwrap(a) != Unit.unwrap(b); }
function mt_u_le(Unit a, Unit b) pure returns (bool) { return Unit.unwrap(a) < Unit.unwrap(b); }
function mt_u_lte(Unit a, Unit b) pure returns (bool) { return Unit.unwrap(a) <= Unit.unwrap(b); }
function mt_u_gt(Unit a, Unit b) pure returns (bool) { return Unit.unwrap(a) > Unit.unwrap(b); }
function mt_u_gte(Unit a, Unit b) pure returns (bool) { return Unit.unwrap(a) >= Unit.unwrap(b); }
function mt_u_add_u(Unit a, Unit b) pure returns (Unit) { return Unit.wrap(Unit.unwrap(a) + Unit.unwrap(b)); }
function mt_u_sub_u(Unit a, Unit b) pure returns (Unit) { return Unit.wrap(Unit.unwrap(a) - Unit.unwrap(b)); }
function mt_u_inv(Unit a) pure returns (Unit) { return Unit.wrap(-Unit.unwrap(a)); }
using { mt_u_eq as ==, mt_u_add_u as +, mt_u_sub_u as -, mt_u_inv as - } for Unit global;
using { mt_u_eq as ==, mt_u_neq as !=, mt_u_le as <, mt_u_lte as <=, mt_u_gt as >, mt_u_gte as >=,
mt_u_add_u as +, mt_u_sub_u as -, mt_u_inv as - } for Unit global;

/**
* @title FlowRate value represented with half the size of `Value`.
*/
type FlowRate is int128;
function mt_r_eq(FlowRate a, FlowRate b) pure returns (bool) { return FlowRate.unwrap(a) == FlowRate.unwrap(b); }
function mt_r_neq(FlowRate a, FlowRate b) pure returns (bool) { return FlowRate.unwrap(a) != FlowRate.unwrap(b); }
function mt_r_le(FlowRate a, FlowRate b) pure returns (bool) { return FlowRate.unwrap(a) < FlowRate.unwrap(b); }
function mt_r_lte(FlowRate a, FlowRate b) pure returns (bool) { return FlowRate.unwrap(a) <= FlowRate.unwrap(b); }
function mt_r_gt(FlowRate a, FlowRate b) pure returns (bool) { return FlowRate.unwrap(a) > FlowRate.unwrap(b); }
function mt_r_gte(FlowRate a, FlowRate b) pure returns (bool) { return FlowRate.unwrap(a) >= FlowRate.unwrap(b); }
function mt_r_add_r(FlowRate a, FlowRate b) pure returns (FlowRate) {
return FlowRate.wrap(FlowRate.unwrap(a) + FlowRate.unwrap(b));
}
function mt_r_sub_r(FlowRate a, FlowRate b) pure returns (FlowRate) {
return FlowRate.wrap(FlowRate.unwrap(a) - FlowRate.unwrap(b));
}
using { mt_r_eq as ==, mt_r_add_r as +, mt_r_sub_r as - } for FlowRate global;
function mt_r_inv(FlowRate a) pure returns (FlowRate) { return FlowRate.wrap(-FlowRate.unwrap(a)); }
using { mt_r_eq as ==, mt_r_neq as !=, mt_r_le as <, mt_r_lte as <=, mt_r_gt as >, mt_r_gte as >=,
mt_r_add_r as +, mt_r_sub_r as -, mt_r_inv as - } for FlowRate global;

/**
* @dev Additional helper functions for the monetary types
Expand All @@ -113,22 +138,34 @@ using { mt_r_eq as ==, mt_r_add_r as +, mt_r_sub_r as - } for FlowRate global;
* Read more at: https://github.com/ethereum/solidity/issues/11969#issuecomment-1448445474
*/
library AdditionalMonetaryTypeHelpers {
function inv(Value x) internal pure returns (Value) {
return Value.wrap(-Value.unwrap(x));
// Additional Time operators
//
function quotrem(Time a, Time b) internal pure returns (uint256 quot, uint256 rem) {
quot = Time.unwrap(a) / Time.unwrap(b);
rem = Time.unwrap(a) - quot * Time.unwrap(b);
}

// Additional Value operators
//
function quotrem(Value a, Value b) internal pure returns (int256 quot, int256 rem) {
quot = Value.unwrap(a) / Value.unwrap(b);
rem = Value.unwrap(a) - quot * Value.unwrap(b);
}
function mul(Value a, Unit b) internal pure returns (Value) {
return Value.wrap(Value.unwrap(a) * int256(Unit.unwrap(b)));
return Value.wrap(Value.unwrap(a) * Unit.unwrap(b));
}
function div(Value a, Unit b) internal pure returns (Value) {
return Value.wrap(Value.unwrap(a) / int256(Unit.unwrap(b)));
return Value.wrap(Value.unwrap(a) / Unit.unwrap(b));
}

function inv(FlowRate r) internal pure returns (FlowRate) {
return FlowRate.wrap(-FlowRate.unwrap(r));
// Additional FlowRate operators
//
function quotrem(FlowRate a, FlowRate b) internal pure returns (int256 quot, int256 rem) {
quot = FlowRate.unwrap(a) / FlowRate.unwrap(b);
rem = FlowRate.unwrap(a) - quot * FlowRate.unwrap(b);
}

function mul(FlowRate r, Time t) internal pure returns (Value) {
return Value.wrap(int256(FlowRate.unwrap(r)) * int256(uint256(Time.unwrap(t))));
return Value.wrap(FlowRate.unwrap(r) * int256(uint256(Time.unwrap(t))));
}
function mul(FlowRate r, Unit u) internal pure returns (FlowRate) {
return FlowRate.wrap(FlowRate.unwrap(r) * Unit.unwrap(u));
Expand All @@ -144,6 +181,13 @@ library AdditionalMonetaryTypeHelpers {
function mul_quotrem(FlowRate r, Unit u1, Unit u2) internal pure returns (FlowRate nr, FlowRate er) {
return r.mul(u1).quotrem(u2);
}

// Additional Unit operators
//
function quotrem(Unit a, Unit b) internal pure returns (int256 quot, int256 rem) {
quot = Unit.unwrap(a) / Unit.unwrap(b);
rem = Unit.unwrap(a) - quot * Unit.unwrap(b);
}
}
using AdditionalMonetaryTypeHelpers for Time global;
using AdditionalMonetaryTypeHelpers for Value global;
Expand Down Expand Up @@ -404,14 +448,14 @@ library SemanticMoney {
function shift2(BasicParticle memory a, BasicParticle memory b, Value x) internal pure
returns (BasicParticle memory m, BasicParticle memory n)
{
m = a.shift1(x.inv());
m = a.shift1(-x);
n = b.shift1(x);
}

function flow2(BasicParticle memory a, BasicParticle memory b, FlowRate r, Time t) internal pure
returns (BasicParticle memory m, BasicParticle memory n)
{
m = a.settle(t).flow1(r.inv());
m = a.settle(t).flow1(-r);
n = b.settle(t).flow1(r);
}

Expand All @@ -422,7 +466,7 @@ library SemanticMoney {
BasicParticle memory a1;
BasicParticle memory a2;
FlowRate r = b.flow_rate();
(a1, ) = mempty.flow2(b, r.inv(), t);
(a1, ) = mempty.flow2(b, -r, t);
(a2, n) = mempty.flow2(b, r + dr, t);
m = a.mappend(a1).mappend(a2);
}
Expand All @@ -435,24 +479,24 @@ library SemanticMoney {
BasicParticle memory mempty;
BasicParticle memory b1;
BasicParticle memory b2;
FlowRate r = b.flow_rate();
FlowRate r = a.flow_rate();
( , b1) = a.flow2(mempty, r, t);
(m, b2) = a.flow2(mempty, r.inv() + dr, t);
(m, b2) = a.flow2(mempty, -r + dr, t);
n = b.mappend(b1).mappend(b2);
}

function shift2b(BasicParticle memory a, PDPoolIndex memory b, Value x) internal pure
returns (BasicParticle memory m, PDPoolIndex memory n, Value x1)
{
(n, x1) = b.shift1(x);
m = a.shift1(x1.inv());
m = a.shift1(-x1);
}

function flow2(BasicParticle memory a, PDPoolIndex memory b, FlowRate r, Time t) internal pure
returns (BasicParticle memory m, PDPoolIndex memory n, FlowRate r1)
{
(n, r1) = b.settle(t).flow1(r);
m = a.settle(t).flow1(r1.inv());
m = a.settle(t).flow1(-r1);
}

function shift_flow2b(BasicParticle memory a, PDPoolIndex memory b, FlowRate dr, Time t) internal pure
Expand All @@ -462,7 +506,7 @@ library SemanticMoney {
BasicParticle memory a1;
BasicParticle memory a2;
FlowRate r = b.flow_rate();
(a1, , ) = mempty.flow2(b, r.inv(), t);
(a1, , ) = mempty.flow2(b, -r, t);
(a2, n, r1) = mempty.flow2(b, r + dr, t);
m = a.mappend(a1).mappend(a2);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/solidity-semantic-money/src/TokenEff.sol
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ library TokenEffLib {
FlowRate flowRateDelta = flowRate - eff.getFlowRate(flowHash);
BasicParticle memory a = eff.getUIndex(from);
BasicParticle memory b = eff.getUIndex(to);
(a, b) = a.shift_flow2a(b, flowRateDelta, t);
(a, b) = a.shift_flow2b(b, flowRateDelta, t);
return eff.setUIndex(from, a)
.setUIndex(to, b)
.setFlowInfo(eff, flowHash, from, to, flowRate);
Expand Down
2 changes: 1 addition & 1 deletion packages/solidity-semantic-money/src/TokenMonad.sol
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ abstract contract TokenMonad {
vars.newAdjustmentFlowRate = FlowRate.wrap(0);
} else {
// previous adjustment flow still needed
vars.newAdjustmentFlowRate = newActualFlowRate.inv();
vars.newAdjustmentFlowRate = -newActualFlowRate;
newActualFlowRate = FlowRate.wrap(0);
}

Expand Down
105 changes: 105 additions & 0 deletions packages/solidity-semantic-money/test/SemanticMoney.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,81 @@ contract SemanticMoneyTest is Test {
assertEq(q.mul(u) + e, r, "e1");
}

function test_operators() external {
assertTrue(Time.wrap(0) == Time.wrap(0));
assertTrue(Time.wrap(0) != Time.wrap(1));
assertTrue(Time.wrap(0) < Time.wrap(1));
assertFalse(Time.wrap(1) < Time.wrap(1));
assertTrue(Time.wrap(0) <= Time.wrap(1));
assertTrue(Time.wrap(1) <= Time.wrap(1));
assertTrue(Time.wrap(1) > Time.wrap(0));
assertFalse(Time.wrap(1) > Time.wrap(1));
assertTrue(Time.wrap(1) >= Time.wrap(0));
assertTrue(Time.wrap(1) >= Time.wrap(1));
{
(uint256 quot, uint256 rem) = Time.wrap(5).quotrem(Time.wrap(2));
assertEq(quot, 2);
assertEq(rem, 1);
}

assertTrue(FlowRate.wrap(0) == FlowRate.wrap(0));
assertTrue(FlowRate.wrap(0) != FlowRate.wrap(1));
assertTrue(FlowRate.wrap(0) < FlowRate.wrap(1));
assertFalse(FlowRate.wrap(1) < FlowRate.wrap(1));
assertTrue(FlowRate.wrap(0) <= FlowRate.wrap(1));
assertTrue(FlowRate.wrap(1) <= FlowRate.wrap(1));
assertTrue(FlowRate.wrap(1) > FlowRate.wrap(0));
assertFalse(FlowRate.wrap(1) > FlowRate.wrap(1));
assertTrue(FlowRate.wrap(1) >= FlowRate.wrap(0));
assertTrue(FlowRate.wrap(1) >= FlowRate.wrap(1));
{
(int256 quot, int256 rem) = FlowRate.wrap(5).quotrem(FlowRate.wrap(2));
assertEq(quot, 2);
assertEq(rem, 1);
(quot, rem) = FlowRate.wrap(-5).quotrem(FlowRate.wrap(2));
assertEq(quot, -2);
assertEq(rem, -1);
(quot, rem) = FlowRate.wrap(5).quotrem(FlowRate.wrap(-2));
assertEq(quot, -2);
assertEq(rem, 1);
(quot, rem) = FlowRate.wrap(-5).quotrem(FlowRate.wrap(-2));
assertEq(quot, 2);
assertEq(rem, -1);
}

assertTrue(Value.wrap(0) == Value.wrap(0));
assertTrue(Value.wrap(0) != Value.wrap(1));
assertTrue(Value.wrap(0) < Value.wrap(1));
assertFalse(Value.wrap(1) < Value.wrap(1));
assertTrue(Value.wrap(0) <= Value.wrap(1));
assertTrue(Value.wrap(1) <= Value.wrap(1));
assertTrue(Value.wrap(1) > Value.wrap(0));
assertFalse(Value.wrap(1) > Value.wrap(1));
assertTrue(Value.wrap(1) >= Value.wrap(0));
assertTrue(Value.wrap(1) >= Value.wrap(1));
{
(int256 quot, int256 rem) = Value.wrap(5).quotrem(Value.wrap(2));
assertEq(quot, 2);
assertEq(rem, 1);
}

assertTrue(Unit.wrap(0) == Unit.wrap(0));
assertTrue(Unit.wrap(0) != Unit.wrap(1));
assertTrue(Unit.wrap(0) < Unit.wrap(1));
assertFalse(Unit.wrap(1) < Unit.wrap(1));
assertTrue(Unit.wrap(0) <= Unit.wrap(1));
assertTrue(Unit.wrap(1) <= Unit.wrap(1));
assertTrue(Unit.wrap(1) > Unit.wrap(0));
assertFalse(Unit.wrap(1) > Unit.wrap(1));
assertTrue(Unit.wrap(1) >= Unit.wrap(0));
assertTrue(Unit.wrap(1) >= Unit.wrap(1));
{
(int256 quot, int256 rem) = Unit.wrap(5).quotrem(Unit.wrap(2));
assertEq(quot, 2);
assertEq(rem, 1);
}
}

////////////////////////////////////////////////////////////////////////////////
// Particle/Universal Index Properties: Monoidal Laws & Monetary Unit Laws
////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -176,9 +251,39 @@ contract SemanticMoneyTest is Test {
(d.a, d.b) = d.a.flow2(d.b, FlowRate.wrap(r1), d.t1);
(d.a, d.b) = d.a.flow2(d.b, FlowRate.wrap(r2), d.t2);

assertEq(Value.unwrap(-d.a.rtb(d.t3)), Value.unwrap(d.b.rtb(d.t3)));
assertEq(Value.unwrap(d.b.rtb(d.t3)),
int256(r1) * int256(uint256(m2)) + int256(r2) * int256(uint256(m3)));
}
function test_uu_shift_flow2b(uint16 m1, int64 r1, uint16 m2, int64 r2, uint16 m3) external {
UUTestVars memory d;
d.t1 = Time.wrap(m1);
d.t2 = d.t1 + Time.wrap(m2);
d.t3 = d.t2 + Time.wrap(m3);

(d.a, d.b) = d.a.shift_flow2b(d.b, FlowRate.wrap(r1), d.t1);
(d.a, d.b) = d.a.shift_flow2b(d.b, FlowRate.wrap(r2), d.t2);

assertEq(Value.unwrap(-d.a.rtb(d.t3)), Value.unwrap(d.b.rtb(d.t3)));
assertEq(Value.unwrap(d.b.rtb(d.t3)),
int256(r1) * int256(uint256(m2)) + (int256(r1) + int256(r2)) * int256(uint256(m3)));
}
function test_uu_shift_flow2ab_equiv(int64 r1, uint16 m1, int64 r2, uint16 m2) external {
Time t1 = Time.wrap(m1);
Time t2 = Time.wrap(m1) + Time.wrap(m2);
BasicParticle memory a0;
BasicParticle memory b0;

(BasicParticle memory a1a, BasicParticle memory b1a) = a0.shift_flow2a(b0, FlowRate.wrap(r1), t1);
(BasicParticle memory a1b, BasicParticle memory b1b) = a0.shift_flow2b(b0, FlowRate.wrap(r1), t1);
assertEq(a1a, a1b, "a1");
assertEq(b1a, b1b, "b1");

(BasicParticle memory a2a, BasicParticle memory b2a) = a1a.shift_flow2a(b1a, FlowRate.wrap(r2), t2);
(BasicParticle memory a2b, BasicParticle memory b2b) = a1b.shift_flow2b(b1b, FlowRate.wrap(r2), t2);
assertEq(a2a, a2b, "a2");
assertEq(b2a, b2b, "b2");
}

// Universal Index to Proportional Distribution Pool
function updp_shift2(BasicParticle memory a, PDPoolIndex memory b, Time /* t */, int64 v)
Expand Down
Loading

0 comments on commit cdaff8d

Please sign in to comment.