Skip to main content

Deploy Contracts

Deploying a contract from the Universal Profile using the Browser Extension
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 Universal Profile​

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();

Retrieving Contract Deployment Information​

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);
}
}