Smart Contract

FCLCrypto

A.5d4604a414ba4155.FCLCrypto

Valid From

85,516,738

Deployed

3d ago
Feb 25, 2026, 02:55:46 PM UTC

Dependents

0 imports
1/*
2  FCLCrypto
3
4  The FCLCrypto contract provides functions which allow to verify signatures and check for signing power.
5*/
6
7access(all) contract FCLCrypto {
8
9    /// verifyUserSignatures  allows to verify the user signatures for the given account.
10    /// 
11    /// @param address: The address of the account
12    /// @param message: The signed data
13    /// @param keyIndices: This integer array maps the signatures to the account keys by index
14    /// @param signatures: The signatures belonging to the account keys
15    ///
16    /// @return Whether all signatures are valid and the combined total key weight reaches signing power
17    ///
18    access(all) view fun verifyUserSignatures(
19        address: Address,
20        message: String,
21        keyIndices: [Int],
22        signatures: [String]
23    ): Bool {
24        return self.verifySignatures(
25            address: address,
26            message: message,
27            keyIndices: keyIndices,
28            signatures: signatures,
29            domainSeparationTag: self.domainSeparationTagFlowUser,
30        )
31    }
32
33    /// verifyAccountProofSignatures allows to verify the account proof signatures for the given account.
34    /// 
35    /// @param address: The address of the account
36    /// @param message: The signed data
37    /// @param keyIndices: This integer array maps the signatures to the account keys by index
38    /// @param signatures: The signatures belonging to the account keys
39    ///
40    /// @return Whether all signatures are valid and the combined total key weight reaches signing power
41    ///
42    access(all) view fun verifyAccountProofSignatures(
43        address: Address,
44        message: String,
45        keyIndices: [Int],
46        signatures: [String]
47    ): Bool {
48        return self.verifySignatures(
49            address: address,
50            message: message,
51            keyIndices: keyIndices,
52            signatures: signatures,
53            domainSeparationTag: self.domainSeparationTagAccountProof,
54        ) || 
55        self.verifySignatures(
56            address: address,
57            message: message,
58            keyIndices: keyIndices,
59            signatures: signatures,
60            domainSeparationTag: self.domainSeparationTagFlowUser,
61        )
62    }
63
64    /// verifySignatures is a private function which provides the functionality to verify 
65    /// signatures for the public functions.
66    /// 
67    /// @param address: The address of the account
68    /// @param message: The signed data
69    /// @param keyIndices: This integer array maps the signatures to the account keys by index
70    /// @param signatures: The signatures belonging to the account keys
71    /// @param domainSeparationTag: The domain tag originally used for the signatures
72    ///
73    /// @return Whether all signatures are valid and the combined total key weight reaches signing power
74    ///
75    access(self) view fun verifySignatures(
76        address: Address,
77        message: String,
78        keyIndices: [Int],
79        signatures: [String],
80        domainSeparationTag: String,
81    ): Bool {
82        pre {
83            keyIndices.length == signatures.length : "Key index list length does not match signature list length"
84        }
85
86        let account = getAccount(address)
87        let messageBytes = message.decodeHex()
88
89        var totalWeight: UFix64 = 0.0
90        let seenKeyIndices: {Int: Bool} = {}
91
92        var i = 0
93
94        for keyIndex in keyIndices {
95
96            let accountKey = account.keys.get(keyIndex: keyIndex) ?? panic("Key provided does not exist on account")
97            let signature = signatures[i].decodeHex()
98
99            // Ensure this key index has not already been seen
100
101            if seenKeyIndices[accountKey.keyIndex] ?? false {
102                return false
103            }
104
105            // Record the key index was seen
106
107            seenKeyIndices[accountKey.keyIndex] = true
108
109            // Ensure the key is not revoked
110
111            if accountKey.isRevoked {
112                return false
113            }
114
115            // Ensure the signature is valid
116
117            if !accountKey.publicKey.verify(
118                signature: signature,
119                signedData: messageBytes,
120                domainSeparationTag: domainSeparationTag,
121                hashAlgorithm: accountKey.hashAlgorithm
122            ) {
123                return false
124            }
125
126            totalWeight = totalWeight + accountKey.weight
127
128            i = i + 1
129        }
130        
131        return totalWeight >= 999.0
132    }
133
134    access(self) let domainSeparationTagFlowUser: String
135    access(self) let domainSeparationTagFCLUser: String
136    access(self) let domainSeparationTagAccountProof: String
137
138    init() {
139        self.domainSeparationTagFlowUser = "FLOW-V0.0-user"
140        self.domainSeparationTagFCLUser = "FCL-USER-V0.0"
141        self.domainSeparationTagAccountProof = "FCL-ACCOUNT-PROOF-V0.0"
142    }
143}