Whether it's to deploy a contract from a wasm/polkavm code or using an existing code hash. You can do it using the ContractDeployer.
Initialize ContractDeployer
import{ DedotClient, WsProvider }from'dedot';import{ ContractDeployer }from'dedot/contract';import{ stringToHex }from'dedot/utils'import{ FlipperContractApi }from'./flipper';import flipperMetadata from'./flipper.json'assert{ type:'json'};// instanciate an api clientconstclient=awaitDedotClient.new(newWsProvider('...'));// load contract wasm or prepare a codeHashconstcode='0x...';// wasm or polkavm (from .wasm or .polkavm files)constexistingCodeHash='0x...'// uploaded wasm/polkavm// create a ContractDeployer instanceconstdeployer=newContractDeployer<FlipperContractApi>(client, flipperMetadata, code);// OR from existingCodeHash// const deployer = new ContractDeployer<FlipperContractApi>(client, flipperMetadata, existingCodeHash);
[ink! v6 & solidity] Map the account before interacting with pallet-revive
Pallet revive is designed to work with evm address/account (20 bytes / H160) by default. So before interact with contracts deployed on pallet revive via a Substrate address (32 bytes / H256), one need to map their Substrate address to a corresponding EVM address first.
Simply submit a transaction (tx.revive.mapAccount) to map the account:
Dry-run the contract instantiation
Starting from [email protected], dry-runs will be automatically performed internally to validate the transaction and estimate gas fees and storage deposit limits. You no longer need to run a dry-run manually unless you have advanced or custom use cases.
Dry run the constructor call to help validate the contract instantiation and estimate gas-fee for the transaction.
Error handling if contract constructor returning a Result<Self, Error>
In case the contract constructor returning a Result<Self, Error>, you can also check the see if the instantiation get any errors before submitting the transaction.
Submit contract instantiation transaction
After dry-run the transaction to make sure there will be no errors. Now let's submit the transaction to instantiate the contract and listen for events to extract contract address.
Retrieve contract address & contract instance after deployment
Once the contract deployment transaction is included in the best chain block or finalized, you can easily retrieve the contract address or initialize the contract instance directly from the deployment result.
Calculate contract address manually
The instructions below show how to manually calculate the contract address after deployment. This is intended for advanced use cases only β we recommend using the unified API to retrieve the contract address from the deployment result as shown above.
[ink! v4 & v5] Retrieve contract address from deployment events
Extract from Contract.Instantiated event
Listen for Contract.Instantiated event from system events
Calculate contract address via deployment salt (CREATE2)
If one deploy the contract using a deployment salt (32 bytes), one can deterministically calculate the contract address even before deploying it using the CREATE2 method.
Calculate contract address via deployer's nonce (CREATE1)
If one deploy the contract without a deployment salt, one can calculate the contract address using the deployer's nonce before submitting the contract deployment transaction using the CREATE1 method
import { toEvmAddress } from 'dedot/contracts';
// Check if the account is mapped yet
const mappedAccount = await client.query.revive.originalAccount(toEvmAddress(CALLER));
if (mappedAccount) {
console.log('Address has already been mapped!');
} else {
console.log('Address not mapped yet, map it now!');
await client.tx.revive
.mapAccount()
.signAndSend(CALLER, ({ status }) => console.log(status.type))
.untilFinalized();
}
import { generateRandomHex } from 'dedot/utils';
const CALLER = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'; // Alice
// Some random salt to prevent duplication issue
// Salt is optional, you can skip this to use an empty salt
const salt = generateRandomHex(); // for inkv6, salt need to be a 32-byte hex
// Dry run the constructor call for validation and gas estimation
// An Error will be thrown out if there's a DispatchError or LangError (contract level error)
// More on this in the handling error section below
const dryRun = await deployer.query.new(true, { caller: CALLER, salt })
const { raw: { gasRequired, storageDeposit } } = dryRun;
const { data, raw } = await deployer.query.new(true, { caller: ALICE, salt })
if (data.isErr) {
console.log('Contract instantiation returning an error:', data.err);
} else {
// submitting the transaction
}
// Generate a random salt
const salt = generateRandomHex();
// Submitting the transaction to instanciate the contract
const deploymentResult = await deployer.tx
.new(true, { salt }) // `new` is the constructor defined in the contract
.signAndSend(alice, ({ status }) => {
console.log(`π Transaction status: ${status.type}`);
})
.untilFinalized(); // or .untilBestChainBlockIncluded();
// Calculate contract address
const contractAddress = await deploymentResult.contractAddress()
// Initialize fully-typed contract instance
const contract = await deploymentResult.contract();
// You can now interact with the contract via contract instance
const { data: value } = await contract.query.get();
console.log('Flipper value:', value)