Smart Contract
ColdStorage
A.123cb47fe122f6e3.ColdStorage
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