22pragma solidity >= 0.8.0 ;
33
44import { Enum } from "./dep-ports/Enum.sol " ;
5- import { CPKFactory, CPKFactoryTx } from "./CPKFactory.sol " ;
5+ import { CPKFactory, CPKFactoryTx, TxReaction } from "./CPKFactory.sol " ;
6+ import { SafeSignatureUtils } from "./SafeSignatureUtils.sol " ;
67
78contract CPKFactoryFacade {
9+ using SafeSignatureUtils for bytes ;
10+
811 CPKFactory immutable cpkFactory;
912 address immutable safeVersion;
1013 uint256 immutable salt;
@@ -38,22 +41,35 @@ contract CPKFactoryFacade {
3841 payable
3942 returns (bool )
4043 {
44+ // The signatures here aren't just the Gnosis Safe signatures,
45+ // but more data has been appended to them. In particular, the
46+ // format for the signatures is now like the following:
47+ // [inner sig][owner][outer sig]
48+ // where the inner signature is what is submitted to the
49+ // proxy as a first transaction to be executed after its creation,
50+ // the owner is the address of the owner of the new proxy,
51+ // left-padded to 32 bytes, and the outer signature is the overall
52+ // signature required by the CPKFactory.
53+ // The following process copies this signatures data into memory,
54+ // and then figures out the length of the inner signature,
55+ // and then extracts the owner from the signatures in memory,
56+ // and then overwrites the owner in memory with the length
57+ // of the outer signature, which is just the remaining
58+ // bytes of the signature. We end up with a situation in memory like
59+ // [inner sig]*[outer sig length][outer sig]
60+ // where the * is the pointer assigned to outerSig.
61+ bytes memory outerSig;
4162 address owner;
42- // the following assembly block extracts the owner from the signature data
43- // solium-disable-next-line security/no-inline-assembly
44- assembly {
45- // 0x124 is the start of the calldata word for the signature parameter
46- // since it's a dynamic type, it stores the offset to the part of the calldata
47- // that stores the actual data being sent as a signature so we load the
48- // offset to the data, and then load the first word of the data which is
49- // the length of the signature. Adding this offset to the signature data position
50- // lets us grab the trailing word of the signature, which we will interpret
51- // as the owner.
52- let sigPos := calldataload (0x124 )
53- owner := and (
54- calldataload (add (sigPos, calldataload (sigPos))),
55- 0xffffffffffffffffffffffffffffffffffffffff
56- )
63+ uint innerSigLen;
64+ {
65+ bytes memory innerSig = signatures;
66+ innerSigLen = innerSig.actualLength ();
67+ uint outerSigLen = signatures.length - innerSigLen - 0x20 ;
68+ assembly {
69+ outerSig := add (add (innerSig, 0x20 ), innerSigLen)
70+ owner := mload (outerSig)
71+ mstore (outerSig, outerSigLen)
72+ }
5773 }
5874
5975 CPKFactoryTx[] memory txs = new CPKFactoryTx [](2 );
@@ -75,23 +91,58 @@ contract CPKFactoryFacade {
7591 ") " ,
7692 owners, uint256 (1 ), address (0 ), "" ,
7793 fallbackHandler, address (0 ), uint256 (0 ), payable (0 )
78- )
94+ ),
95+ reaction: TxReaction.IgnoreReturn
7996 });
8097 }
8198
82- // msg.data works here as a substitute for encoding because this function's signature
83- // exactly matches the execTransaction signature from the Gnosis Safe, so the calldata
84- // encoding will be the same.
85- txs[1 ] = CPKFactoryTx ({
86- value: msg .value ,
87- data: msg .data
88- });
99+ {
100+ // trying to reencode the msg.data with the params except instead
101+ // of signatures using innerSig would be the obvious and readable approach
102+ // except we run into stack too deep errors that can't be circumvented yet,
103+ // so instead we copy the entire msg.data into memory and
104+ // mutate the signatures portion in it.
105+ bytes memory innerTxData = msg .data ;
106+ assembly {
107+ // index param 9 is signatures, and 9 * 0x20 + 4 = 0x124
108+ // 0x124 from the start of the data portion of innerTxData
109+ // contains the offset to the start of the word
110+ // containing a further offset of where the signatures data
111+ // begins.
112+ // We add 0x20 to account for the word containing the length
113+ // of innerTxData, making a total offset of 0x144 to the offset data
114+ // Then we add that offset, a word for the overall length, and
115+ // the pointer to innerTxData, to arrive at a pointer to the signatures
116+ // inside innerTxData
117+ let sigs := add (innerTxData, add (mload (add (innerTxData, 0x144 )), 0x20 ))
118+ // Since we know the length of the inner signature, we get the
119+ // size reduction we need by subtracting the inner signature length
120+ // from the size of all the signature data
121+ let sizeReduction := sub (mload (sigs), innerSigLen)
122+ mstore (sigs, innerSigLen)
123+ // The size reduction is then used to cut the outer signature out
124+ // of the msg.data.
125+ // This assumes that the signatures data is the last chunk of data
126+ // in the msg.data.
127+ // We can't keep the outer signature in the inner transaction data
128+ // because the outer signature signs the inner transaction data,
129+ // so keeping that there would make the outer signature impossible
130+ // to generate.
131+ mstore (innerTxData, sub (mload (innerTxData), sizeReduction))
132+ }
133+ txs[1 ] = CPKFactoryTx ({
134+ value: msg .value ,
135+ data: innerTxData,
136+ reaction: TxReaction.CaptureBoolReturn
137+ });
138+ }
89139
90140 return cpkFactory.createProxyAndExecTransaction {value: msg .value }(
91141 owner,
92142 safeVersion,
93143 salt,
94- txs
144+ txs,
145+ outerSig
95146 );
96147 }
97148}
0 commit comments