1+ import " FlowToken"
2+ import " FungibleToken"
3+ import " EVM"
4+
5+ /// Contract deployed to the service account to emit events when fraudulent tokens
6+ /// from the Dec 27th, 2025 attack are retrieved or destroyed
7+ /// This is to have transparency with the actions the service account committee takes
8+ /// to reconcile the fraudulent tokens
9+
10+ access (all ) contract RetrieveFraudulentTokensEvents {
11+
12+ access (all ) let adminStoragePath : StoragePath
13+
14+ /// Event emitted when fraudulent tokens are retrieved from any address and stored in the service account's vault
15+ /// for later destruction
16+ /// @param typeIdentifier - The type identifier of the fraudulent tokens
17+ /// @param amount - The amount of fraudulent tokens retrieved
18+ /// @param fromAddress - The address from which the fraudulent tokens were retrieved.
19+ /// This can be a Cadence Address, a COA Address, or an EOA Address.
20+ access (all ) event FraudulentTokensRetrieved (typeIdentifier : String , amount : UFix64 , fromAddress : String )
21+
22+ /// Event emitted when fraudulent tokens are destroyed from the service account's vault
23+ /// @param typeIdentifier - The type identifier of the fraudulent tokens to be destroyed
24+ /// @param amount - The amount of fraudulent tokens destroyed
25+ access (all ) event FraudulentTokensDestroyed (typeIdentifier : String , amount : UFix64 )
26+
27+ /// Resource that allows only the service account admin to emit the events
28+ access (all ) resource Admin {
29+
30+ /// Emits the FraudulentTokensRetrieved event
31+ access (all ) fun emitRetrieveTokensEvent (typeIdentifier : String , amount : UFix64 , fromAddress : String ) {
32+ emit FraudulentTokensRetrieved (typeIdentifier : typeIdentifier , amount : amount , fromAddress : fromAddress )
33+ }
34+
35+ /// Emits the FraudulentTokensDestroyed event
36+ access (all ) fun emitDestroyTokensEvent (typeIdentifier : String , amount : UFix64 ) {
37+ emit FraudulentTokensDestroyed (typeIdentifier : typeIdentifier , amount : amount )
38+ }
39+ }
40+
41+ init () {
42+
43+ self .adminStoragePath = /storage/serviceAccountAdmin
44+
45+ // Create a new ServiceAccountAdmin resource
46+ self .account .storage .save (<- create Admin (), to : self .adminStoragePath )
47+
48+ // Store a new FlowToken Vault at a non-standard storage path to hold fraudulent tokens
49+ let emptyVault <- FlowToken .createEmptyVault (vaultType : Type <@FlowToken.Vault >())
50+ self .account .storage .save (<- emptyVault , to : /storage/fraudulentFlowTokenVault )
51+
52+ // Create a public capability to the Vault that only exposes
53+ // the deposit function through the Receiver interface
54+ let receiverCapability = self .account .capabilities .storage .issue <&FlowToken.Vault >(/storage/fraudulentFlowTokenVault )
55+ self .account .capabilities .publish (receiverCapability , at : /public/fraudulentFlowTokenReceiver )
56+
57+ // Create a public capability to the Vault that only exposes
58+ // the balance field through the Balance interface
59+ let balanceCapability = self .account .capabilities .storage .issue <&FlowToken.Vault >(/storage/fraudulentFlowTokenVault )
60+ self .account .capabilities .publish (balanceCapability , at : /public/fraudulentFlowTokenBalance )
61+
62+ // Create a new array to store the COAs to be destroyed
63+ let newCoaArray : @[EVM .CadenceOwnedAccount ] <- []
64+ self .account .storage .save (<- newCoaArray , to : /storage/coaArrayToDestroy )
65+
66+ /* --- Configure COA --- */
67+ //
68+ // Ensure there is not yet a CadenceOwnedAccount in the standard path
69+ let coaPath = /storage/evm
70+ if self .account .storage .type (at : coaPath ) == nil {
71+ // COA not found in standard path, create and publish a public **unentitled** capability
72+ self .account .storage .save (<- EVM .createCadenceOwnedAccount (), to : coaPath )
73+ let coaCapability = self .account .capabilities .storage .issue <&EVM.CadenceOwnedAccount >(coaPath )
74+ self .account .capabilities .publish (coaCapability , at : /public/evm )
75+ }
76+
77+ }
78+ }
0 commit comments