blockchain-utils-service 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +29 -0
- package/src/contractSubscribeWebhook.js +88 -0
- package/src/deploy.js +73 -0
- package/src/getBalance.js +34 -0
- package/src/getBlockNumberReached.js +34 -0
- package/src/getCreatorTxHash.js +25 -0
- package/src/getEvents.js +67 -0
- package/src/getTokenLatestPrice.js +33 -0
- package/src/getTokenPrice.js +46 -0
- package/src/getTransactionHash.js +45 -0
- package/src/index.js +12 -0
- package/src/readTransaction.js +36 -0
- package/src/transfer.js +46 -0
- package/src/writeTransaction.js +55 -0
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "blockchain-utils-service",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A lightweight service for deploying contracts, writing transactions, reading events, and checking block numbers.",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "echo 'No build needed'",
|
|
9
|
+
"test": "echo 'No test yet'"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"thirdweb",
|
|
13
|
+
"blockchain",
|
|
14
|
+
"smart-contracts",
|
|
15
|
+
"events",
|
|
16
|
+
"deploy",
|
|
17
|
+
"transactions"
|
|
18
|
+
],
|
|
19
|
+
"author": "Mai Labs",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@thirdweb-dev/sdk": "^4.0.99",
|
|
23
|
+
"axios": "^1.6.7",
|
|
24
|
+
"dotenv": "^16.3.0",
|
|
25
|
+
"ethers": "^5.8.0",
|
|
26
|
+
"thirdweb": "^5.104.1",
|
|
27
|
+
"undici": "^7.8.0"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// src/contractSubscribeWebhook.js
|
|
2
|
+
|
|
3
|
+
// #pending
|
|
4
|
+
// Add check webhook URL function!
|
|
5
|
+
/**
|
|
6
|
+
* Subscribes to smart contract events and sets up a webhook for notifications.
|
|
7
|
+
*
|
|
8
|
+
* @param {Object} config - Subscription configuration.
|
|
9
|
+
* @param {string} config.name - A name for the webhook subscription.
|
|
10
|
+
* @param {number|string} config.chainId - The chain ID of the blockchain network (e.g., 137 for Polygon).
|
|
11
|
+
* @param {string} config.contractAddress - The address of the deployed smart contract.
|
|
12
|
+
* @param {string} config.thirdwebInsightUrl - The base URL for the Thirdweb Insight API.
|
|
13
|
+
* @param {string[]} config.eventsSignature - An array of event signatures to listen for (e.g., ["Transfer(address,address,uint256)"]).
|
|
14
|
+
* @param {string} config.secretKey - Secret key for authenticating with the Thirdweb Insight.
|
|
15
|
+
* @param {string} config.abi - The contract's ABI (Application Binary Interface), required to decode events.
|
|
16
|
+
* @param {string} config.webhookUrl - The URL where event notifications will be sent.
|
|
17
|
+
* @param {string} config.environment - The Development Environment.
|
|
18
|
+
* @param {string} config.subscriptionType - The type of webhook e.g. "activityType" or "eventType"
|
|
19
|
+
* @returns {Promise<Object>} - Result of the webhook subscription, typically includes subscription ID or status.
|
|
20
|
+
*/
|
|
21
|
+
export async function contractSubscribeWebhook({
|
|
22
|
+
chainId,
|
|
23
|
+
contractAddress,
|
|
24
|
+
inhouseStreamUrl,
|
|
25
|
+
eventsSignature,
|
|
26
|
+
webhookUrl,
|
|
27
|
+
environment,
|
|
28
|
+
subscriptionType
|
|
29
|
+
}) {
|
|
30
|
+
try {
|
|
31
|
+
const uri = `${inhouseStreamUrl}/streams/evm`;
|
|
32
|
+
const body = {
|
|
33
|
+
chainId,
|
|
34
|
+
contractAddress,
|
|
35
|
+
eventsSignature,
|
|
36
|
+
webhookUrl,
|
|
37
|
+
environment,
|
|
38
|
+
subscriptionType
|
|
39
|
+
};
|
|
40
|
+
const headers = {
|
|
41
|
+
"Content-Type": "application/json",
|
|
42
|
+
};
|
|
43
|
+
const response = await fetch(uri, {
|
|
44
|
+
method: "POST",
|
|
45
|
+
headers: headers,
|
|
46
|
+
body: JSON.stringify(body),
|
|
47
|
+
});
|
|
48
|
+
const result = await response.json();
|
|
49
|
+
if (!response.ok) {
|
|
50
|
+
throw new Error(`Failed - Webhook Creation for contract: ${JSON.stringify(result)}`);
|
|
51
|
+
}
|
|
52
|
+
return { isSuccess: true, data: { webhookResponse: result } };
|
|
53
|
+
} catch (error) {
|
|
54
|
+
console.log(error)
|
|
55
|
+
return { isSuccess: false, data: `Webhook creation failed: ${JSON.stringify(error.message)}` };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// #pending
|
|
60
|
+
/**
|
|
61
|
+
* Updates a Thirdweb Insight Webhook status.
|
|
62
|
+
*
|
|
63
|
+
* @param {string} webhookId – The ID of the webhook to be updated.
|
|
64
|
+
* @param {boolean} disabledFlag – The new status for the webhook, either "true" (to disable) or "false" (to enable).
|
|
65
|
+
* @param {string} inhouseStreamUrl - The base URL for the Thirdweb Insight API.
|
|
66
|
+
* @returns {Promise<{isSuccess: boolean, data: any}>}
|
|
67
|
+
*/
|
|
68
|
+
export async function updateWebhookStatus({ streamId, status, inhouseStreamUrl }) {
|
|
69
|
+
if (!streamId || !inhouseStreamUrl || !status) {
|
|
70
|
+
return { isSuccess: false, data: "Missing required parameters" };
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
const uri = `${inhouseStreamUrl}/streams/evm/${streamId}/status`;
|
|
74
|
+
const body = { status };
|
|
75
|
+
const headers = {
|
|
76
|
+
"Content-Type": "application/json"
|
|
77
|
+
};
|
|
78
|
+
const response = await fetch(uri, { method: "POST", headers, body: JSON.stringify(body) });
|
|
79
|
+
if (!response.ok) {
|
|
80
|
+
const errorData = await response.json();
|
|
81
|
+
throw new Error(`UpdateWebhookStatus Failed: ${errorData}`);
|
|
82
|
+
}
|
|
83
|
+
const result = await response.json();
|
|
84
|
+
return { isSuccess: true, data: result };
|
|
85
|
+
} catch (error) {
|
|
86
|
+
return { isSuccess: false, data: JSON.stringify(error) };
|
|
87
|
+
}
|
|
88
|
+
}
|
package/src/deploy.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// src/deployNew.js
|
|
2
|
+
|
|
3
|
+
import { createThirdwebClient } from "thirdweb";
|
|
4
|
+
import { deployContract } from "thirdweb/deploys";
|
|
5
|
+
import { engineAccount } from "thirdweb/wallets/engine";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Deploys a new smart contract using the provided configuration.
|
|
9
|
+
*
|
|
10
|
+
* @param {Object} config - Deployment configuration.
|
|
11
|
+
* @param {string} config.secretKey - The Thirdweb Engine API secret key.
|
|
12
|
+
* @param {string} config.abi - The ABI (Application Binary Interface) of the contract.
|
|
13
|
+
* @param {string} config.bytecode - The compiled bytecode of the contract.
|
|
14
|
+
* @param {object} config.constructorArgs - Constructor arguments required by the contract (e.g., name, symbol, owner).
|
|
15
|
+
* @param {object} config.chain - The Thirdweb chain configuration object (e.g., `80002`).
|
|
16
|
+
* @param {string} config.engineUrl - The thirdweb engine url
|
|
17
|
+
* @param {string} config.backendWalletAddress - The deployer address
|
|
18
|
+
* @param {string} config.authorizationToken - The thirdweb auth key
|
|
19
|
+
* @returns {Promise<string>} - The address of the deployed contract.
|
|
20
|
+
*/
|
|
21
|
+
export async function deployNewContract({
|
|
22
|
+
secretKey,
|
|
23
|
+
abi,
|
|
24
|
+
bytecode,
|
|
25
|
+
constructorArgs,
|
|
26
|
+
chain,
|
|
27
|
+
engineUrl,
|
|
28
|
+
backendWalletAddress,
|
|
29
|
+
authorizationToken,
|
|
30
|
+
}) {
|
|
31
|
+
try {
|
|
32
|
+
if (!secretKey || !abi || !bytecode || !chain || !engineUrl || !backendWalletAddress || !authorizationToken) {
|
|
33
|
+
throw new Error("Missing required parameters for deployNewContract");
|
|
34
|
+
}
|
|
35
|
+
const client = createThirdwebClient({ secretKey });
|
|
36
|
+
|
|
37
|
+
const engineAcc = engineAccount({
|
|
38
|
+
engineUrl: engineUrl,
|
|
39
|
+
authToken: authorizationToken,
|
|
40
|
+
walletAddress: backendWalletAddress,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const constructorAbi = abi.find(item => item.type === "constructor");
|
|
44
|
+
if (!constructorAbi) {
|
|
45
|
+
const deployedAddress = await deployContract({
|
|
46
|
+
client,
|
|
47
|
+
account: engineAcc,
|
|
48
|
+
chain,
|
|
49
|
+
abi,
|
|
50
|
+
bytecode,
|
|
51
|
+
constructorParams: {}, // Empty if no constructor
|
|
52
|
+
});
|
|
53
|
+
return { deployedAddress };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const constructorParams = {};
|
|
57
|
+
constructorAbi.inputs.forEach((input, index) => {
|
|
58
|
+
constructorParams[input.name] = constructorArgs[index];
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const deployedAddress = await deployContract({
|
|
62
|
+
client,
|
|
63
|
+
account: engineAcc,
|
|
64
|
+
chain,
|
|
65
|
+
abi,
|
|
66
|
+
bytecode,
|
|
67
|
+
constructorParams,
|
|
68
|
+
});
|
|
69
|
+
return { deployedAddress };
|
|
70
|
+
} catch (err) {
|
|
71
|
+
throw new Error(`Failed to deploy contract: ${err.message}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Function to check the balance of a blockchain address
|
|
2
|
+
/**
|
|
3
|
+
* Check the balance of a blockchain address through a Thirdweb Engine backend.
|
|
4
|
+
*
|
|
5
|
+
* @param {Object} config
|
|
6
|
+
* @param {string} config.address - Address to check balance for
|
|
7
|
+
* @param {number|string} config.chainId - Chain ID (eg. 80002 for Amoy)
|
|
8
|
+
* @param {string} config.engineUrl - Base URL of your Engine backend (eg. 'https://your-engine.com')
|
|
9
|
+
* @param {string} config.authorizationToken - Bearer token for Engine access
|
|
10
|
+
* @returns {Promise<object>} - Result containing balance in Wei and Eth
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// #Review Pending
|
|
14
|
+
export const checkBalance = async ({ address, chainId, engineUrl, authorizationToken }) => {
|
|
15
|
+
try {
|
|
16
|
+
const url = `${engineUrl}/backend-wallet/${chainId}/${address}/get-balance`
|
|
17
|
+
const headers = {
|
|
18
|
+
"Content-Type": "application/json",
|
|
19
|
+
Authorization: `Bearer ${authorizationToken}`,
|
|
20
|
+
};
|
|
21
|
+
const response = await fetch(url, { method: "GET", headers });
|
|
22
|
+
if (!response.ok) {
|
|
23
|
+
const errorData = await response.json();
|
|
24
|
+
throw new Error(`${JSON.stringify(errorData)}`);
|
|
25
|
+
}
|
|
26
|
+
const { result } = await response.json();
|
|
27
|
+
const balanceInWei = result.value.toString();
|
|
28
|
+
const balanceInEth = result.displayValue;
|
|
29
|
+
|
|
30
|
+
return { balanceInWei, balanceInEth }
|
|
31
|
+
} catch (error) {
|
|
32
|
+
throw new Error(`failed in checkBalance : ${error}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// src/getBlockNumberReached.js
|
|
2
|
+
|
|
3
|
+
import { getRpcClient, eth_blockNumber } from 'thirdweb/rpc';
|
|
4
|
+
import { createThirdwebClient } from 'thirdweb';
|
|
5
|
+
import { defineChain } from 'thirdweb/chains';
|
|
6
|
+
/**
|
|
7
|
+
* Checks whether the current block number on the blockchain has reached or surpassed a specified target block number.
|
|
8
|
+
*
|
|
9
|
+
* @param {Object} config - Configuration for the block check.
|
|
10
|
+
* @param {string} config.chainId - The ChainId
|
|
11
|
+
* @param {string} config.clientId - The Thirdweb API clientId used for authentication (if required).
|
|
12
|
+
* @param {number} config.targetBlockNumber - The target block number to compare against.
|
|
13
|
+
* @returns {Promise<boolean>} - Returns `true` if the current block number is greater than or equal to the target, otherwise `false`.
|
|
14
|
+
*/
|
|
15
|
+
export async function hasBlockNumberReached({
|
|
16
|
+
chainId,
|
|
17
|
+
clientId,
|
|
18
|
+
targetBlockNumber,
|
|
19
|
+
}) {
|
|
20
|
+
try {
|
|
21
|
+
if (!chainId || !clientId || targetBlockNumber === undefined) {
|
|
22
|
+
throw new Error("Missing required parameters for hasBlockNumberReached");
|
|
23
|
+
}
|
|
24
|
+
const chain = defineChain(Number(chainId));
|
|
25
|
+
const client = createThirdwebClient({
|
|
26
|
+
clientId: clientId,
|
|
27
|
+
});
|
|
28
|
+
const rpcRequest = getRpcClient({ client, chain });
|
|
29
|
+
const currentBlock = await eth_blockNumber(rpcRequest);
|
|
30
|
+
return currentBlock >= targetBlockNumber;
|
|
31
|
+
} catch (err) {
|
|
32
|
+
throw new Error(`Failed to check block number reached: ${err.message}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finds the deployment transaction hash for a given smart contract address on a specific blockchain.
|
|
3
|
+
*
|
|
4
|
+
* @param {string} contractAddress - The address of the deployed smart contract.
|
|
5
|
+
* @param {number|string} chainId - The chain ID of the blockchain network (e.g., 1 for Ethereum Mainnet, 137 for Polygon).
|
|
6
|
+
* @param {string} etherscanAPI - The API key for Etherscan or an Etherscan-compatible explorer used to query transactions.
|
|
7
|
+
* @returns {Promise<string|null>} - The transaction hash of the contract deployment, or `null` if not found.
|
|
8
|
+
*/
|
|
9
|
+
export async function findDeploymentTxHash(contractAddress, chainId, etherscanAPI) {
|
|
10
|
+
try {
|
|
11
|
+
const apiUrl = `https://api.etherscan.io/v2/api?chainid=${chainId}&module=contract&action=getcontractcreation&contractaddresses=${contractAddress}&apikey=${etherscanAPI}`;
|
|
12
|
+
const response = await fetch(apiUrl);
|
|
13
|
+
if (!response.ok) {
|
|
14
|
+
const errorData = await response.json();
|
|
15
|
+
throw new Error(`Failed to fetch contract creation info: ${JSON.stringify(errorData)}`);
|
|
16
|
+
}
|
|
17
|
+
const { result, status } = await response.json();
|
|
18
|
+
if (status != '1' || result.length === 0) {
|
|
19
|
+
throw new Error("Failed to retrieve hash!");
|
|
20
|
+
}
|
|
21
|
+
return { isSuccess: true, data: { txHash: result[0].txHash, blockNumber: result[0].blockNumber } };
|
|
22
|
+
} catch (err) {
|
|
23
|
+
return { isSuccess: false, data: err.message }
|
|
24
|
+
}
|
|
25
|
+
}
|
package/src/getEvents.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// src/getEvents.js
|
|
2
|
+
import axios from "axios";
|
|
3
|
+
import https from "https";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Fetch events from a Thirdweb Engine backend between specific blocks.
|
|
7
|
+
*
|
|
8
|
+
* @param {Object} config
|
|
9
|
+
* @param {string} config.thirdwebInsightUrl - Thirdweb Insight base URL (eg. https://insight.thirdweb.com/v1)
|
|
10
|
+
* @param {number|string} config.chainId - Chain ID (eg. 80002 for Amoy)
|
|
11
|
+
* @param {string} config.contractAddress - Smart contract address
|
|
12
|
+
* @param {string} config.eventName - Name of the event to fetch
|
|
13
|
+
* @param {number|string} config.fromBlock - Starting block number
|
|
14
|
+
* @param {number|string} [config.toBlock] - Ending block number (default: 'latest')
|
|
15
|
+
* @param {object} [config.filters] - Optional filters object
|
|
16
|
+
* @param {string} config.clientId - Thirdweb Insight Client Id
|
|
17
|
+
* @param {number} config.limit - Number of events to fetch per page
|
|
18
|
+
* @param {number} config.page - Page number for pagination
|
|
19
|
+
* @returns {Promise<object>} - Events response
|
|
20
|
+
*/
|
|
21
|
+
export async function fetchContractEvents({
|
|
22
|
+
thirdwebInsightUrl,
|
|
23
|
+
chainId,
|
|
24
|
+
contractAddress,
|
|
25
|
+
eventSignature,
|
|
26
|
+
fromBlock,
|
|
27
|
+
toBlock = "latest",
|
|
28
|
+
filters = {},
|
|
29
|
+
clientId,
|
|
30
|
+
limit,
|
|
31
|
+
page,
|
|
32
|
+
}) {
|
|
33
|
+
if (
|
|
34
|
+
thirdwebInsightUrl == null ||
|
|
35
|
+
chainId == null ||
|
|
36
|
+
contractAddress == null ||
|
|
37
|
+
eventSignature == null ||
|
|
38
|
+
fromBlock == null ||
|
|
39
|
+
toBlock == null ||
|
|
40
|
+
clientId == null ||
|
|
41
|
+
limit == null ||
|
|
42
|
+
page == null
|
|
43
|
+
) {
|
|
44
|
+
throw new Error("Missing required parameters for fetchContractEvents");
|
|
45
|
+
}
|
|
46
|
+
const agent = new https.Agent({
|
|
47
|
+
rejectUnauthorized: false,
|
|
48
|
+
});
|
|
49
|
+
const url = `${thirdwebInsightUrl}/events/${contractAddress}/${eventSignature}`;
|
|
50
|
+
const params = {
|
|
51
|
+
chain_id: chainId,
|
|
52
|
+
filter_block_number_gte: fromBlock,
|
|
53
|
+
filter_block_number_lte: toBlock,
|
|
54
|
+
sort_order: "asc",
|
|
55
|
+
limit: limit,
|
|
56
|
+
page: page,
|
|
57
|
+
};
|
|
58
|
+
const headers = {
|
|
59
|
+
"x-client-id": clientId,
|
|
60
|
+
};
|
|
61
|
+
try {
|
|
62
|
+
const response = await axios.get(url, { params, headers });
|
|
63
|
+
return response.data;
|
|
64
|
+
} catch (error) {
|
|
65
|
+
throw new Error(`Failed to fetch events: ${error.response?.data?.error || error.message}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finds the deployment transaction hash for a given smart contract address on a specific blockchain.
|
|
3
|
+
*
|
|
4
|
+
* @param {string} contractAddress - The address of the deployed smart contract.
|
|
5
|
+
* @param {number|string} chainId - The chain ID of the blockchain network (e.g., 1 for Ethereum Mainnet, 137 for Polygon).
|
|
6
|
+
* @param {string} apiKey - The API key Moralis
|
|
7
|
+
* @returns {Promise<object|null>} - Returns { tokenName, tokenSymbol, usdPrice }.
|
|
8
|
+
*/
|
|
9
|
+
export async function fetchTokenLatestPrice({ contractAddress, chainId, apiKey }) {
|
|
10
|
+
try {
|
|
11
|
+
if (!contractAddress || !chainId || !apiKey) {
|
|
12
|
+
throw new Error("Missing required parameters for fetchTokenLatestPrice");
|
|
13
|
+
}
|
|
14
|
+
const hexChainId = '0x' + chainId.toString(16);
|
|
15
|
+
const headers = {
|
|
16
|
+
"Content-Type": "application/json",
|
|
17
|
+
"x-api-key": apiKey,
|
|
18
|
+
};
|
|
19
|
+
const apiUrl = `https://deep-index.moralis.io/api/v2.2/erc20/${contractAddress}/price?chain=${hexChainId}&include=percent_change`;
|
|
20
|
+
const response = await fetch(apiUrl, {
|
|
21
|
+
method: 'GET',
|
|
22
|
+
headers: headers
|
|
23
|
+
});
|
|
24
|
+
if (!response.ok) {
|
|
25
|
+
const errorData = await response.json();
|
|
26
|
+
throw new Error(`Fetching price failed: ${JSON.stringify(errorData)}`);
|
|
27
|
+
}
|
|
28
|
+
const { usdPrice, tokenName, tokenSymbol } = await response.json();
|
|
29
|
+
return { tokenName, tokenSymbol, usdPrice };
|
|
30
|
+
} catch (err) {
|
|
31
|
+
throw new Error(`Failed to fetch latest price for ${contractAddress} at ${chainId} : ${err.message}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// src/getTokenPrice.js
|
|
2
|
+
|
|
3
|
+
import { createThirdwebClient, getContract, readContract } from 'thirdweb';
|
|
4
|
+
import { defineChain } from 'thirdweb/chains';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Fetches the latest token price from a Chainlink oracle on the specified blockchain.
|
|
8
|
+
*
|
|
9
|
+
* @param {Object} params - Parameters for retrieving the token price.
|
|
10
|
+
* @param {string} params.chainlinkOracleAddress - The address of the Chainlink price feed oracle contract.
|
|
11
|
+
* @param {number} params.chainlinkOracleChainId - The chain ID of the blockchain where the oracle contract is deployed.
|
|
12
|
+
* @param {string} params.clientId - The Thirdweb client ID used for authenticated RPC requests.
|
|
13
|
+
* @returns {Promise<string>} - Returns the latest token price as a string.
|
|
14
|
+
*/
|
|
15
|
+
export async function getTokenPrice({
|
|
16
|
+
chainlinkOracleAddress,
|
|
17
|
+
chainlinkOracleChainId,
|
|
18
|
+
clientId
|
|
19
|
+
}) {
|
|
20
|
+
try {
|
|
21
|
+
if (!(chainlinkOracleAddress && chainlinkOracleChainId)) {
|
|
22
|
+
throw new Error("Missing required parameters for getTokenPrice");
|
|
23
|
+
}
|
|
24
|
+
const client = createThirdwebClient({ clientId: clientId });
|
|
25
|
+
const chain = defineChain(Number(chainlinkOracleChainId));
|
|
26
|
+
const contract = getContract({
|
|
27
|
+
client,
|
|
28
|
+
chain: chain,
|
|
29
|
+
address: chainlinkOracleAddress,
|
|
30
|
+
});
|
|
31
|
+
const decimals = Number(await readContract({
|
|
32
|
+
contract,
|
|
33
|
+
method: "function decimals() view returns (uint8)",
|
|
34
|
+
params: [],
|
|
35
|
+
}));
|
|
36
|
+
const latestPrice = Number(await readContract({
|
|
37
|
+
contract,
|
|
38
|
+
method: "function latestAnswer() view returns (int256)",
|
|
39
|
+
params: [],
|
|
40
|
+
}));
|
|
41
|
+
const priceInUsd = latestPrice / Math.pow(10, decimals);
|
|
42
|
+
return priceInUsd;
|
|
43
|
+
} catch (err) {
|
|
44
|
+
throw new Error(`Failed getting token price ${err.message}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retrieve the transaction hash from Thirdweb Engine using the queueId.
|
|
3
|
+
* Retries until the transactionHash is available or max attempts are reached.
|
|
4
|
+
*
|
|
5
|
+
* @param {string} queueId - The queue ID returned after submitting the transaction
|
|
6
|
+
* @param {string} authorizationToken - Bearer token to authorize the request
|
|
7
|
+
* @param {string} engineUrl - Base URL of the Thirdweb Engine
|
|
8
|
+
* @param {number} [maxAttempts=10] - Maximum number of retry attempts
|
|
9
|
+
* @param {number} [delayMs=3000] - Delay in milliseconds between attempts
|
|
10
|
+
* @returns {Promise<Object>} - TransactionHash if available
|
|
11
|
+
*/
|
|
12
|
+
export async function getTransactionHash({ queueId, engineUrl, authorizationToken, maxAttempts = 10, delayMs = 3000 }) {
|
|
13
|
+
if (!queueId || !authorizationToken || !engineUrl) {
|
|
14
|
+
throw new Error("Missing required parameters for getTransactionHash");
|
|
15
|
+
}
|
|
16
|
+
const writeUrl = `${engineUrl}/transaction/status/${queueId}`;
|
|
17
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
18
|
+
try {
|
|
19
|
+
const response = await fetch(writeUrl, {
|
|
20
|
+
method: "GET",
|
|
21
|
+
headers: {
|
|
22
|
+
accept: "application/json",
|
|
23
|
+
Authorization: `Bearer ${authorizationToken}`,
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
if (!response.ok) {
|
|
27
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
28
|
+
}
|
|
29
|
+
const result = await response.json();
|
|
30
|
+
if (result.result?.transactionHash) {
|
|
31
|
+
return result.result.transactionHash;
|
|
32
|
+
}
|
|
33
|
+
if (attempt < maxAttempts) {
|
|
34
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
35
|
+
} else {
|
|
36
|
+
throw new Error("Transaction hash not available after maximum retries.");
|
|
37
|
+
}
|
|
38
|
+
} catch (error) {
|
|
39
|
+
if (attempt === maxAttempts) {
|
|
40
|
+
throw new Error("Failed to fetch transaction hash after multiple attempts.");
|
|
41
|
+
}
|
|
42
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { deployNewContract } from "./deploy.js";
|
|
2
|
+
export { writeTransaction } from "./writeTransaction.js";
|
|
3
|
+
export { fetchContractEvents } from "./getEvents.js";
|
|
4
|
+
export { hasBlockNumberReached } from "./getBlockNumberReached.js";
|
|
5
|
+
export { contractSubscribeWebhook, updateWebhookStatus } from "./contractSubscribeWebhook.js";
|
|
6
|
+
export { getTransactionHash } from "./getTransactionHash.js";
|
|
7
|
+
export { fetchTokenLatestPrice } from "./getTokenLatestPrice.js";
|
|
8
|
+
export { getTokenPrice } from "./getTokenPrice.js";
|
|
9
|
+
export { findDeploymentTxHash } from "./getCreatorTxHash.js";
|
|
10
|
+
export { readTransaction } from "./readTransaction.js";
|
|
11
|
+
export { checkBalance } from "./getBalance.js";
|
|
12
|
+
export { transferFunds } from "./transfer.js";
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// src/readTransaction.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Reads data from a smart contract using Thirdweb SDK.
|
|
5
|
+
*
|
|
6
|
+
* @param {Object} config
|
|
7
|
+
* @param {string} config.engineUrl - Base URL of your Engine backend (eg. 'https://your-engine.com')
|
|
8
|
+
* @param {number|string} config.chainId - Chain ID (eg. 80002 for Amoy)
|
|
9
|
+
* @param {string} config.contractAddress - Smart contract address
|
|
10
|
+
* @param {string} config.functionName - Full function signature (eg. 'function mint(address,uint256)')
|
|
11
|
+
* @param {string} config.args - Function arguments
|
|
12
|
+
* @param {string} config.authorizationToken - Bearer token for Engine access
|
|
13
|
+
* @returns {Promise<object>} - Result of the read transaction
|
|
14
|
+
*/
|
|
15
|
+
export async function readTransaction({ engineUrl, chainId, contractAddress, functionName, args, authorizationToken }) {
|
|
16
|
+
try {
|
|
17
|
+
if (!engineUrl || !chainId || !contractAddress || !functionName || !authorizationToken) {
|
|
18
|
+
throw new Error("Missing required parameters for readTransaction");
|
|
19
|
+
}
|
|
20
|
+
const readUrl = `${engineUrl}/contract/${chainId}/${contractAddress}/read`;
|
|
21
|
+
const headers = {
|
|
22
|
+
"Content-Type": "application/json",
|
|
23
|
+
Authorization: `Bearer ${authorizationToken}`,
|
|
24
|
+
};
|
|
25
|
+
const url = `${readUrl}?functionName=${encodeURIComponent(functionName)}&args=${encodeURIComponent(args)}`;
|
|
26
|
+
const response = await fetch(url, { method: "GET", headers });
|
|
27
|
+
if (!response.ok) {
|
|
28
|
+
const errorData = await response.json();
|
|
29
|
+
throw new Error(`Transaction read failed: ${JSON.stringify(errorData)}`);
|
|
30
|
+
}
|
|
31
|
+
const { result } = await response.json();
|
|
32
|
+
return result;
|
|
33
|
+
} catch (err) {
|
|
34
|
+
throw new Error(`Failed to read transaction: ${err.message}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
package/src/transfer.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transfer funds through a Thirdweb Engine backend.
|
|
3
|
+
*
|
|
4
|
+
* @param {Object} config
|
|
5
|
+
* @param {string} config.recipient - Recipient address
|
|
6
|
+
* @param {number|string} config.chainId - Chain ID (eg. 80002 for Amoy)
|
|
7
|
+
* @param {number|string} config.amount - Amount to transfer
|
|
8
|
+
* @param {string} config.engineUrl - Base URL of your Engine backend (eg. 'https://your-engine.com')
|
|
9
|
+
* @param {string} config.authorizationToken - Bearer token for Engine access
|
|
10
|
+
* @param {string} config.backendWalletAddress - Address of backend signer wallet
|
|
11
|
+
* @returns {Promise<object>} - Result of the transfer transaction
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
// #Review Pending
|
|
15
|
+
export const transferFunds = async ({ recipient, chainId, amount, engineUrl, authorizationToken, backendWalletAddress }) => {
|
|
16
|
+
try {
|
|
17
|
+
if ((amount) > 1) {
|
|
18
|
+
throw new Error(`More than 1 Eth not supported!`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const url = `${engineUrl}/backend-wallet/${chainId}/transfer`
|
|
22
|
+
const headers = {
|
|
23
|
+
"Content-Type": "application/json",
|
|
24
|
+
Authorization: `Bearer ${authorizationToken}`,
|
|
25
|
+
"x-backend-wallet-address": backendWalletAddress,
|
|
26
|
+
"x-idempotency-key": `idemp-${Date.now()}`,
|
|
27
|
+
};
|
|
28
|
+
const body = JSON.stringify({
|
|
29
|
+
to: recipient,
|
|
30
|
+
currencyAddress: "0x0000000000000000000000000000000000000000",
|
|
31
|
+
amount: amount
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const response = await fetch(url, { method: "POST", headers, body });
|
|
35
|
+
if (!response.ok) {
|
|
36
|
+
const errorData = await response.json();
|
|
37
|
+
throw new Error(`${JSON.stringify(errorData)}`);
|
|
38
|
+
}
|
|
39
|
+
const { result } = await response.json();
|
|
40
|
+
return result
|
|
41
|
+
} catch (error) {
|
|
42
|
+
throw new Error(`Failed to transfer: ${error}`);
|
|
43
|
+
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// src/writeTransaction.js
|
|
2
|
+
import { Agent, setGlobalDispatcher } from "undici";
|
|
3
|
+
|
|
4
|
+
const agent = new Agent({
|
|
5
|
+
connect: {
|
|
6
|
+
rejectUnauthorized: false,
|
|
7
|
+
},
|
|
8
|
+
});
|
|
9
|
+
setGlobalDispatcher(agent);
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Write a transaction through a Thirdweb Engine backend.
|
|
13
|
+
*
|
|
14
|
+
* @param {Object} config
|
|
15
|
+
* @param {string} config.engineUrl - Base URL of your Engine backend (eg. 'https://your-engine.com')
|
|
16
|
+
* @param {number|string} config.chainId - Chain ID (eg. 80002 for Amoy)
|
|
17
|
+
* @param {string} config.contractAddress - Smart contract address
|
|
18
|
+
* @param {string} config.functionName - Full function signature (eg. 'function mint(address,uint256)')
|
|
19
|
+
* @param {Array} config.args - Function arguments
|
|
20
|
+
* @param {string} config.backendWalletAddress - Address of backend signer wallet
|
|
21
|
+
* @param {string} config.authorizationToken - Bearer token for Engine access
|
|
22
|
+
* @returns {Promise<object>} - Result of the write transaction
|
|
23
|
+
*/
|
|
24
|
+
export async function writeTransaction({
|
|
25
|
+
engineUrl,
|
|
26
|
+
chainId,
|
|
27
|
+
contractAddress,
|
|
28
|
+
functionName,
|
|
29
|
+
args,
|
|
30
|
+
backendWalletAddress,
|
|
31
|
+
authorizationToken,
|
|
32
|
+
}) {
|
|
33
|
+
try {
|
|
34
|
+
if (!engineUrl || !chainId || !contractAddress || !functionName || !args || !backendWalletAddress || !authorizationToken) {
|
|
35
|
+
throw new Error("Missing required parameters for writeTransaction");
|
|
36
|
+
}
|
|
37
|
+
const writeUrl = `${engineUrl}/contract/${chainId}/${contractAddress}/write`;
|
|
38
|
+
const headers = {
|
|
39
|
+
"Content-Type": "application/json",
|
|
40
|
+
Authorization: `Bearer ${authorizationToken}`,
|
|
41
|
+
"x-backend-wallet-address": backendWalletAddress,
|
|
42
|
+
"x-idempotency-key": `idemp-${Date.now()}`,
|
|
43
|
+
};
|
|
44
|
+
const body = JSON.stringify({ functionName, args });
|
|
45
|
+
const response = await fetch(writeUrl, { method: "POST", headers, body });
|
|
46
|
+
if (!response.ok) {
|
|
47
|
+
const errorData = await response.json();
|
|
48
|
+
throw new Error(`Transaction write failed: ${JSON.stringify(errorData)}`);
|
|
49
|
+
}
|
|
50
|
+
const { result } = await response.json();
|
|
51
|
+
return result;
|
|
52
|
+
} catch (err) {
|
|
53
|
+
throw new Error(`Failed to write transaction: ${err.message}`);
|
|
54
|
+
}
|
|
55
|
+
}
|