Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Burner contract to allow resource callbacks before destruction #407

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions contracts/Burner.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Burner is a contract that can facilitate the destruction of any resource on flow.
//
// Contributors
// - Austin Kline - https://twitter.com/austin_flowty
// - Deniz Edincik - https://twitter.com/bluesign
// - Bastian Müller - https://twitter.com/turbolent
pub contract Burner {
// When Crescendo (Cadence 1.0) is released, custom destructors will be removed from cadece.
// Burnable is an interface meant to replace this lost feature, allowing anyone to add a callback
// method to ensure they do not destroy something which is not meant to be, or to add logic based on destruction
// such as tracking the supply of an NFT Collection
//
// NOTE: The only way to see benefit from this interface is to call the burnCallback method yourself,
// or to always use the burn method in this contract. Anyone who owns a resource can always elect **not**
// to destroy a resource this way
pub resource interface Burnable {
pub fun burnCallback()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub fun burnCallback()
access(contract) fun burnCallback()

If this method is public and we use it to emit the FungibleToken.Burned event, wouldn't that be able to be spoofed or spammed since anyone can call it from their resource? I propose making this access(contract) so that only the burn function in this contract can call it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wait, is that even possible? If I make an access(contract) method in an interface, does that mean that only the contract the defines the resource interface implementation can access it, or does it mean the Burner contract can access it?

}

// burn is a global method which will destroy any resource it is given.
// If the provided resource implements the Burnable interface, it will call the burnCallback
// method and then destroy afterwards.
pub fun burn(_ r: @AnyResource) {
if let s <- r as? @{Burnable} {
s.burnCallback()
destroy s
} else if let arr <- r as? @[AnyResource] {
while arr.length > 0 {
let item <- arr.removeFirst()
self.burn(<-item)
}
destroy arr
} else if let stringDict <- r as? @{String: AnyResource} {
let keys = stringDict.keys
while keys.length > 0 {
let item <- stringDict.remove(key: keys.removeFirst())!
self.burn(<-item)
}
destroy stringDict
} else if let numDict <- r as? @{Number: AnyResource} {
let keys = numDict.keys
while keys.length > 0 {
let item <- numDict.remove(key: keys.removeFirst())!
self.burn(<-item)
}
destroy numDict
} else if let typeDict <- r as? @{Type: AnyResource} {
let keys = typeDict.keys
while keys.length > 0 {
let item <- typeDict.remove(key: keys.removeFirst())!
self.burn(<-item)
}
destroy typeDict
} else if let addressDict <- r as? @{Address: AnyResource} {
let keys = addressDict.keys
while keys.length > 0 {
let item <- addressDict.remove(key: keys.removeFirst())!
self.burn(<-item)
}
destroy addressDict
} else if let pathDict <- r as? @{Path: AnyResource} {
let keys = pathDict.keys
while keys.length > 0 {
let item <- pathDict.remove(key: keys.removeFirst())!
self.burn(<-item)
}
destroy pathDict
} else if let charDict <- r as? @{Character: AnyResource} {
let keys = charDict.keys
while keys.length > 0 {
let item <- charDict.remove(key: keys.removeFirst())!
self.burn(<-item)
}
destroy charDict
} else {
destroy r
}
}
}
32 changes: 32 additions & 0 deletions contracts/testContracts/BurnableTest.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import "Burner"

pub contract BurnableTest {
pub var totalBurned: UInt64

pub resource WithCallback: Burner.Burnable {
pub let allowDestroy: Bool

pub fun burnCallback() {
assert(self.allowDestroy, message: "allowDestroy must be set to true")
BurnableTest.totalBurned = BurnableTest.totalBurned + 1
}

init(_ allowDestroy: Bool) {
self.allowDestroy = allowDestroy
}
}

pub resource WithoutCallback {}

pub fun createSafe(allowDestroy: Bool): @WithCallback {
return <- create WithCallback(allowDestroy)
}

pub fun createUnsafe(): @WithoutCallback {
return <- create WithoutCallback()
}

init() {
self.totalBurned = 0
}
}
12 changes: 12 additions & 0 deletions flow.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,18 @@
"aliases": {
"testing": "0x0000000000000007"
}
},
"Burner": {
"source": "./contracts/Burner.cdc",
"aliases": {
"testing": "0x0000000000000007"
}
},
"BurnableTest": {
"source": "./contracts/testContracts/BurnableTest.cdc",
"aliases": {
"testing": "0x0000000000000007"
}
}
},
"networks": {
Expand Down
46 changes: 46 additions & 0 deletions lib/go/contracts/internal/assets/assets.go

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions lib/go/test/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ github.com/cockroachdb/pebble v0.0.0-20230906160148-46873a6a7a06 h1:T+Np/xtzIjYM
github.com/cockroachdb/pebble v0.0.0-20230906160148-46873a6a7a06/go.mod h1:bynZ3gvVyhlvjLI7PT6dmZ7g76xzJ7HpxfjgkzCGz6s=
github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ=
github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ=
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
Expand Down Expand Up @@ -242,6 +243,7 @@ github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA=
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
Expand Down Expand Up @@ -378,6 +380,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFb
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3 h1:lLT7ZLSzGLI08vc9cpd+tYmNWjdKDqyr/2L+f6U12Fk=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w=
github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU=
github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
Expand Down Expand Up @@ -447,14 +451,17 @@ github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0
github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.1.1-0.20170430222011-975b5c4c7c21/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
Expand Down Expand Up @@ -566,6 +573,7 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
Expand Down Expand Up @@ -593,6 +601,7 @@ github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXS
github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8=
github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
Expand All @@ -601,6 +610,7 @@ github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxzi
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
Expand Down Expand Up @@ -807,6 +817,7 @@ github.com/vmihailenco/msgpack/v4 v4.3.11 h1:Q47CePddpNGNhk4GCnAx9DDtASi2rasatE0
github.com/vmihailenco/msgpack/v4 v4.3.11/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
github.com/vmihailenco/tagparser v0.1.1 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY=
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM=
github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
Expand Down Expand Up @@ -1375,6 +1386,7 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Expand Down
98 changes: 98 additions & 0 deletions tests/test_burner.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import Test
import BlockchainHelpers

pub fun setup() {
Test.deployContract(name: "Burner", path: "../contracts/Burner.cdc", arguments: [])
Test.deployContract(name: "BurnableTest", path: "../contracts/testContracts/BurnableTest.cdc", arguments: [])
}

pub fun testWithCallbackDestory_Allowed() {
let acct = Test.createAccount()
txExecutor(
"burner/create_and_destroy_with_callback.cdc",
[acct],
[true]
)
}

pub fun testWithCallback_NotAllowed() {
let acct = Test.createAccount()

Test.expectFailure(fun() {
txExecutor(
"burner/create_and_destroy_with_callback.cdc",
[acct],
[false]
)
}, errorMessageSubstring: "allowDestroy must be set to true")
}

pub fun testWithoutCallbackDestroy_Allowed() {
let acct = Test.createAccount()
txExecutor(
"burner/create_and_destroy_without_callback.cdc",
[acct],
[]
)
}

pub fun testDestroy_Dict() {
let acct = Test.createAccount()

let types = [Type<Address>(), Type<String>(), Type<CapabilityPath>(), Type<Number>(), Type<Type>(), Type<Character>()]
for type in types {
txExecutor(
"burner/create_and_destroy_dict.cdc",
[acct],
[true, type]
)
}
}

pub fun testDestroy_Dict_NotAllowed() {
let acct = Test.createAccount()

let types = [Type<Address>(), Type<String>(), Type<CapabilityPath>(), Type<Number>(), Type<Type>(), Type<Character>()]
for type in types {
Test.expectFailure(fun() {
txExecutor(
"burner/create_and_destroy_dict.cdc",
[acct],
[false, type]
)
}, errorMessageSubstring: "allowDestroy must be set to true")
}
}

pub fun testDestroy_Array() {
let acct = Test.createAccount()
txExecutor(
"burner/create_and_destroy_array.cdc",
[acct],
[true]
)
}

pub fun loadCode(_ fileName: String, _ baseDirectory: String): String {
return Test.readFile("./".concat(baseDirectory).concat("/").concat(fileName))
}

pub fun txExecutor(_ txName: String, _ signers: [Test.Account], _ arguments: [AnyStruct]): Test.TransactionResult {
let txCode = loadCode(txName, "transactions")

let authorizers: [Address] = []
for signer in signers {
authorizers.append(signer.address)
}
let tx = Test.Transaction(
code: txCode,
authorizers: authorizers,
signers: signers,
arguments: arguments,
)
let txResult = Test.executeTransaction(tx)
if let err = txResult.error {
panic(err.message)
}
return txResult
}
17 changes: 17 additions & 0 deletions tests/transactions/burner/create_and_destroy_array.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import "BurnableTest"
import "Burner"

transaction(allowDestroy: Bool) {
prepare(acct: AuthAccount) {
let before = BurnableTest.totalBurned

let r <- BurnableTest.createSafe(allowDestroy: allowDestroy)
Burner.burn(<- [<- r])

if allowDestroy {
assert(before + 1 == BurnableTest.totalBurned, message: "totalBurned was lower than expected")
} else {
assert(before == BurnableTest.totalBurned, message: "totalBurned value changed unexpectedly")
}
}
}
35 changes: 35 additions & 0 deletions tests/transactions/burner/create_and_destroy_dict.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import "BurnableTest"
import "Burner"

transaction(allowDestroy: Bool, dictType: Type) {
prepare(acct: AuthAccount) {
let before = BurnableTest.totalBurned

let r <- BurnableTest.createSafe(allowDestroy: allowDestroy)
if dictType as? Number != nil {
let d: @{Number: AnyResource} <- {1: <-r}
Burner.burn(<-d)
} else if dictType as? String != nil {
let d: @{String: AnyResource} <- {"a": <-r}
Burner.burn(<-d)
} else if dictType as? Path != nil {
let d: @{Path: AnyResource} <- {/public/foo: <-r}
Burner.burn(<-d)
} else if dictType as? Address != nil {
let d: @{Address: AnyResource} <- {Address(0x1): <-r}
Burner.burn(<-d)
} else if dictType as? Character != nil {
let d: @{Character: AnyResource} <- {"c": <-r}
Burner.burn(<-d)
} else {
let d: @{Type: AnyResource} <- {Type<Burner>(): <-r}
Burner.burn(<-d)
}

if allowDestroy {
assert(before + 1 == BurnableTest.totalBurned, message: "totalBurned was lower than expected")
} else {
assert(before == BurnableTest.totalBurned, message: "totalBurned value changed unexpectedly")
}
}
}
17 changes: 17 additions & 0 deletions tests/transactions/burner/create_and_destroy_with_callback.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import "BurnableTest"
import "Burner"

transaction(allowDestroy: Bool) {
prepare(acct: AuthAccount) {
let before = BurnableTest.totalBurned

let r <- BurnableTest.createSafe(allowDestroy: allowDestroy)
Burner.burn(<- r)

if allowDestroy {
assert(before + 1 == BurnableTest.totalBurned, message: "totalBurned was lower than expected")
} else {
assert(before == BurnableTest.totalBurned, message: "totalBurned value changed unexpectedly")
}
}
}
Loading