;; Multisig wallet smart contract ;; Cleanup expired partial orders (& get contract data) ;; needsWrite, seqno, keys, messages (int, int, cell, cell) collect_garbage() { var data = get_data().begin_parse(); var (seqno, keys, messages) = (data~load_int(32), data~load_dict(), data~load_dict()); data.end_parse(); var needsWrite = 0; var hash = -1; do { (hash, var cs, var ok) = messages.idict_get_next?(256, hash); if (ok) { ;; expiry <= now if (cs~load_uint(32) <= now()) { messages.idict_delete?(256, hash); needsWrite = -1; } } } until (~ ok); return (needsWrite, seqno, keys, messages); } () store_db(int seqno, cell keys, cell messages) { set_data(begin_cell().store_uint(seqno, 32).store_dict(keys).store_dict(messages).end_cell()); } () recv_internal(slice in_msg) impure { ;; do nothing for internal messages } ;; multiSigWrapper$0 keys_signatures:(HashmapE 4 ^Signature) message:(WrappedMessage X) = MultiSigWrapper X; () recv_external(slice in_msg) impure { ;; Check if multiSigWrapper$0 or future unsupported protocol throw_if(32, in_msg~load_uint(1) != 0); ;; Check if is hme_empty$0 or hme_root$1 ;; If empty signature list throw_if(33, in_msg.preload_uint(1) != 1); var signatures = in_msg~load_dict(); var cs = in_msg; ;; wrappedMessage$_ expires_at:uint32 seqno:uint32 body:(Either (Message X) ^(Message X)) = WrappedMessage X; var (expires_at, msg_seqno) = (cs~load_uint(32), cs~load_uint(32)); ;; Message expired throw_if(34, expires_at <= now()); var hash = slice_hash(in_msg); var (needsWrite, stored_seqno, keys, messages) = collect_garbage(); var storedSignatures = new_dict(); ;; If new message, increase seqno if (stored_seqno == msg_seqno) { stored_seqno += 1; needsWrite = -1; ;; If old message } else { (var storedMessage, var ok) = messages.idict_get?(256, hash); ;; If old message and doesn't exist in db ifnot (ok) { ;; Store new garbage-collected db if (needsWrite) { store_db(stored_seqno, keys, messages); } ;; Throw throw_if(35, -1); } ;; Skip expiry storedMessage.skip_bits(32); ;; No seqno: we can't regenerate the hash of a stored message. ;; That's a tradeoff to save grams on storage (and storing the seqno would be pointless anyway, ;; since all integrity checks were already made when the message was first stored) ;; ;; Load signatures var storedSignatures = storedMessage.load_dict(); } accept_message(); cs~touch(); while (cs.slice_refs()) { var mode = cs~load_uint(8); send_raw_message(cs~load_ref(), mode); } cs.end_parse(); ;;set_data(begin_cell().store_uint(stored_seqno + 1, 32).store_uint(public_key, 256).end_cell()); } ;; Get methods int seqno() method_id { return get_data().begin_parse().preload_uint(32); }