EthWarsaw 2023, 2nd Version
function _doFlow(bytes memory eff,
address from, address to, bytes32 flowHash, FlowRate flowRate,
Time t)
internal returns (bytes memory)
{
if (from == to) return eff; // short circuit
FlowRate flowRateDelta = flowRate - _getFlowRate(eff, flowHash);
BasicParticle memory a = _getUIndex(eff, from);
BasicParticle memory b = _getUIndex(eff, to);
(a, b) = a.shift_flow2b(b, flowRateDelta, t);
eff = _setUIndex(eff, from, a);
eff = _setUIndex(eff, to, b);
eff = _setFlowInfo(eff, flowHash, from, to, flowRate, flowRateDelta);
return eff;
}
Solidity is no where near being able to express all functional programming ideas, but we can emulate some ideas.
Here are some techniques:
type FlowRate is int128;
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_add_r as +, mt_r_sub_r as - } for FlowRate global;
library AdditionalMonetaryTypeHelpers {
//...
function inv(FlowRate r) internal pure returns (FlowRate) {
return FlowRate.wrap(-FlowRate.unwrap(r));
}
function mul(FlowRate r, Time t) internal pure returns (Value) {
return Value.wrap(FlowRate.unwrap(r) * int(uint(Time.unwrap(t))));
}
//...
}
using AdditionalMonetaryTypeHelpers for Time global;
using AdditionalMonetaryTypeHelpers for Value global;
using AdditionalMonetaryTypeHelpers for Unit global;
using AdditionalMonetaryTypeHelpers for FlowRate global;
/// Pure data clone function.
function clone(BasicParticle memory a) internal pure
returns (BasicParticle memory b)
{
b._settled_at = a._settled_at;
b._settled_value = a._settled_value;
b._flow_rate = a._flow_rate;
}
Note the boilerplate needed. There is no general way to do "memcpy" in Solidity (lack of sizeof and a EVM opcode?).
/// Monetary unit rtb function for basic particle/universal index.
function rtb(BasicParticle memory a, Time t) internal pure
returns (Value v)
{
return a._flow_rate.mul(t - a._settled_at) + a._settled_value;
}
/// Monetary unit settle function for basic particle/universal index.
function settle(BasicParticle memory a, Time t) internal pure
returns (BasicParticle memory b)
{
b = a.clone();
b._settled_value = rtb(a, t);
b._settled_at = t;
}
function run_uu_test(uint16 m1, int64 x1, uint16 m2, int64 x2, uint16 m3,
function (BasicParticle memory, BasicParticle memory, Time, int64)
internal pure
returns (BasicParticle memory, BasicParticle memory) op1,
function (BasicParticle memory, BasicParticle memory, Time, int64)
internal pure
returns (BasicParticle memory, BasicParticle memory) op2
) internal {
UU_Test_Data 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) = op1(d.a, d.b, d.t1, x1);
(d.a, d.b) = op2(d.a, d.b, d.t2, x2);
assertEq(0, Value.unwrap(d.a.rtb(d.t3) + d.b.rtb(d.t3)));
}
function test_uu_ss(uint16 m1, int64 x1, uint16 m2, int64 x2, uint16 m3) external {
run_uu_test(m1, x1, m2, x2, m3, uu_shift2, uu_shift2);
}
function test_uu_ff(uint16 m1, int64 r1, uint16 m2, int64 r2, uint16 m3) external {
run_uu_test(m1, r1, m2, r2, m3, uu_flow2, uu_flow2);
}
function test_uu_fs(uint16 m1, int64 r1, uint16 m2, int64 x2, uint16 m3) external {
run_uu_test(m1, r1, m2, x2, m3, uu_flow2, uu_shift2);
}
function test_uu_sf(uint16 m1, int64 x1, uint16 m2, int64 r2, uint16 m3) external {
run_uu_test(m1, x1, m2, r2, m3, uu_shift2, uu_flow2);
}
/**
* @title Basic particle: the building block for payment primitives.
*/
struct BasicParticle {
Time _settled_at;
FlowRate _flow_rate;
Value _settled_value;
}
/// Pure data clone function.
function clone(BasicParticle memory a ) internal pure
returns (BasicParticle memory b)
{
// TODO memcpy
b._settled_at = a._settled_at;
b._flow_rate = a._flow_rate;
b._settled_value = a._settled_value;
}
function shift1(BasicParticle memory a, Value x) internal pure
returns (BasicParticle memory b)
{
b = a.clone();
b._settled_value = b._settled_value + x;
}
function flow1(BasicParticle memory a, FlowRate r) internal pure
returns (BasicParticle memory b)
{
b = a.clone();
b._flow_rate = r;
}
\[ a.settle(t1).settled\_at() = t1 \] \[ a.settle(t1).settle(t1) = a.settle(t1) \]
function test_u_settle_idempotence(BasicParticle memory p, uint16 m) external {
limitToSafeParticle(p);
Time t1 = p.settled_at() + Time.wrap(m);
BasicParticle memory p1 = p.settle(t1);
BasicParticle memory p2 = p1.settle(t1);
assertEq(p1.settled_at(), t1, "e1");
assertEq(p1, p2, "e2");
}
\[ (a \times b) \times c = a \times (b \times c) \]
function test_u_monoid_assoc(BasicParticle memory p1, BasicParticle memory p2, BasicParticle memory p3) external {
limitToSafeParticle(p1);
limitToSafeParticle(p2);
limitToSafeParticle(p3);
assertEq(p1.mappend(p2).mappend(p3), p1.mappend(p2.mappend(p3)), "e2");
}
No one has managed to explain what is a monad.
A way to emulate the concept of Monad in Solidity.
Use Virtual functions deferring the implementations of side effects:
abstract contract TokenMonad {
function _getUIndex(bytes memory eff, address owner)
virtual internal view returns (BasicParticle memory);
function _setUIndex(bytes memory eff, address owner, BasicParticle memory p)
virtual internal returns (bytes memory);
}
function _doFlow(bytes memory eff,
address from, address to, bytes32 flowHash, FlowRate flowRate,
Time t)
internal returns (bytes memory)
{
if (from == to) return eff; // short circuit
// retrieving data
FlowRate flowRateDelta = flowRate - _getFlowRate(eff, flowHash);
BasicParticle memory a = _getUIndex(eff, from);
BasicParticle memory b = _getUIndex(eff, to);
// applying data transformations
(a, b) = a.shift_flow2b(b, flowRateDelta, t);
// applying updating side effects
eff = _setUIndex(eff, from, a);
eff = _setUIndex(eff, to, b);
eff = _setFlowInfo(eff, flowHash, from, to, flowRate, flowRateDelta);
return eff;
}
Not directly, but to use another higher level functional programming language to transpire to it?
Let's Make Solidity Code Better Using Functional Programming Now!!
Transcript available from github-wiki:superfluid-finance/protocol-monorepo.