Skip to content

Commit

Permalink
WALLET-196: Add Send Crypto action
Browse files Browse the repository at this point in the history
  • Loading branch information
devsMetapro committed Oct 16, 2024
1 parent b1d07f0 commit d25e740
Show file tree
Hide file tree
Showing 9 changed files with 220 additions and 20 deletions.
90 changes: 76 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
# Metapro Plugin for Construct 3

Metapro is a powerful plugin for Construct 3 that enables developers to integrate blockchain functionality, user management, and leaderboard systems into their games and applications.

## Table of Contents

- [Features](#features)
- [Installation](#installation)
- [Getting Started](#getting-started)
- [Properties](#properties)
- [Supported API Calls](#supported-api-calls)
- [Actions](#actions)
- [Conditions](#conditions)
- [Expressions](#expressions)
- [Usage](#usage)
- [Troubleshooting](#troubleshooting)
- [Contributing](#contributing)
- [Author](#author)
- [License](#license)

## Features

- **Account Management**:
Expand Down Expand Up @@ -44,24 +62,38 @@

## Installation

The Metapro Plugin can be added through either the Construct store or as a dev addon in the editor.
The Metapro Plugin can be added to your Construct 3 project in two ways:

1. Through the Construct store:

- Open Construct 3
- Go to Menu -> View -> Addons
- Search for "Metapro"
- Click "Add" to install the plugin

2. As a dev addon in the editor:
- Download the Metapro Plugin addon from the `builds` directory in this repository
- In Construct 3, go to Menu -> View -> Addons
- Click "Install new addon" and select the downloaded file

## Downloads
## Getting Started

The Metapro Plugin addons are available in builds directory.
1. After installation, add the Metapro object to your project layout.
2. Configure the plugin properties (see [Properties](#properties) section).
3. Use the provided actions, conditions, and expressions in your event sheets to interact with Metapro functionality.

### Properties
## Properties

| **Property Name** | **Description** |
| --------------------------- | -------------------------------------------------------------- |
| **Users Service API URL** | The URL of the Users Service API used by the Metapro system. |
| **Project ID** | The unique identifier for your project in Metapro. |
| **Leaderboard ID** | The ID of the leaderboard used in the Metapro system. |
| **Referral Leaderboard ID** | The ID of the referral leaderboard used in the Metapro system. |
| **Leaderboard API Key** | The API key required to access the leaderboard. |
| **Leaderboard API URL** | The URL of the Leaderboard API used in the Metapro system. |
| **Referral API URL** | The URL of the Referral API used in the Metapro system. |
| **Platform ID** | The ID of the platform associated with the project. |
| Property Name | Description |
| ----------------------- | -------------------------------------------------------------- |
| Users Service API URL | The URL of the Users Service API used by the Metapro system. |
| Project ID | The unique identifier for your project in Metapro. |
| Leaderboard ID | The ID of the leaderboard used in the Metapro system. |
| Referral Leaderboard ID | The ID of the referral leaderboard used in the Metapro system. |
| Leaderboard API Key | The API key required to access the leaderboard. |
| Leaderboard API URL | The URL of the Leaderboard API used in the Metapro system. |
| Referral API URL | The URL of the Referral API used in the Metapro system. |
| Platform ID | The ID of the platform associated with the project. |

## Supported API Calls

Expand Down Expand Up @@ -90,6 +122,7 @@ The Metapro Plugin addons are available in builds directory.
| **Request User NFTs** | Retrieves the NFTs owned by a user based on a provided query as stringified JSON. | **Query**: A stringified JSON representation of the query params. Example format: `{'tokens': [{'contractAddress': '0x3203c9e46ca618c8c1ce5dc67e7e9d75f5da2377', 'tokenId': 123}], 'sort': {'sortKey': 'token.creationBlock', 'sortDirection': 'desc'}}`. |
| **Set Transaction Status** | Sets the internal transaction status variable to the provided status value. | **Status**: The status value to set for the internal transaction status variable. |
| **Multiple Read Contract** | Reads data from a smart contract at the specified address using the provided ABI, multiple function names, input data, and RPC URL. | **Contract Address**: The address of the smart contract.<br>**ABI**: A JSON stringified representation of the ABI.<br>**Function Names**: A JSON stringified representation of the array of function names to be called.<br>**Inputs Data**: A JSON stringified representation of the array of input data for each function call.<br>**RPC URL**: The URL of the Remote Procedure Call (RPC) endpoint for interacting with the blockchain. |
| **Send Crypto** | Send a transaction to transfer cryptocurrency or tokens from one address to another, based on the specified token contract. | **Token Contract Address**: The address of the token contract to send cryptocurrency from. If empty, sends native cryptocurrency (e.g., ETH, BNB).<br>**Amount**: The amount of cryptocurrency to send, provided in proper unit. Unit converter: https://etherscan.io/unitconverter.<br>**Receiver Address**: The address of the receiver.<br>**Chain ID**: The target blockchain network ID (e.g., 1 for Ethereum Mainnet, 56 for Binance Smart Chain). |

### Conditions

Expand Down Expand Up @@ -149,6 +182,35 @@ The Metapro Plugin addons are available in builds directory.
| **Get NFT API URL** | Retrieve the URL for accessing the NFT API. |
| **Get Transaction Status** | Retrieves the current transaction status from the internal status variable. |

## Usage

Here's a simple example of how to use the Metapro plugin to request a user's account:

1. Add the Metapro object to your layout.
2. In your event sheet, add a new event.
3. For the event's condition, you might use a button click or a system start.
4. Add an action to this event: Metapro -> Request Account.
5. Add another event with the condition: Metapro -> On Account Received.
6. In this event, you can add actions to use the received account, such as displaying it or using it for further authentication.

## Troubleshooting

- If you encounter issues with API calls, ensure that all required properties are correctly set in the Metapro object.
- Check the browser console for any error messages related to Metapro.
- Use the "Get Last Error" expression to retrieve detailed error information when an operation fails.

## Contributing

We welcome contributions to improve the Metapro plugin! If you'd like to contribute:

1. Fork the repository
2. Create a new branch for your feature or bug fix
3. Make your changes and commit them with clear, descriptive messages
4. Push your changes to your fork
5. Submit a pull request with a description of your changes

Please ensure your code adheres to the existing style and includes appropriate tests.

## Author

metapro
Expand Down
11 changes: 11 additions & 0 deletions aces.json
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,17 @@
"id": "set-transaction-status",
"scriptName": "SetTransactionStatus",
"params": [{ "id": "status", "type": "string" }]
},
{
"id": "send-crypto",
"scriptName": "SendCrypto",
"isAsync": true,
"params": [
{ "id": "token-address", "type": "string" },
{ "id": "amount", "type": "string" },
{ "id": "receiver", "type": "string" },
{ "id": "chain-id", "type": "number" }
]
}
],
"expressions": [
Expand Down
2 changes: 1 addition & 1 deletion addon.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"type": "plugin",
"name": "Metapro Plugin",
"id": "MetaproPlugin",
"version": "3.11.0.0",
"version": "3.12.0.0",
"author": "metapro",
"website": "https://metapro.one/",
"documentation": "https://metapro.one/",
Expand Down
Binary file added builds/metapro-c3-plugin-v3.12.0.0.c3addon
Binary file not shown.
3 changes: 3 additions & 0 deletions c3runtime/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,7 @@ self.C3.Plugins.MetaproPlugin.Acts = {
SetTransactionStatus(status) {
this._SetTransactionStatus(status);
},
async SendCrypto(token_address, amount, receiver, chain_id) {
await this._SendCrypto(token_address, amount, receiver, chain_id);
},
};
107 changes: 104 additions & 3 deletions c3runtime/instance.js
Original file line number Diff line number Diff line change
Expand Up @@ -1216,7 +1216,7 @@ C3.Plugins.MetaproPlugin.Instance = class MetaproPluginInstance extends (
} catch (error) {
console.log(error);
this._transactionStatus = "error";
this.HandleError(error.message);
this.HandleError(error.data?.message || error.message);
}
}

Expand Down Expand Up @@ -1260,7 +1260,7 @@ C3.Plugins.MetaproPlugin.Instance = class MetaproPluginInstance extends (
this.OnReadContractDataReceived();
} catch (error) {
console.log(error);
this.HandleError("Failed to send transaction: " + error.message);
this.HandleError(error.data?.message || error.message);
}
}

Expand Down Expand Up @@ -1308,7 +1308,7 @@ C3.Plugins.MetaproPlugin.Instance = class MetaproPluginInstance extends (
this.OnMultipleReadContractDataReceived();
} catch (error) {
console.log(error);
this.HandleError("Failed to send transaction: " + error.message);
this.HandleError(error.data?.message || error.message);
}
}

Expand Down Expand Up @@ -1401,6 +1401,107 @@ C3.Plugins.MetaproPlugin.Instance = class MetaproPluginInstance extends (
}
}

async _SendCrypto(token_address, amount, receiver, chain_id) {
const abi = [
{
constant: false,
inputs: [
{ name: "_to", type: "address" },
{ name: "_value", type: "uint256" },
],
name: "transfer",
outputs: [{ name: "", type: "bool" }],
type: "function",
},
{
constant: true,
inputs: [{ name: "_owner", type: "address" }],
name: "balanceOf",
outputs: [{ name: "balance", type: "uint256" }],
type: "function",
},
];

this._transactionStatus = "pending";
try {
if (!this._account) {
throw new Error("Account information is missing or not initialized.");
}

await this.PostToDOMAsync("switch-chain", chain_id);

const amountInBigInt = BigInt(amount);
const currentGasPrice = await web3.eth.getGasPrice();
let transaction;

if (!token_address) {
const tx = {
from: this._account,
to: receiver,
value: amountInBigInt,
};
const estimatedGas = await web3.eth.estimateGas(tx);

transaction = await web3.eth.sendTransaction({
...tx,
gas: estimatedGas,
gasPrice: currentGasPrice,
});
} else {
const contract = new web3.eth.Contract(abi, token_address);
const estimatedGas = await contract.methods
.transfer(receiver, amountInBigInt)
.estimateGas({
from: this._account,
});

transaction = await contract.methods
.transfer(receiver, amountInBigInt)
.send({
from: this._account,
gas: estimatedGas,
gasPrice: currentGasPrice,
});
}

this._lastTransactionHash = transaction.transactionHash;

// Wait for transaction confirmation
const timeout = 120000; // 120sec
const startTime = Date.now();
let receipt = null;

while (receipt === null) {
receipt = await web3.eth.getTransactionReceipt(
transaction.transactionHash
);

if (receipt === null) {
const elapsedTime = Date.now() - startTime;

if (elapsedTime > timeout) {
throw new Error("Transaction confirmation timed out.");
}

// Wait for a short time before polling again to avoid spamming the node
await new Promise((resolve) => setTimeout(resolve, 3000));
}
}

if (!!receipt && this.SerializeData(receipt.status)) {
this._transactionStatus = "success";
} else {
throw new Error("Transaction failed or was reverted.");
}

this.OnTransactionSent();
} catch (error) {
console.log(error);
this._transactionStatus = "error";
this.HandleError(error.data?.message || error.message);
}
}

// Conditions
OnAccountReceived() {
this._triggerAccountReceived = true;
Expand Down
23 changes: 23 additions & 0 deletions lang/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,29 @@
"desc": "The status value to set for the internal transaction status variable."
}
}
},
"send-crypto": {
"list-name": "Send Crypto",
"display-text": "Send {1} amount of tokens from contract address {0} to receiver {2} on chain ID {3}.",
"description": "Sends cryptocurrency from the specified contract address to a receiver, including the amount and target chain ID.",
"params": {
"token-address": {
"name": "Token Contract Address",
"desc": "Address of the token contract to send cryptocurrency from."
},
"amount": {
"name": "Amount",
"desc": "The amount of cryptocurrency to send."
},
"receiver": {
"name": "Receiver Address",
"desc": "The address of the receiver to whom the cryptocurrency will be sent."
},
"chain-id": {
"name": "Chain ID",
"desc": "The target blockchain network ID, provided as a decimal."
}
}
}
},
"expressions": {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "plugin",
"version": "3.11.0.0",
"version": "3.12.0.0",
"description": "",
"main": "plugin.js",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const SDK = self.SDK;
const PLUGIN_ID = "MetaproPlugin";
////////////////////////////////////////////

const PLUGIN_VERSION = "3.11.0.0";
const PLUGIN_VERSION = "3.12.0.0";
const PLUGIN_CATEGORY = "platform-specific";

const PLUGIN_CLASS = (SDK.Plugins.MetaproPlugin = class MyCustomPlugin extends (
Expand Down

0 comments on commit d25e740

Please sign in to comment.