Deploy Contracts
Deploying a contract from a Universal Profile using the Browser Extension showing the bytecode of the contract
Setupβ
npm install ethers
Deploy a contract from a UPβ
The Universal Profile browser extension will magically wrap all the calls internally so you don't have to worry about crafting custom transactions. Simply use eth_sendTransaction
as you always did while working with EOA.
import { ethers } from 'ethers';
const provider = new ethers.BrowserProvider(window.lukso);
await provider.send("eth_requestAccounts", []);
const signer = await provider.getSigner();
const account = await signer.getAddress();
// Send transaction
const tx = await signer.sendTransaction({
from: account, // The Universal Profile address
value: 0, // optional, in case constructor is payable
data: "0x608060405234801561001..." // Full bytecode of the contract to deploy + constructor args
});
const receipt = await tx.wait();
You can use the contract factory from ethers.js to deploy a contract by supplying its ABI and bytecode. For instance, if you're deploying an LSP7Mintable contract, you can obtain its ABI and bytecode from the @lukso/lsp-smart-contracts
package.
npm install @lukso/lsp-smart-contracts
import LSP7Mintable from "@lukso/lsp-smart-contracts/artifacts/LSP7Mintable.json"
// Prepare the factory with the abi and bytecode
const lsp7Factory = new ethers.ContractFactory(
LSP7Mintable.abi,
LSP7Mintable.bytecode,
signer
);
// send the transaction of deployment
const transaction = await lsp7Factory.deploy(
'Yamen token',
'no desc',
"0x0000000000000000000000000000000000000011",
0,
false
);
const receipt = await transaction.waitForDeployment();
Retrieve Contract Deployment Infosβ
There will be no contractAddress
field in the transaction receipt
unlike normal transactions happening with Metamask. This is because the UP browser extension wraps the deployment transaction within a normal contract execution transaction. The contract address can be calculated using the nonce and the address of the profile or retrieved using the logs (code snippet below).
import { ethers } from 'ethers';
const provider = new ethers.BrowserProvider(window.lukso);
await provider.send("eth_requestAccounts", []);
const signer = await provider.getSigner();
const account = await signer.getAddress();
// Send transaction
const tx = await signer.sendTransaction({
from: account, // The Universal Profile address
value: 0 // optional, in case constructor is payable
data: "0x0x608060405234801561001.." // Full bytecode of the contract to deploy + constructor args
});
const receipt = await tx.wait();
// The signature of the ContractCreated event emitted by the profile
/**
* event ContractCreated(
* uint256 indexed operationType,
* address indexed contractAddress,
* uint256 value,
* bytes32 indexed salt
* )
*
* 0xa1fb700aaee2ae4a2ff6f91ce7eba292f89c2f5488b8ec4c5c5c8150692595c3 = keccak256('ContractCreated(uint256,address,uint256,bytes32)')
*/
const targetTopic0 = '0xa1fb700aaee2ae4a2ff6f91ce7eba292f89c2f5488b8ec4c5c5c8150692595c3';
for (const log of receipt.logs) {
// Check if the first topic matches the target topic
if (log.topics[0] === targetTopic0) {
// Decode the operation type from bytes32 to number
const operationType = parseInt(log.topics[1], 16);
// Decode the contract address from bytes32 to Ethereum address
const contractAddress = `0x${log.topics[2].slice(-40)}`;
// Keep the salt as is
const salt = log.topics[3];
console.log('Found target event:');
console.log('Operation Type:', operationType);
console.log('Contract Address:', contractAddress);
console.log('Salt:', salt);
}
}
Examplesβ
Deploy an LSP7 Tokenβ
import { ethers } from 'ethers';
import UniversalProfileArtifact from '@lukso/lsp-smart-contracts/artifacts/UniversalProfile.json';
// Path to generated artifact for your Custom Token Contract
import MyCustomToken from './path/to/artifacts/MyCustomToken.json';
// Connect to UP Browser Extension and get the UP address
const provider = new ethers.BrowserProvider(window.lukso);
await provider.send('eth_requestAccounts', []);
const account = await deployer.getAddress();
console.log('Deploying contract from Universal Profile: ', account);
// Create instance of the Universal Profile
const universalProfile = new ethers.Contract(UniversalProfileArtifact, account);
const abiEncoder = new ethers.AbiCoder();
// Encode constructor parameters
const encodedConstructorParams = abiEncoder.encode(
['string', 'string', 'address', 'uint256', 'bool'],
[
'My Custom Token', // token name
'MCT', // token symbol
accounts, // token owner (our UP)
0, // token type = TOKEN
false, // isNonDivisible?
],
);
// Add the constructor parameters to the token bytecode
const tokenBytecodeWithConstructor = ethers.concat([
MyCustomToken.bytecode,
encodedConstructorParams,
]);
// Get the address of the custom token contract that will be created
// https://docs.lukso.tech/contracts/contracts/ERC725/#execute
const customTokenAddress = await universalProfile.execute.staticCall(
1, // Operation type: CREATE
ethers.ZeroAddress, // Target: 0x0 as contract will be initialized
0, // Value is empty
tokenBytecodeWithConstructor, // Payload of the contract
);
// Deploy the contract by the Universal Profile
// https://docs.lukso.tech/contracts/contracts/ERC725/#execute
const tx = await universalProfile.execute(
1, // Operation type: CREATE
ethers.ZeroAddress, // Target: 0x0000...0000 as contract will be initialized
0, // Value is empty
tokenBytecodeWithConstructor, // Payload of the contract
);
// Wait for the transaction to be included in a block
await tx.wait();
console.log('πͺ Token contract deployed by π at: ', customTokenAddress);