Skip to main content

Instantiating a Contract

When contracts are stored on the chain they must be instantiated. I cover storing contracts on a chain in a later section. Instantiating a contract is like creating an object in other languages, however, it is achieved by a special message. This message is an InstantiateMsg located under src/lib.rs of package/sg721. Let's add something to it!

The InstantiateMsgโ€‹

When a user instantiates the NFT contract, he must specify the name and symbol (for metadata) of NFT, as well as a minter address. This is a special address that has full power to mint new NFTs (but not modify existing ones).

Our InstantiateMsg looks like this:

pub struct InstantiateMsg {
pub name: String,
pub symbol: String,
pub minter: String,
pub collection_info: CollectionInfo<RoyaltyInfoResponse>,
}

The minter can either be an external actor (e.g. web server, using PubKey) or another contract. If you just want to customize the minting behavior but not other functionality, you could extend this contract (importing code and wiring it together) or just create a custom contract as the owner and use that contract to Mint.

Instantiationโ€‹

The instantiation code is implemented in src/contract.rs:

pub fn instantiate(
&self,
deps: DepsMut,
_env: Env,
info: MessageInfo,
msg: InstantiateMsg,
) -> Result<Response, ContractError> {
// no funds should be sent to this contract
nonpayable(&info)?;

// cw721 instantiation
let info = ContractInfoResponse {
name: msg.name,
symbol: msg.symbol,
};
self.parent.contract_info.save(deps.storage, &info)?;

let minter = deps.api.addr_validate(&msg.minter)?;
self.parent.minter.save(deps.storage, &minter)?;

// sg721 instantiation
if msg.collection_info.description.len() > MAX_DESCRIPTION_LENGTH as usize {
return Err(ContractError::DescriptionTooLong {});
}

let image = Url::parse(&msg.collection_info.image)?;

if let Some(ref external_link) = msg.collection_info.external_link {
Url::parse(external_link)?;
}

let royalty_info: Option<RoyaltyInfo> = match msg.collection_info.royalty_info {
Some(royalty_info) => Some(RoyaltyInfo {
payment_address: deps.api.addr_validate(&royalty_info.payment_address)?,
share: share_validate(royalty_info.share)?,
}),
None => None,
};

deps.api.addr_validate(&msg.collection_info.creator)?;

let collection_info = CollectionInfo {
creator: msg.collection_info.creator,
description: msg.collection_info.description,
image: msg.collection_info.image,
external_link: msg.collection_info.external_link,
royalty_info,
};

self.collection_info.save(deps.storage, &collection_info)?;

self.ready.save(deps.storage, &false)?;

Ok(Response::new()
.add_attribute("action", "instantiate")
.add_attribute("collection_name", info.name)
.add_attribute("image", image.to_string()))
}

The instantiate has 4 arguments:

  • deps - The dependencies, this contains your contract storage, the ability to query other contracts and balances, and some API functionality.
  • env - The environment, contains contract information such as its address, block information such as current height and time, as well as some optional transaction info.
  • info - Message metadata, contains the sender of the message (Addr) and the funds sent with it a Vec<Coin>.
  • msg - The InstantiateMsg you define in package/sg721/src/lib.rs.