Skip to content

Commit 64c9531

Browse files
authored
invalid transfer token and forwarding (#24)
* invalid transfer token * fix forwarding contract and tests * assets
1 parent dde8b46 commit 64c9531

File tree

8 files changed

+255
-70
lines changed

8 files changed

+255
-70
lines changed

contracts/contracts.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ func ExampleToken(fungibleTokenAddr string) []byte {
5454
// CustomToken returns the ExampleToken contract with a custom name.
5555
//
5656
// The returned contract will import the FungibleToken interface from the specified address.
57-
func CustomToken(fungibleTokenAddr, tokenName, storageName string) []byte {
57+
func CustomToken(fungibleTokenAddr, tokenName, storageName, initialBalance string) []byte {
5858
code := assets.MustAssetString(exampleTokenFilename)
5959

6060
code = strings.ReplaceAll(
@@ -75,6 +75,12 @@ func CustomToken(fungibleTokenAddr, tokenName, storageName string) []byte {
7575
storageName,
7676
)
7777

78+
code = strings.ReplaceAll(
79+
code,
80+
"1000.0",
81+
initialBalance,
82+
)
83+
7884
return []byte(code)
7985
}
8086

contracts/contracts_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ func TestExampleTokenContract(t *testing.T) {
2929
}
3030

3131
func TestCustomExampleTokenContract(t *testing.T) {
32-
contract := contracts.CustomToken(addrA.Hex(), "UtilityCoin", "utilityCoin")
32+
contract := contracts.CustomToken(addrA.Hex(), "UtilityCoin", "utilityCoin", "100.0")
3333
assert.NotNil(t, contract)
3434
assert.Contains(t, string(contract), addrA.Hex())
3535
}

contracts/internal/assets/assets.go

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/contracts/ExampleToken.cdc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ pub contract ExampleToken: FungibleToken {
176176
// Create a public capability to the stored Vault that only exposes
177177
// the `deposit` method through the `Receiver` interface
178178
//
179-
self.account.link<&ExampleToken.Vault{FungibleToken.Receiver}>(
179+
self.account.link<&{FungibleToken.Receiver}>(
180180
/public/exampleTokenReceiver,
181181
target: /storage/exampleTokenVault
182182
)

src/contracts/TokenForwarding.cdc

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,32 +26,43 @@ pub contract TokenForwarding {
2626
// This is where the deposited tokens will be sent.
2727
// The type indicates that it is a reference to a receiver
2828
//
29-
access(self) var recipient: &{FungibleToken.Receiver}
29+
access(self) var recipient: Capability
3030

3131
// deposit
3232
//
3333
// Function that takes a Vault object as an argument and forwards
3434
// it to the recipient's Vault using the stored reference
3535
//
3636
pub fun deposit(from: @FungibleToken.Vault) {
37-
emit ForwardedDeposit(amount: from.balance, from: self.owner?.address)
38-
self.recipient.deposit(from: <-from)
37+
let receiverRef = self.recipient.borrow<&{FungibleToken.Receiver}>()!
38+
39+
let balance = from.balance
40+
41+
receiverRef.deposit(from: <-from)
42+
43+
emit ForwardedDeposit(amount: balance, from: self.owner?.address)
3944
}
4045

4146
// changeRecipient changes the recipient of the forwarder to the provided recipient
4247
//
43-
pub fun changeRecipient(_ newRecipient: &{FungibleToken.Receiver}) {
48+
pub fun changeRecipient(_ newRecipient: Capability) {
49+
pre {
50+
newRecipient.borrow<&{FungibleToken.Receiver}>() != nil: "Could not borrow Receiver reference from the Capability"
51+
}
4452
self.recipient = newRecipient
4553
}
4654

47-
init(recipient: &{FungibleToken.Receiver}) {
55+
init(recipient: Capability) {
56+
pre {
57+
recipient.borrow<&{FungibleToken.Receiver}>() != nil: "Could not borrow Receiver reference from the Capability"
58+
}
4859
self.recipient = recipient
4960
}
5061
}
5162

5263
// createNewForwarder creates a new Forwarder reference with the provided recipient
5364
//
54-
pub fun createNewForwarder(recipient: &{FungibleToken.Receiver}): @Forwarder {
65+
pub fun createNewForwarder(recipient: Capability): @Forwarder {
5566
return <-create Forwarder(recipient: recipient)
5667
}
5768
}

test/templates.go

Lines changed: 84 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ import (
1010
// a new Vault instance and stores it in storage.
1111
// balance is an argument to the Vault constructor.
1212
// The Vault must have been deployed already.
13-
func GenerateCreateTokenScript(fungibleAddr, tokenAddr flow.Address, tokenName, storageName string) []byte {
13+
func GenerateCreateTokenScript(fungibleAddr, tokenAddr flow.Address, tokenName string) []byte {
14+
storageName := MakeFirstLowerCase(tokenName)
15+
1416
template := `
1517
import FungibleToken from 0x%[1]s
1618
import %[3]s from 0x%[2]s
@@ -21,7 +23,7 @@ func GenerateCreateTokenScript(fungibleAddr, tokenAddr flow.Address, tokenName,
2123
let vault <- %[3]s.createEmptyVault()
2224
acct.save(<-vault, to: /storage/%[4]sVault)
2325
24-
acct.link<&%[3]s.Vault{FungibleToken.Receiver}>(/public/%[4]sReceiver, target: /storage/%[4]sVault)
26+
acct.link<&{FungibleToken.Receiver}>(/public/%[4]sReceiver, target: /storage/%[4]sVault)
2527
acct.link<&%[3]s.Vault{FungibleToken.Balance}>(/public/%[4]sBalance, target: /storage/%[4]sVault)
2628
}
2729
}
@@ -31,7 +33,9 @@ func GenerateCreateTokenScript(fungibleAddr, tokenAddr flow.Address, tokenName,
3133

3234
// GenerateDestroyVaultScript creates a script that withdraws
3335
// tokens from a vault and destroys the tokens
34-
func GenerateDestroyVaultScript(fungibleAddr, tokenAddr flow.Address, tokenName, storageName string, withdrawAmount int) []byte {
36+
func GenerateDestroyVaultScript(fungibleAddr, tokenAddr flow.Address, tokenName string, withdrawAmount int) []byte {
37+
storageName := MakeFirstLowerCase(tokenName)
38+
3539
template := `
3640
import FungibleToken from 0x%[1]s
3741
import %[3]s from 0x%[2]s
@@ -55,7 +59,9 @@ func GenerateDestroyVaultScript(fungibleAddr, tokenAddr flow.Address, tokenName,
5559

5660
// GenerateTransferVaultScript creates a script that withdraws an tokens from an account
5761
// and deposits it to another account's vault
58-
func GenerateTransferVaultScript(fungibleAddr, tokenAddr flow.Address, receiverAddr flow.Address, tokenName, storageName string, amount int) []byte {
62+
func GenerateTransferVaultScript(fungibleAddr, tokenAddr flow.Address, receiverAddr flow.Address, tokenName string, amount int) []byte {
63+
storageName := MakeFirstLowerCase(tokenName)
64+
5965
template := `
6066
import FungibleToken from 0x%s
6167
import %s from 0x%s
@@ -64,10 +70,10 @@ func GenerateTransferVaultScript(fungibleAddr, tokenAddr flow.Address, receiverA
6470
prepare(acct: AuthAccount) {
6571
let recipient = getAccount(0x%s)
6672
67-
let providerRef = acct.borrow<&%s.Vault{FungibleToken.Provider}>(from: /storage/%sVault)
73+
let providerRef = acct.borrow<&{FungibleToken.Provider}>(from: /storage/%sVault)
6874
?? panic("Could not borrow Provider reference to the Vault!")
6975
70-
let receiverRef = recipient.getCapability(/public/%sReceiver)!.borrow<&%s.Vault{FungibleToken.Receiver}>()
76+
let receiverRef = recipient.getCapability(/public/%sReceiver)!.borrow<&{FungibleToken.Receiver}>()
7177
?? panic("Could not borrow receiver reference to the recipient's Vault")
7278
7379
let tokens <- providerRef.withdraw(amount: %d.0)
@@ -77,19 +83,21 @@ func GenerateTransferVaultScript(fungibleAddr, tokenAddr flow.Address, receiverA
7783
}
7884
`
7985

80-
return []byte(fmt.Sprintf(template, fungibleAddr, tokenName, tokenAddr, receiverAddr, tokenName, storageName, storageName, tokenName, amount))
86+
return []byte(fmt.Sprintf(template, fungibleAddr, tokenName, tokenAddr, receiverAddr, storageName, storageName, amount))
8187
}
8288

8389
// GenerateMintTokensScript creates a script that uses the admin resource
8490
// to mint new tokens and deposit them in a Vault
85-
func GenerateMintTokensScript(fungibleAddr, tokenAddr flow.Address, receiverAddr flow.Address, tokenName, storageName string, amount float64) []byte {
91+
func GenerateMintTokensScript(fungibleAddr, tokenAddr flow.Address, receiverAddr flow.Address, tokenName string, amount float64) []byte {
92+
storageName := MakeFirstLowerCase(tokenName)
93+
8694
template := `
8795
import FungibleToken from 0x%[1]s
8896
import %[3]s from 0x%[2]s
8997
9098
transaction {
9199
let tokenAdmin: &%[3]s.Administrator
92-
let tokenReceiver: &%[3]s.Vault{FungibleToken.Receiver}
100+
let tokenReceiver: &{FungibleToken.Receiver}
93101
94102
prepare(signer: AuthAccount) {
95103
self.tokenAdmin = signer
@@ -98,7 +106,7 @@ func GenerateMintTokensScript(fungibleAddr, tokenAddr flow.Address, receiverAddr
98106
99107
self.tokenReceiver = getAccount(0x%[5]s)
100108
.getCapability(/public/%[4]sReceiver)!
101-
.borrow<&%[3]s.Vault{FungibleToken.Receiver}>()
109+
.borrow<&{FungibleToken.Receiver}>()
102110
?? panic("Unable to borrow receiver reference")
103111
}
104112
@@ -118,7 +126,9 @@ func GenerateMintTokensScript(fungibleAddr, tokenAddr flow.Address, receiverAddr
118126

119127
// GenerateBurnTokensScript creates a script that uses the admin resource
120128
// to destroy tokens and deposit them in a Vault
121-
func GenerateBurnTokensScript(fungibleAddr, tokenAddr flow.Address, tokenName, storageName string, amount int) []byte {
129+
func GenerateBurnTokensScript(fungibleAddr, tokenAddr flow.Address, tokenName string, amount int) []byte {
130+
storageName := MakeFirstLowerCase(tokenName)
131+
122132
template := `
123133
import FungibleToken from 0x%[1]s
124134
import %[3]s from 0x%[2]s
@@ -154,10 +164,44 @@ func GenerateBurnTokensScript(fungibleAddr, tokenAddr flow.Address, tokenName, s
154164
return []byte(fmt.Sprintf(template, fungibleAddr, tokenAddr, tokenName, storageName, amount))
155165
}
156166

167+
// GenerateTransferInvalidVaultScript creates a script that withdraws an tokens from an account
168+
// and tries to deposit it into a vault of the wrong type. Should fail
169+
func GenerateTransferInvalidVaultScript(fungibleAddr, tokenAddr, otherTokenAddr, receiverAddr flow.Address, tokenName, otherTokenName string, amount int) []byte {
170+
storageName := MakeFirstLowerCase(tokenName)
171+
172+
otherStorageName := MakeFirstLowerCase(tokenName)
173+
174+
template := `
175+
import FungibleToken from 0x%s
176+
import %s from 0x%s
177+
import %s from 0x%s
178+
179+
transaction {
180+
prepare(acct: AuthAccount) {
181+
let recipient = getAccount(0x%s)
182+
183+
let providerRef = acct.borrow<&{FungibleToken.Provider}>(from: /storage/%sVault)
184+
?? panic("Could not borrow Provider reference to the Vault!")
185+
186+
let receiverRef = recipient.getCapability(/public/%sReceiver)!.borrow<&{FungibleToken.Receiver}>()
187+
?? panic("Could not borrow receiver reference to the recipient's Vault")
188+
189+
let tokens <- providerRef.withdraw(amount: %d.0)
190+
191+
receiverRef.deposit(from: <-tokens)
192+
}
193+
}
194+
`
195+
196+
return []byte(fmt.Sprintf(template, fungibleAddr, tokenName, tokenAddr, otherTokenName, otherTokenAddr, receiverAddr, storageName, otherStorageName, amount))
197+
}
198+
157199
// GenerateInspectVaultScript creates a script that retrieves a
158200
// Vault from the array in storage and makes assertions about
159201
// its balance. If these assertions fail, the script panics.
160-
func GenerateInspectVaultScript(fungibleAddr, tokenAddr, userAddr flow.Address, tokenName, storageName string, expectedBalance float64) []byte {
202+
func GenerateInspectVaultScript(fungibleAddr, tokenAddr, userAddr flow.Address, tokenName string, expectedBalance float64) []byte {
203+
storageName := MakeFirstLowerCase(tokenName)
204+
161205
template := `
162206
import FungibleToken from 0x%[1]s
163207
import %[3]s from 0x%[2]s
@@ -180,6 +224,7 @@ func GenerateInspectVaultScript(fungibleAddr, tokenAddr, userAddr flow.Address,
180224
// the total supply of tokens in existence
181225
// and makes assertions about the number
182226
func GenerateInspectSupplyScript(fungibleAddr, tokenAddr flow.Address, tokenName string, expectedSupply int) []byte {
227+
183228
template := `
184229
import FungibleToken from 0x%[1]s
185230
import %[3]s from 0x%[2]s
@@ -194,3 +239,30 @@ func GenerateInspectSupplyScript(fungibleAddr, tokenAddr flow.Address, tokenName
194239

195240
return []byte(fmt.Sprintf(template, fungibleAddr, tokenAddr, tokenName, expectedSupply))
196241
}
242+
243+
// GenerateCreateForwarderScript creates a script that instantiates
244+
// a new forwarder instance in an account
245+
func GenerateCreateForwarderScript(fungibleAddr, forwardingAddr, receiverAddr flow.Address, tokenName string) []byte {
246+
storageName := MakeFirstLowerCase(tokenName)
247+
248+
template := `
249+
import FungibleToken from 0x%[1]s
250+
import TokenForwarding from 0x%[2]s
251+
252+
transaction {
253+
254+
prepare(acct: AuthAccount) {
255+
let recipient = getAccount(0x%[4]s).getCapability(/public/%[3]sReceiver)!
256+
257+
let vault <- TokenForwarding.createNewForwarder(recipient: recipient)
258+
acct.save(<-vault, to: /storage/%[3]sForwarder)
259+
260+
if acct.getCapability(/public/%[3]sReceiver)!.borrow<&{FungibleToken.Receiver}>() != nil {
261+
acct.unlink(/public/%[3]sReceiver)
262+
}
263+
acct.link<&{FungibleToken.Receiver}>(/public/%[3]sReceiver, target: /storage/%[3]sForwarder)
264+
}
265+
}
266+
`
267+
return []byte(fmt.Sprintf(template, fungibleAddr, forwardingAddr, storageName, receiverAddr))
268+
}

test/test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package test
22

33
import (
4+
"bytes"
45
"io/ioutil"
6+
"strings"
57
"testing"
68

79
"github.com/onflow/flow-go-sdk/crypto"
@@ -99,3 +101,18 @@ func readFile(path string) []byte {
99101
}
100102
return contents
101103
}
104+
105+
// MakeFirstLowerCase makes the first letter in a string lowercase
106+
func MakeFirstLowerCase(s string) string {
107+
108+
if len(s) < 2 {
109+
return strings.ToLower(s)
110+
}
111+
112+
bts := []byte(s)
113+
114+
lc := bytes.ToLower([]byte{bts[0]})
115+
rest := bts[1:]
116+
117+
return string(bytes.Join([][]byte{lc, rest}, nil))
118+
}

0 commit comments

Comments
 (0)