The create_contract function doesn’t actually invoke the constructor of the provided contract. Instead, it initializes the contract’s storage directly. Therefore, when using create_contract, you’re required to specify the storage structure directly rather than providing arguments for a constructor.
Just to clarify. The deployed contract’s storage does not contain a record, per se. My example was about passing in more than one value. For example, if the storage would be
Whenever you define multiple fields within data, it inherently behaves as a record. In the provided example, even though there isn’t a direct record definition in the constructor, data is still structured as a record due to its multiple fields. It’s the inherent nature of SmartPy contracts to treat data with multiple fields as a record.
Thanks for the answer. So basically you pass in as if the storage were a record, and if there were to be a map or anything else more complex, you would pass it in as it were one element of a record.
or would, in this case, the first_owner have to be a map object with address and balance as the first entry of the map, since constructor of MyContract takes in the ‘first_owner’ as singular parameter?
You’re close! But let’s clarify a few things about initializing maps in SmartPy.
The sp.map type is a key-value store. In your example, it looks like address would be the key and balance the associated value.
For your contract:
class MyContract(sp.Contract):
def __init__(self, admin, first_owner):
# Specify the type of the parameters
sp.cast(admin, sp.address)
sp.cast(
first_owner,
sp.record(address=sp.address, balance=sp.nat))
# Build the balances map
# Notice you could accept balances directly as the parameter
# instead of building it in the constructor from a record.
balances = { first_owner.address: first_owner.balance }
# storage initialization
self.data.admin = admin
self.data.balances = balances
# Assigning to self.data is a convenient way to build a record.
# So the two previous assignation are perfectly equivalent to
self.data = sp.record(admin=admin, balances=balances)
When using create_contract, you never invoke the constructor so you have to build the storage exactly like the constructor would do, except this time you cannot insert values into self.data.
And how would you pass them in since they are intended to be empty initially?
rn, if I try to run the code (without passing the ownersMap or ticket), it says
Variable ‘storage’ of type TRecord++(admin = sp.address, idNum= sp.nat, totalAmount= sp.nat, ownersMap = sp.map(sp.address, sp.nat), ticket = sp.option(sp.ticket(sp.nat))) cannot be used twice because it contains a ticket.
In SmartPy, there isn’t a concept of an “empty record” in the way that you might think of an empty map ({}). Instead, if you want a record that might be unset or “empty”, you would utilize the option type.
Refer back to the option documentation page for a clearer understanding. With SmartPy’s option system, you can define values that can be None or sp.Some<value>. The type of this is denoted as sp.option[<type of the value>].
You seem to be trying to cast None into a record. What you likely intend is to cast it into an option of a record. The correct way would be: sp.cast(None, sp.option[sp.record(contract=sp.address, id=sp.nat, amount=sp.nat)])
In your example, when you use sp.cast(None, ...), you’re essentially defining an “empty” (in fact unset) record. The None in the sp.cast is already signifying the absence of a value. So, you’ve correctly represented an “unset” or “empty” record without perhaps realizing it. The explicit type definition that follows just ensures that it’s cast correctly and enforces type.