// This is just an approximated TL scheme, explaining the message formats used by the multisig wallet. // Multisig wallet constructors // 512 bits signature$_ R:bits256 s:bits256 = Signature; // Actual multisigned message wrappedMessage$_ expires_at:uint32 seqno:uint32 body:(Either (Message X) ^(Message X)) = WrappedMessage X; // key ID => signature multiSigWrapper$0 keys_signatures:(HashmapE 4 ^Signature) message:(WrappedMessage X) = MultiSigWrapper X; // multisignature_future$1 = MultiSignatureWrapper; // Constructors for wallet storage simple_multisigned_message$_ signatures:[ 4 Signature) expires_at:uint32 body:(Either (Message X) ^(Message X)) = SimpleMultiSigMessage X; // Not doing explicit versioning here, since the structure of the storage can only change after an update of the onchain wallet code // Max 256 buffered messages, to avoid excessive memory usage, could be tweaked. // Max 10 keys (not sure about this, the contest instruction file says 10 in one place and 100 in another, so I chose 10) // Min 3 signatures per message storage$_ seqno:uint32 {k:(## 4)} n:(## 4) { k > 0 } { k >= 3 } { n >= k } { n <= 10 } keys:(HashMap 256 (## 4)) messages:(HashMapE 8 ^(SimpleMultiSigMessage X)) = Storage; // TON stuff unit$_ = Unit; true$_ = True; // EMPTY False; bool_false$0 = Bool; bool_true$1 = Bool; bool_false$0 = BoolFalse; bool_true$1 = BoolTrue; nothing$0 {X:Type} = Maybe X; just$1 {X:Type} value:X = Maybe X; left$0 {X:Type} {Y:Type} value:X = Either X Y; right$1 {X:Type} {Y:Type} value:Y = Either X Y; pair$_ {X:Type} {Y:Type} first:X second:Y = Both X Y; bit$_ (## 1) = Bit; /* * * FROM hashmap.tlb * */ // ordinary Hashmap / HashmapE, with fixed length keys // hm_edge#_ {n:#} {X:Type} {l:#} {m:#} label:(HmLabel ~l n) {n = (~m) + l} node:(HashmapNode m X) = Hashmap n X; hmn_leaf#_ {X:Type} value:X = HashmapNode 0 X; hmn_fork#_ {n:#} {X:Type} left:^(Hashmap n X) right:^(Hashmap n X) = HashmapNode (n + 1) X; hml_short$0 {m:#} {n:#} len:(Unary ~n) {n <= m} s:(n * Bit) = HmLabel ~n m; hml_long$10 {m:#} n:(#<= m) s:(n * Bit) = HmLabel ~n m; hml_same$11 {m:#} v:Bit n:(#<= m) = HmLabel ~n m; unary_zero$0 = Unary ~0; unary_succ$1 {n:#} x:(Unary ~n) = Unary ~(n + 1); hme_empty$0 {n:#} {X:Type} = HashmapE n X; hme_root$1 {n:#} {X:Type} root:^(Hashmap n X) = HashmapE n X; extra_currencies$_ dict:(HashmapE 32 (VarUInteger 32)) = ExtraCurrencyCollection; // true#_ = True; _ {n:#} _:(Hashmap n True) = BitstringSet n; // HashmapAug, hashmap with an extra value // (augmentation) of type Y at every node // ahm_edge#_ {n:#} {X:Type} {Y:Type} {l:#} {m:#} label:(HmLabel ~l n) {n = (~m) + l} node:(HashmapAugNode m X Y) = HashmapAug n X Y; ahmn_leaf#_ {X:Type} {Y:Type} extra:Y value:X = HashmapAugNode 0 X Y; ahmn_fork#_ {n:#} {X:Type} {Y:Type} left:^(HashmapAug n X Y) right:^(HashmapAug n X Y) extra:Y = HashmapAugNode (n + 1) X Y; ahme_empty$0 {n:#} {X:Type} {Y:Type} extra:Y = HashmapAugE n X Y; ahme_root$1 {n:#} {X:Type} {Y:Type} root:^(HashmapAug n X Y) extra:Y = HashmapAugE n X Y; // VarHashmap / VarHashmapE, with variable-length keys // vhm_edge#_ {n:#} {X:Type} {l:#} {m:#} label:(HmLabel ~l n) {n = (~m) + l} node:(VarHashmapNode m X) = VarHashmap n X; vhmn_leaf$00 {n:#} {X:Type} value:X = VarHashmapNode n X; vhmn_fork$01 {n:#} {X:Type} left:^(VarHashmap n X) right:^(VarHashmap n X) value:(Maybe X) = VarHashmapNode (n + 1) X; vhmn_cont$1 {n:#} {X:Type} branch:Bit child:^(VarHashmap n X) value:X = VarHashmapNode (n + 1) X; // nothing$0 {X:Type} = Maybe X; // just$1 {X:Type} value:X = Maybe X; vhme_empty$0 {n:#} {X:Type} = VarHashmapE n X; vhme_root$1 {n:#} {X:Type} root:^(VarHashmap n X) = VarHashmapE n X; // // PfxHashmap / PfxHashmapE, with variable-length keys // constituting a prefix code // phm_edge#_ {n:#} {X:Type} {l:#} {m:#} label:(HmLabel ~l n) {n = (~m) + l} node:(PfxHashmapNode m X) = PfxHashmap n X; phmn_leaf$0 {n:#} {X:Type} value:X = PfxHashmapNode n X; phmn_fork$1 {n:#} {X:Type} left:^(PfxHashmap n X) right:^(PfxHashmap n X) = PfxHashmapNode (n + 1) X; phme_empty$0 {n:#} {X:Type} = PfxHashmapE n X; phme_root$1 {n:#} {X:Type} root:^(PfxHashmap n X) = PfxHashmapE n X; /* * * END hashmap.tlb * */ // TON messages addr_none$00 = MsgAddressExt; addr_extern$01 len:(## 9) external_address:(bits len) = MsgAddressExt; anycast_info$_ depth:(#<= 30) { depth >= 1 } rewrite_pfx:(bits depth) = Anycast; addr_std$10 anycast:(Maybe Anycast) workchain_id:int8 address:bits256 = MsgAddressInt; addr_var$11 anycast:(Maybe Anycast) addr_len:(## 9) workchain_id:int32 address:(bits addr_len) = MsgAddressInt; _ _:MsgAddressInt = MsgAddress; _ _:MsgAddressExt = MsgAddress; // var_uint$_ {n:#} len:(#< n) value:(uint (len * 8)) = VarUInteger n; var_int$_ {n:#} len:(#< n) value:(int (len * 8)) = VarInteger n; nanograms$_ amount:(VarUInteger 16) = Grams; // extra_currencies$_ dict:(HashmapE 32 (VarUInteger 32)) = ExtraCurrencyCollection; currencies$_ grams:Grams other:ExtraCurrencyCollection = CurrencyCollection; // int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddressInt dest:MsgAddressInt value:CurrencyCollection ihr_fee:Grams fwd_fee:Grams created_lt:uint64 created_at:uint32 = CommonMsgInfo; ext_in_msg_info$10 src:MsgAddressExt dest:MsgAddressInt import_fee:Grams = CommonMsgInfo; ext_out_msg_info$11 src:MsgAddressInt dest:MsgAddressExt created_lt:uint64 created_at:uint32 = CommonMsgInfo; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress dest:MsgAddressInt value:CurrencyCollection ihr_fee:Grams fwd_fee:Grams created_lt:uint64 created_at:uint32 = CommonMsgInfoRelaxed; ext_out_msg_info$11 src:MsgAddress dest:MsgAddressExt created_lt:uint64 created_at:uint32 = CommonMsgInfoRelaxed; tick_tock$_ tick:Bool tock:Bool = TickTock; _ split_depth:(Maybe (## 5)) special:(Maybe TickTock) code:(Maybe ^Cell) data:(Maybe ^Cell) library:(HashmapE 256 SimpleLib) = StateInit; simple_lib$_ public:Bool root:^Cell = SimpleLib; // create a message message$_ {X:Type} info:CommonMsgInfo init:(Maybe (Either StateInit ^StateInit)) body:(Either X ^X) = Message X;