Smart Contract

ColdStorage

A.123cb47fe122f6e3.ColdStorage

Deployed

1d ago
Feb 26, 2026, 09:44:38 PM UTC

Dependents

0 imports
1import Crypto
2
3import FungibleToken from 0xf233dcee88fe0abe
4import MoxyToken from 0x123cb47fe122f6e3
5
6pub contract ColdStorage {
7
8  pub struct Key {
9    pub let publicKey: String
10    pub let weight: UFix64
11
12    init(
13      publicKey: String, 
14      // signatureAlgorithm: SignatureAlgorithm, 
15      // hashAlgorithm: HashAlgorithm, 
16      weight: UFix64,
17    ) {
18      self.publicKey = publicKey
19      self.weight = weight
20    }
21  }
22
23  pub struct interface ColdStorageRequest {
24    pub var sigSet: [Crypto.KeyListSignature]
25    pub var seqNo: UInt64
26    pub var senderAddress: Address
27
28    pub fun signableBytes(): [UInt8]
29  }
30
31  pub struct WithdrawRequest: ColdStorageRequest {
32    pub var sigSet: [Crypto.KeyListSignature]
33    pub var seqNo: UInt64
34
35    pub var senderAddress: Address
36    pub var recipientAddress: Address
37    pub var amount: UFix64
38
39    init(
40      senderAddress: Address,
41      recipientAddress: Address,  
42      amount: UFix64, 
43      seqNo: UInt64, 
44      sigSet: [Crypto.KeyListSignature],
45    ) {
46      self.senderAddress = senderAddress
47      self.recipientAddress = recipientAddress
48      self.amount = amount
49
50      self.seqNo = seqNo
51      self.sigSet = sigSet
52    }
53
54    pub fun signableBytes(): [UInt8] {
55      let senderAddress = self.senderAddress.toBytes()
56      let recipientAddressBytes = self.recipientAddress.toBytes()
57      let amountBytes = self.amount.toBigEndianBytes()
58      let seqNoBytes = self.seqNo.toBigEndianBytes()
59
60      return senderAddress.concat(recipientAddressBytes).concat(amountBytes).concat(seqNoBytes)
61    }
62  }
63
64  pub struct KeyListChangeRequest: ColdStorageRequest {
65    pub var sigSet: [Crypto.KeyListSignature]
66    pub var seqNo: UInt64
67    pub var senderAddress: Address
68
69    pub var newKeys: [Key]
70
71    init(
72      newKeys: [Key],
73      seqNo: UInt64,
74      senderAddress: Address,
75      sigSet: [Crypto.KeyListSignature],
76    ) {
77      self.newKeys = newKeys
78      self.seqNo = seqNo
79      self.senderAddress = senderAddress
80      self.sigSet = sigSet
81    }
82
83    pub fun signableBytes(): [UInt8] {
84      let senderAddress = self.senderAddress.toBytes()
85      let seqNoBytes = self.seqNo.toBigEndianBytes()
86
87      return senderAddress.concat(seqNoBytes)
88    }
89  }
90
91  pub resource PendingWithdrawal {
92
93    access(self) var pendingVault: @FungibleToken.Vault
94    access(self) var request: WithdrawRequest
95
96    init(pendingVault: @FungibleToken.Vault, request: WithdrawRequest) {
97      self.pendingVault <- pendingVault
98      self.request = request
99    }
100
101    pub fun execute(fungibleTokenReceiverPath: PublicPath) {
102      var pendingVault: @FungibleToken.Vault <- MoxyToken.createEmptyVault()
103      self.pendingVault <-> pendingVault
104
105      let recipient = getAccount(self.request.recipientAddress)
106      let receiver = recipient
107        .getCapability(fungibleTokenReceiverPath)
108        .borrow<&{FungibleToken.Receiver}>()
109        ?? panic("Unable to borrow receiver reference for recipient")
110
111      receiver.deposit(from: <- pendingVault)
112    }
113
114    destroy (){
115      pre {
116        self.pendingVault.balance == 0.0 as UFix64
117      }
118      destroy self.pendingVault
119    }
120  }
121
122  pub resource interface PublicVault {
123    pub fun getSequenceNumber(): UInt64
124
125    pub fun getBalance(): UFix64
126
127    pub fun getKeys(): [Key]
128
129    pub fun prepareWithdrawal(request: WithdrawRequest): @PendingWithdrawal
130
131    pub fun updateSignatures(request: KeyListChangeRequest)
132  }
133
134  pub resource Vault : FungibleToken.Receiver, PublicVault {    
135    access(self) var address: Address
136    access(self) var keys: [Key]
137    access(self) var contents: @FungibleToken.Vault
138    access(self) var seqNo: UInt64
139
140    pub fun deposit(from: @FungibleToken.Vault) {
141      self.contents.deposit(from: <-from)
142    }
143
144    pub fun getSequenceNumber(): UInt64 {
145        return self.seqNo
146    }
147
148    pub fun getBalance(): UFix64 {
149      return self.contents.balance
150    }
151
152    pub fun getKeys(): [Key] {
153      return self.keys
154    }
155
156    pub fun prepareWithdrawal(request: WithdrawRequest): @PendingWithdrawal {
157      pre {
158        self.isValidSignature(request: request)
159      } 
160      post {
161        self.seqNo == request.seqNo + UInt64(1)
162      }
163
164      self.incrementSequenceNumber()
165
166      return <- create PendingWithdrawal(pendingVault: <- self.contents.withdraw(amount: request.amount), request: request)
167    }
168
169    pub fun updateSignatures(request: KeyListChangeRequest) {
170      pre {
171        self.seqNo == request.seqNo 
172        self.address == request.senderAddress
173        self.isValidSignature(request: request)
174      }
175      post {
176        self.seqNo == request.seqNo + UInt64(1)
177      }
178
179      self.incrementSequenceNumber()
180
181      self.keys = request.newKeys
182    } 
183
184    access(self) fun incrementSequenceNumber(){
185      self.seqNo = self.seqNo + UInt64(1)
186    }
187
188    access(self) fun isValidSignature(request: {ColdStorage.ColdStorageRequest}): Bool {
189      pre {
190        self.seqNo == request.seqNo : "Squence number does not match"
191        self.address == request.senderAddress : "Address does not match"
192      }
193
194      let a = ColdStorage.validateSignature(
195        keys: self.keys,
196        signatureSet: request.sigSet,
197        message: request.signableBytes()
198      )
199
200      return a
201    }
202
203    init(address: Address, keys: [Key], contents: @FungibleToken.Vault) {
204      self.keys = keys
205      self.seqNo = UInt64(0)
206      self.contents <- contents
207      self.address = address
208    }
209
210    destroy() {
211      destroy self.contents
212    }
213  }
214
215  pub fun createVault(
216    address: Address, 
217    keys: [Key], 
218    contents: @FungibleToken.Vault,
219  ): @Vault {
220    return <- create Vault(address: address, keys: keys, contents: <- contents)
221  }
222
223  pub fun validateSignature(
224    keys: [Key],
225    signatureSet: [Crypto.KeyListSignature],
226    message: [UInt8],
227  ): Bool {
228
229    let keyList = Crypto.KeyList()
230
231    for key in keys {
232      keyList.add(
233        PublicKey(
234          publicKey: key.publicKey.decodeHex(),
235          signatureAlgorithm: SignatureAlgorithm.ECDSA_P256,
236        ),
237        hashAlgorithm: HashAlgorithm.SHA3_256,
238        weight: key.weight,
239      )
240    }
241
242    return keyList.verify(
243      signatureSet: signatureSet,
244      signedData: message
245    )
246  }
247}
248 
249