Skip to content

Commit

Permalink
feat: added get real storage submission example using the contract
Browse files Browse the repository at this point in the history
  • Loading branch information
irediaes committed Jan 16, 2025
1 parent 5b00bbb commit fabc38b
Show file tree
Hide file tree
Showing 3 changed files with 239 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -446,8 +446,6 @@ class PeaqGetRealCampaignClass {

}



// Main function to create DID
const createDid = async () => {
const campaignClass = new PeaqGetRealCampaignClass();
Expand Down
232 changes: 232 additions & 0 deletions examples/js/getRealCampaignExamples/PeaqStorageGetRealCampaignClass.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
import { AbiItem } from 'web3-utils';
import { AbiCoder, ethers } from 'ethers';
import axios from 'axios';

// Import the contract ABI
import {abi} from '../MachineStationFactoryABI.json';

// peaq RPC URL: https://peaq.api.onfinality.io/public
const rpcURL = "https://erpc-async.agung.peaq.network"; // replace with peaq RPC URL during mainnet deployment.
const chainID = 3338;

// Contract details
// Replace with the your dedicated machine station factory contract address during deployment
const MachineStationFactoryContractAddress: string = '0x31B80DbA6806E0335Bfac6D12A5A820C32D73d68';
const contract = new ethers.ContractFactory(abi as AbiItem[], MachineStationFactoryContractAddress);
const abiCoder = new AbiCoder();

// Wallet details
const ownerPrivateKey: string | undefined = process.env.CONTRACT_OWNER_PRIVATE_KEY??""; // Replace with owner wallet's private key

const provider = new ethers.JsonRpcProvider(rpcURL);
const ownerAccount = new ethers.Wallet(ownerPrivateKey, provider);

const PEAQ_SERVICE_URL = "<PEAQ_SERVICE_URL>"; // URL to the peaq campaign service
// dev URL: https://lift-off-campaign-service-jx-devbr.jx.peaq.network

const API_KEY = "<API_KEY>"; // peaq campaign service APIKEY value added to the header of all requests
// dev APIKEY: aa69cb8e92b2e27eb26996fc9b02f6df24
// production APIKEY will be sent to you after deployment

const PROJECT_API_KEY = "<API_KEY>"; // peaq campaign service unique APIKEY value for specific project added to the header of all requests
// dev P-APIKEY: all_0821fcaa69
// your production P-APIKEY will be sent to you after deployment


class PeaqGetRealCampaignClass {


async submitGetRealStorageTx() {
try {
const nonce = this.getRandomNonce();
const target = '0x0000000000000000000000000000000000000801';

const addItemFunctionSignature = "addItem(bytes,bytes)";
const addItemFunctionSelector = ethers.keccak256(ethers.toUtf8Bytes(addItemFunctionSignature)).substring(0, 10);

let now = new Date().getTime();

// Send data to peaq data storage service
// replace <YOUR_CUSTOM_TASK_TAG> with your unique task identity tag used to track your tasks on-chain.
// all item types are required to be constructed in this format
// [<YOUR_CUSTOM_TASK_TAG>] + [-] + [a-zA-Z0-9-_]
// we use the dash [-] to split the item type when the event parser receives the chain events
// ItemType has to be unique on every requests
const itemType = "<YOUR_CUSTOM_TASK_TAG>-"+now; // e.g "GET-REAL-CAMPAIGN-ITEM-TYPE-001", "GET-REAL-CAMPAIGN-ITEM-TYPE-002"
const postData = {
item_type: itemType,
email: '[email protected]', // replace this email with the user email address
tag: "<YOUR_CUSTOM_TASK_TAG>", // replace with your unique custom task tag
tags: ["<YOUR-CUSTOM-TASK-TAG>", "<20_YOUR-CUSTOM-TASK-TAG>", "<30_YOUR-CUSTOM-TASK-TAG>"] // replace with your unique custom task tags
};

// register the itemType and tag or tags
await this.registerItemTypeAndTags(postData)

// encode the item storage data for submission to peaq network
const itemTypeHex = ethers.hexlify(ethers.toUtf8Bytes(itemType));
const item = "TASK-COMPLETED"
const itemHex = ethers.hexlify(ethers.toUtf8Bytes(item));

const params = abiCoder.encode(
["bytes", "bytes"],
[itemTypeHex, itemHex]
);

const calldata = params.replace("0x", addItemFunctionSelector);
const ownerSignature = await this.ownerSignTypedDataExecuteTransaction(target, calldata, nonce)

await this.executeTransaction(target, calldata, nonce, ownerSignature,);
} catch (error) {
console.error('Error:', error);
}
}

async executeTransaction(
target: string,
data: string,
nonce: BigInt,
signature: string,
): Promise<void> {
try {
// Encode the method call data
const methodData = contract.interface.encodeFunctionData(
"executeTransaction",
[target, data, nonce, signature]
);

// Send the transaction and get the receipt
const txResponse = await this.sendTransaction(methodData);

let receipt = await txResponse.wait().finally();

console.log('Normal Tx executed:', receipt?.hash);

} catch (error: any) {
console.error("Transaction failed. Error:", error);

// Check if the error is a revert error with data
if (error.data) {
try {
// Decode the revert error using the contract's ABI
const iface = new ethers.Interface(contract.interface.fragments);
const decodedError = iface.parseError(error.data);

console.log("Decoded Error:", decodedError);

// Extract error name and arguments
// const { name, args } = decodedError;
// console.log("Error Name:", name);
// console.log("Arguments:", args);

// if (name === "InvalidSignature") {
// console.error("InvalidSignature Error Details:");
// console.error("structHash:", args.structHash);
// console.error("nonce:", args.nonce.toString());
// }
} catch (decodeError) {
console.error("Failed to decode error data:", decodeError);
}
} else {
console.error("Transaction failed without revert data:", error);
}
}
}

async ownerSignTypedDataExecuteTransaction(
target: string,
data: string,
nonce: BigInt,
): Promise<string> {
// Step 1: Define the EIP-712 Domain
const domain = {
name: "MachineStationFactory",
version: "1",
chainId: chainID,
verifyingContract: MachineStationFactoryContractAddress,
};

const types = {
ExecuteTransaction: [
{ name: "target", type: "address" },
{ name: "data", type: "bytes" },
{ name: "nonce", type: "uint256" },
],
};

const message = {
target: target,
data: data,
nonce: nonce,
};

const signature = await ownerAccount.signTypedData(domain, types, message);

return signature;
}

// Helper function to sign and send transactions
async sendTransaction(
methodData: string
): Promise<ethers.TransactionResponse> {


const tx = {
to: MachineStationFactoryContractAddress,
data: methodData,
};

return await ownerAccount.sendTransaction(tx);
}

getRandomNonce(): BigInt {
const now = BigInt(Date.now());
const randomPart = BigInt(Math.floor(Math.random() * 1e18));
return now * randomPart;
}

// Function to register your item type and tags on campaign verification service
async registerItemTypeAndTags(data:any) {
try {


const response = await axios.post(`${PEAQ_SERVICE_URL}/v1/data/store`, data, {
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'APIKEY': API_KEY,
'P-APIKEY': PROJECT_API_KEY
}
})
.then((response:any) => {
return response.data;
})
.catch((err:any) => {
console.error(err)
throw err;
});

// Note: You may need to adjust the response handling based on the service's response structure
return response.data;

} catch (error) {
console.error("Error registering itemType and tags on campaign verification service", error);
throw error;
}
};

}

// Main function to submit get real storage tx
const submitGetRealStorageTx = async () => {
const campaignClass = new PeaqGetRealCampaignClass();

try {
await campaignClass.submitGetRealStorageTx();
} catch (error) {
console.error(" storage submission failed: Error:", error);
}
};
submitGetRealStorageTx().catch(console.error);


14 changes: 7 additions & 7 deletions examples/js/usingWeb3/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { Keyring } from '@polkadot/keyring';
import {abi} from '../MachineStationFactoryABI.json';
import { AbiCoder, ethers } from 'ethers';

let golabalMachineAddress = "0x8D5fd26b338d9f40b48eD21bBd517E0944a12D48"; // to be replaced after submitDeployMachineSmartAccountTx is triggered
let golabalMachineAddress2 = "0x17420815062B03917bd89430a7eea8bFC84AC1B8"; // to be replaced after submitDeployMachineSmartAccountTx is triggered
let globalMachineAddress = "0x8D5fd26b338d9f40b48eD21bBd517E0944a12D48"; // to be replaced after submitDeployMachineSmartAccountTx is triggered
let globalMachineAddress2 = "0x17420815062B03917bd89430a7eea8bFC84AC1B8"; // to be replaced after submitDeployMachineSmartAccountTx is triggered

// Web3 setup
// for agung: https://erpc-async.agung.peaq.network
Expand Down Expand Up @@ -45,11 +45,11 @@ class MachineStationFactoryExample {
const deploySignature = await this.ownerSignTypedDataDeployMachineSmartAccount(machineOwner, nonce)

// call the deploy smart account tx and update the global machine address var
golabalMachineAddress = await this.deployMachineSmartAccount(machineOwner, nonce, deploySignature);
globalMachineAddress = await this.deployMachineSmartAccount(machineOwner, nonce, deploySignature);
}

async submitMachineTransferBalanceTx() {
let machineAddress = golabalMachineAddress;
let machineAddress = globalMachineAddress;
const recipientAddress = machineOwnerAccount.address;
const nonce = this.getRandomNonce();

Expand Down Expand Up @@ -95,7 +95,7 @@ class MachineStationFactoryExample {
async submitMachineStorageTx() {
try {
const machineOwner = machineOwnerAccount.address;
let machineAddress = golabalMachineAddress;
let machineAddress = globalMachineAddress;
const nonce = this.getRandomNonce();
const target = '0x0000000000000000000000000000000000000801';

Expand Down Expand Up @@ -138,7 +138,7 @@ class MachineStationFactoryExample {

const machineNonces: BigInt[] = [];
const machineOwnerSignatures: string[] = [];
const machineAddresses: string[] = [golabalMachineAddress, golabalMachineAddress2];
const machineAddresses: string[] = [globalMachineAddress, globalMachineAddress2];
const targets = ['0x0000000000000000000000000000000000000801','0x0000000000000000000000000000000000000801'];
const calldata: string[] = [];

Expand Down Expand Up @@ -178,7 +178,7 @@ class MachineStationFactoryExample {
try {
const nonce = this.getRandomNonce(); // Example nonce
const target = "0x0000000000000000000000000000000000000800"; // target contract address - DID contract address
const machineAddress = golabalMachineAddress;
const machineAddress = globalMachineAddress;
const abiCoder = new AbiCoder();

const addAttributeFunctionSignature =
Expand Down

0 comments on commit fabc38b

Please sign in to comment.