description |
---|
Optionally perform state migrations of Secret Network smart contracts |
Contracts on Secret Network can be initialized as migratable, which allows the contract administrator to upload a new version of the contract and then send a migrate
message to move to the new code, which "migrates" storage from a previous contract to the new contract.
There are two key components to a migratable contract:
- A designated
admin
whose address will be allowed to perform migrations. - The availability of a
MigrateMsg
transaction.
On instantiation
, the contract creator can specify an admin
address, which can be the contract creator's wallet address, an external account, or a governance contract, etc. Whatever the address is, this admin
gains the ability to migrate the contract to a new codeId
and codeHash
, and can also update or clear the admin
address. When the admin
invokes the MigrateMsg
message, the migrate()
function is called on the new contract, where the new contract can optionally perform state migrations from an old contract.
{% hint style="info" %}
Once the migrate()
function is invoked, the contract address now points to the new code, and anybody contacting that address will reach the new code. The old code becomes unreachable.
{% endhint %}
Performing a contract migration is a three step process:
- Write a newer version of the contract you wish to update
- Upload the new smart contract, but don’t instantiate it
- Use a dedicated MigrateMsg transaction to point the new contract to the code you wish to migrate
When migrating, the new contract must have a migrate
function as an entry_point.
{% code overflow="wrap" %}
#[entry_point]
pub fn migrate(_deps: DepsMut, _env: Env, msg: MigrateMsg) -> StdResult<Response> {
match msg {
MigrateMsg::Migrate {} => Ok(Response::default()),
MigrateMsg::StdError {} => Err(StdError::generic_err("this is an std error")),
}
}
{% endcode %}
The migrate
function provides the ability to make any desired changes to the contract's state, similar to a database migration.
If the migrate
function returns an error, the transaction will abort and no state changes will occur.
{% code overflow="wrap" %}
import { SecretNetworkClient, Wallet } from "secretjs";
import dotenv from "dotenv";
dotenv.config();
const wallet = new Wallet(process.env.MNEMONIC);
const secretjs = new SecretNetworkClient({
chainId: "pulsar-3",
url: "https://api.pulsar.scrttestnet.com",
wallet: wallet,
walletAddress: wallet.address,
});
const codeId = 1; // codeId for new contract
const contractCodeHash = ""; // codeHash for new contract
const contractAddress = ""; // contract address, which doesn't change upon migration
let main = async () => {
const migrateMsg = {
remove_users: { a, b }, // this is an example. migrateMsg can be left empty if contract storage is unchanged during migration
};
const tx = await secretjs.tx.compute.migrateContract(
{
code_id: codeId,
contract_address: contractAddress,
sender: wallet.address,
code_hash: contractCodeHash,
msg: migrateMsg,
},
{
gasLimit: 400_000,
}
);
console.log(tx.rawLog);
};
main();
{% endcode %}
Secret contracts instantiated prior to the v1.11 network upgrade can be migrated via a SCRT governance signaling proposal. If approved for migration, the contracts' code hashes will be matched to a chosen admin key and added to a list which is hardcoded inside the enclave for reference.
{% hint style="info" %} Further reading on hardcoded admin list {% endhint %}
In order for a Secret contract instantiated after the v1.11 network upgrade to be migrated, the contract must be instantiated with an admin. To query whether or not a Secret contract was instantiated with an admin
, use the contractInfo
method:
let queryContractInfo = async () => {
let query = await secretjs.query.compute.contractInfo({
contract_address: contractAddress,
code_hash: contractCodeHash,
});
console.log(query);
};
queryContractInfo();
The query will return the admin address, if there is one:
{% code overflow="wrap" %}
{
contract_address: 'secret15l8cqadh5pruweuxvr5ku830hamqny8ey2q2vf',
contract_info: {
code_id: '1303',
creator: 'secret1j7n3xx4sfgjea4unghd78qvnvxdz49cxmrkqlj',
label: 'migrate example',
created: { block_height: '1088301', tx_index: '0' },
ibc_port_id: '',
admin: 'secret1j7n3xx4sfgjea4unghd78qvnvxdz49cxmrkqlj',
admin_proof: 'TSxuJZZG0/eYGggmXNXw79So9jET3zLIy2An9bB5dA0='
}
}
{% endcode %}
To query if a Secret contract was migrated successfully, use the contractHistory
method:
let queryContractHistory = async () => {
let query = await secretjs.query.compute.contractHistory({
contract_address: contractAddress,
code_hash: contractCodeHash,
});
console.log(query);
};
queryContractHistory();
The method is designed to retrieve the history of a contract, specifically its code changes over time. The method returns an object containing an array of ContractCodeHistoryEntry
items.