Interact With Contracts
This guide might contain outdated information and will be updated soon.
Similar to our previous guide on setting data on the Vault, in this guide, we will be learning how to interact with other contracts through the Vault's execute function.
This guide is also very similar to the guide: Interact with contract using a Universal Profile.
Interaction flow:
Setupâ
To complete this mini-guide, we will need:
- an EOA with some LYX for gas fees and the required permissions for the interaction.
- the
UniversalProfile
andLSP9Vault
contracts ABIs from the@lukso/lsp-smart-contracts
npm package. - the address of the Universal Profile.
- the address of the LSP9 Vault.
- the
targetContract
ABI. - the address of the Target Contract.
The chosen EOA needs to have CALL Permission together with AllowedCalls or SUPER_CALL Pemrission
Make sure you have the following dependencies installed before beginning this tutorial.
- Either
web3.js
orethers.js
@lukso/lsp-smart-contracts
- web3.js
- ethers.js
npm install web3 @lukso/lsp-smart-contracts
npm install ethers @lukso/lsp-smart-contracts
Step 1 - Setup imports & constantsâ
At this step we will import the needed contract ABIs and we will save all the required addresses in constants.
Also we will initialize our EOA for further use.
Save the Target Contract ABI in a separate json and import it in the main file.
You can quickly compile and get a contract's ABI in Remix IDO
- web3.js
- ethers.js
import UniversalProfile from '@lukso/lsp-smart-contracts/artifacts/UniversalProfile.json';
import LSP9Vault from '@lukso/lsp-smart-contracts/artifacts/LSP9Vault.json';
import TargetContractABI from './TargetContractABI.json';
import Web3 from 'web3';
const web3 = new Web3('https://rpc.testnet.lukso.network');
const universalProfileAddress = '0x...';
const vaultAddress = '0x...';
const targetContractAddress = '0x...';
// setup EOA
const privateKey = '0x...'; // your EOA private key (controller address)
const myEOA = web3.eth.accounts.wallet.add(privateKey);
import UniversalProfile from '@lukso/lsp-smart-contracts/artifacts/UniversalProfile.json';
import LSP9Vault from '@lukso/lsp-smart-contracts/artifacts/LSP9Vault.json';
import TargetContractABI from './TargetContractABI.json';
import { ethers } from 'ethers';
const provider = new ethers.JsonRpcProvider(
'https://rpc.testnet.lukso.network',
);
const universalProfileAddress = '0x...';
const vaultAddress = '0x...';
const targetContractAddress = '0x...';
// setup EOA
const privateKey = '0x...'; // your EOA private key (controller address)
const myEOA = new ethers.Wallet(privateKey).connect(provider);
Step 2 - Create the contracts instancesâ
Further we will create instances for our contracts
- Create a Universal Profile contract instance from
universalProfileAddress
. - Create a Target Contract instance from the
targetContractAddress
.
- web3.js
- ethers.js
// Create Universal Profile contract instance
const universalProfile = new web3.eth.Contract(
UniversalProfile.abi,
universalProfileAddress,
);
// Create LSP9 Vault contract instance
const vault = new web3.eth.Contract(LSP9Vault.abi, vaultAddress);
// Create Target Contract contract instance
const targetContract = new web3.eth.Contract(
TargetContractABI,
targetContractAddress,
);
// Create Universal Profile contract instance
const universalProfile = new ethers.Contract(
universalProfileAddress,
UniversalProfile.abi,
);
// Create LSP9 Vault contract instance
const vault = new ethers.Contract(vaultAddress, LSP9Vault.abi);
// Create Target Contract contract instance
const targetContract = new ethers.Contract(
targetContractAddress,
TargetContractABI,
);
Step 3 - Encode the calldatasâ
This is the easy part, we need to create 2 calldatas:
- The first calldata will be executed on the Target Contract.
- The second calldata will be executed on the Vault and will trigger the first calldata.
Encode Target Contract calldataâ
Encoding the calldata that will be be executed on the Target Contract.
- web3.js
- ethers.js
// 1. encode the calldata to be run at the targetContract
// assuming targetContract is a Contract instance
const targetCalldata = targetContract.methods
.myCoolfunction('dummyParameter')
.encodeABI();
// 1. encode the calldata to be run at the targetContract
// assuming targetContract is a Contract instance
const targetCalldata = targetContract.interface.encodeFunctionData(
'myCoolfunction',
['dummyParameter'],
);
Encode Vault calldataâ
Encoding the calldata that will be be executed on the Vault. This calldata will also trigger the calldata that will be executed on the Target Contract.
- web3.js
- ethers.js
// 2. encode the calldata to be run on the Vault,
// passing the calldata to be run at the targetContract as 4th parameter
const vaultCalldata = await vault.methods
.execute(0, targetContract.address, 0, targetCalldata)
.encodeABI();
// 2. encode the calldata to be run on the Vault,
// passing the calldata to be run at the targetContract as 4th parameter
const vaultCalldata = vault.interface.encodeFunctionData('execute', [
0,
targetContract.address,
0,
targetCalldata,
]);
Step 4 - Execute the calldata through the UPâ
The final step is to execute the encoded calldata through the Universal Profile. Since we are calling from a UP's controller address (with proper permissions), the Key Manager will authorize the transaction.
- web3.js
- ethers.js
// Execute the calldata through the Universal Profile
await universalProfile.methods.execute(0, vaultAddress, 0, vaultCalldata).send({
from: myEOA.address,
gasLimit: 600_000,
});
// Execute the calldata through the Universal Profile
await universalProfile
.connect(myEOA)
.execute(0, vaultAddress, 0, vaultCalldata);
Final codeâ
- web3.js
- ethers.js
import UniversalProfile from '@lukso/lsp-smart-contracts/artifacts/UniversalProfile.json';
import LSP9Vault from '@lukso/lsp-smart-contracts/artifacts/LSP9Vault.json';
import TargetContractABI from './TargetContractABI.json';
import Web3 from 'web3';
const web3 = new Web3('https://rpc.testnet.lukso.network');
const universalProfileAddress = '0x...';
const vaultAddress = '0x...';
const targetContractAddress = '0x...';
// setup EOA
const privateKey = '0x...'; // your EOA private key (controller address)
const myEOA = web3.eth.accounts.wallet.add(privateKey);
// Create Universal Profile contract instance
const universalProfile = new web3.eth.Contract(
UniversalProfile.abi,
universalProfileAddress,
);
// Create LSP9 Vault contract instance
const vault = new web3.eth.Contract(LSP9Vault.abi, vaultAddress);
// Create Target Contract contract instance
const targetContract = new web3.eth.Contract(
TargetContractABI,
targetContractAddress,
);
// 1. encode the calldata to be run at the targetContract
// assuming targetContract is a Contract instance
const targetCalldata = targetContract.methods
.myCoolfunction('dummyParameter')
.encodeABI();
// 2. encode the calldata to be run on the Vault,
// passing the calldata to be run at the targetContract as 4th parameter
const vaultCalldata = await vault.methods
.execute(0, targetContract.address, 0, targetCalldata)
.encodeABI();
// Execute the calldata through the Universal Profile
await universalProfile.methods.execute(0, vaultAddress, 0, vaultCalldata).send({
from: myEOA.address,
gasLimit: 600_000,
});
import UniversalProfile from '@lukso/lsp-smart-contracts/artifacts/UniversalProfile.json';
import LSP9Vault from '@lukso/lsp-smart-contracts/artifacts/LSP9Vault.json';
import TargetContractABI from './TargetContractABI.json';
import { ethers } from 'ethers';
const provider = new ethers.providers.JsonRpcProvider(
'https://rpc.testnet.lukso.network',
);
const universalProfileAddress = '0x...';
const vaultAddress = '0x...';
const targetContractAddress = '0x...';
// setup EOA
const privateKey = '0x...'; // your EOA private key (controller address)
const myEOA = new ethers.Wallet(privateKey).connect(provider);
// Create Universal Profile contract instance
const universalProfile = new ethers.Contract(
universalProfileAddress,
UniversalProfile.abi,
);
// Create LSP9 Vault contract instance
const vault = new ethers.Contract(vaultAddress, LSP9Vault.abi);
// Create Target Contract contract instance
const targetContract = new ethers.Contract(
targetContractAddress,
TargetContractABI,
);
// 1. encode the calldata to be run at the targetContract
// assuming targetContract is a Contract instance
const targetCalldata = targetContract.interface.encodeFunctionData(
'myCoolfunction',
['dummyParameter'],
);
// 2. encode the calldata to be run on the Vault,
// passing the calldata to be run at the targetContract as 4th parameter
const vaultCalldata = vault.interface.encodeFunctionData('execute', [
0,
targetContractAddress,
0,
targetCalldata,
]);
// Execute the calldata through the Universal Profile
await universalProfile
.connect(myEOA)
.execute(0, vaultAddress, 0, vaultCalldata);
In the code snippet above, we interacted with myCoolfunction(..)
function on the targetContract contract through the Vault's execute function. The call was encoded and executed through the Universal Profile.