Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add more filters for transfer tokens for the evm action - MEED-5850 - Meeds-io/MIPs#118 #27

Merged
merged 1 commit into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public String getEventType() {
}

public List<String> getTriggers() {
return List.of(Utils.HOLD_TOKEN_EVENT);
return List.of(Utils.TRANSFER_TOKEN_EVENT);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
*/
package io.meeds.evm.gamification.scheduling.task;

import java.math.BigInteger;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import io.meeds.common.ContainerTransactional;
import io.meeds.evm.gamification.model.TokenTransferEvent;
Expand Down Expand Up @@ -81,10 +83,14 @@ public synchronized void listenTokenTransfer() {
.toList();
if (CollectionUtils.isNotEmpty(filteredRules)) {
filteredRules.forEach(rule -> {
BigInteger minAmount;
String recipientAddress;
BigInteger base = new BigInteger("10");
String blockchainNetwork = rule.getEvent().getProperties().get(Utils.BLOCKCHAIN_NETWORK);
String contractAddress = rule.getEvent().getProperties().get(Utils.CONTRACT_ADDRESS);
String tokenName = rule.getEvent().getProperties().get(Utils.NAME);
String tokenSymbol = rule.getEvent().getProperties().get(Utils.SYMBOL);
Integer tokenDecimals = Integer.parseInt(rule.getEvent().getProperties().get(Utils.DECIMALS));
long lastBlock = blockchainService.getLastBlock(blockchainNetwork);
long lastCheckedBlock = getLastCheckedBlock(contractAddress);
if (lastCheckedBlock == 0) {
Expand All @@ -97,24 +103,36 @@ public synchronized void listenTokenTransfer() {
lastBlock,
contractAddress,
blockchainNetwork);
if (!CollectionUtils.isEmpty(events)) {
events.forEach(event -> {
try {
EvmTrigger evmTrigger = new EvmTrigger();
evmTrigger.setTrigger(Utils.HOLD_TOKEN_EVENT);
evmTrigger.setType(Utils.CONNECTOR_NAME);
evmTrigger.setWalletAddress(event.getTo());
evmTrigger.setTransactionHash(event.getTransactionHash());
evmTrigger.setContractAddress(contractAddress);
evmTrigger.setBlockchainNetwork(blockchainNetwork);
evmTrigger.setTokenName(tokenName);
evmTrigger.setTokenSymbol(tokenSymbol);
evmTriggerService.handleTriggerAsync(evmTrigger);
} catch (Exception e) {
LOG.warn("Error broadcasting event '" + event, e);
}
});
}
if(!CollectionUtils.isEmpty(events) && StringUtils.isNotBlank(rule.getEvent().getProperties().get(Utils.MIN_AMOUNT))) {
minAmount = base.pow(tokenDecimals).multiply(new BigInteger(rule.getEvent().getProperties().get(Utils.MIN_AMOUNT)));
events = events.stream()
.filter(event -> event.getAmount().compareTo(minAmount) > 0)
.collect(Collectors.toSet());
}
if(!CollectionUtils.isEmpty(events) && StringUtils.isNotBlank(rule.getEvent().getProperties().get(Utils.RECIPIENT_ADDRESS))) {
recipientAddress = rule.getEvent().getProperties().get(Utils.RECIPIENT_ADDRESS);
events = events.stream()
.filter(event -> recipientAddress.toUpperCase().equals(event.getTo().toUpperCase()))
.collect(Collectors.toSet());
}
if (!CollectionUtils.isEmpty(events)) {
events.forEach(event -> {
try {
EvmTrigger evmTrigger = new EvmTrigger();
evmTrigger.setTrigger(Utils.TRANSFER_TOKEN_EVENT);
evmTrigger.setType(Utils.CONNECTOR_NAME);
evmTrigger.setWalletAddress(event.getTo());
evmTrigger.setTransactionHash(event.getTransactionHash());
evmTrigger.setContractAddress(contractAddress);
evmTrigger.setBlockchainNetwork(blockchainNetwork);
evmTrigger.setTokenName(tokenName);
evmTrigger.setTokenSymbol(tokenSymbol);
evmTriggerService.handleTriggerAsync(evmTrigger);
} catch (Exception e) {
LOG.warn("Error broadcasting event '" + event, e);
}
});
}
saveLastCheckedBlock(lastBlock, contractAddress);
LOG.info("End listening erc20 token transfers");
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public class Utils {

public static final String CONNECTOR_NAME = "evm";

public static final String HOLD_TOKEN_EVENT = "holdToken";
public static final String TRANSFER_TOKEN_EVENT = "transferToken";

public static final String WALLET_ADDRESS = "walletAddress";

Expand All @@ -34,6 +34,12 @@ public class Utils {

public static final String SYMBOL = "tokenSymbol";

public static final String DECIMALS = "tokenDecimals";

public static final String MIN_AMOUNT = "minAmount";

public static final String RECIPIENT_ADDRESS = "recipientAddress";

public static final String TRANSACTION_HASH = "transactionHash";

private Utils() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,21 @@
<external-component-plugins>
<target-component>io.meeds.gamification.service.EventRegistry</target-component>
<component-plugin>
<name>HoldToken</name>
<name>TransferToken</name>
<set-method>addPlugin</set-method>
<type>io.meeds.gamification.plugin.EventConfigPlugin</type>
<init-params>
<object-param>
<name>event</name>
<object type="io.meeds.gamification.model.EventDTO">
<field name="title">
<string>holdToken</string>
<string>transferToken</string>
</field>
<field name="type">
<string>evm</string>
</field>
<field name="trigger">
<string>holdToken</string>
<string>transferToken</string>
</field>
</object>
</object-param>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
gamification.event.title.holdToken=EVM: Hold Token
gamification.event.title.transferToken=Transfer a Token
gamification.event.form.networks=Blockchain
gamification.event.form.contractAddress=Smart Contract
gamification.event.form.contractAddress.placeholder=Enter the contract address
gamification.event.form.contractAddress=Token Address
gamification.event.form.contractAddress.placeholder=Enter the token address
gamification.event.form.contractAddress.tooltip=Verif addresss on {0}
gamification.event.form.recipientAddress=Recipient
gamification.event.form.recipientAddress.placeholder=Enter the recipient address
gamification.event.form.minAmount=Minimum amount
gamification.event.form.minAmount.placeholder=Enter the minimum amount
gamification.event.detail.invalidContractAddress.error=Please enter a valid contract address
gamification.event.detail.invalidERC20ContractAddress.error=No smart contract found at this address
gamification.event.detail.verifyToken.message=Click the Checkmark icon to verify this address
gamification.event.detail.display.holdToken=Contract address token
gamification.event.detail.display.transferToken=Contract address token

gamification.admin.evm.label.description=Listen to any smart contract transaction on Ethereum Virtual Machine blockchains
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,18 @@
{{ contractAddress }}
</div>
</a>
<div class="subtitle-1 font-weight-bold mb-2 mt-4">
{{ $t('gamification.event.form.recipientAddress') }}
</div>
<div class="text-font-size align-self-start">
{{ recipientAddress }}
</div>
<div class="subtitle-1 font-weight-bold mb-2 mt-4">
{{ $t('gamification.event.form.minAmount') }}
</div>
<div class="text-font-size align-self-start">
{{ minAmount }}
</div>
</div>
</template>
<script>
Expand All @@ -50,8 +62,24 @@ export default {
titleTriggerProps() {
return this.$t(`gamification.event.detail.display.${this.trigger}`);
},
blockchainNetwork() {
return this.properties?.blockchainNetwork;
},
explorerLink() {
return `https://polygonscan.com/address/${this.contractAddress}`;
const url = this.blockchainNetwork?.substring(this.blockchainNetwork.indexOf('//') + 2, this.blockchainNetwork.indexOf('.g.alchemy.com'));
switch (url) {
case 'polygon-mainnet': return `https://polygonscan.com/address/${this.contractAddress}`;
case 'polygon-mumbai': return `https://mumbai.polygonscan.com/address/${this.contractAddress}`;
case 'eth-mainnet': return `https://etherscan.io/address/${this.contractAddress}`;
case 'eth-sepolia': return `https://sepolia.etherscan.io/address/${this.contractAddress}`;
}
return '';
},
minAmount() {
return this.properties?.minAmount;
},
recipientAddress() {
return this.properties?.recipientAddress;
}
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,32 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
<span v-if="isInValidAddressFormat" class="error--text">{{ $t('gamification.event.detail.invalidContractAddress.error') }}</span>
<span v-else-if="isInvalidERC20Address" class="error--text">{{ $t('gamification.event.detail.invalidERC20ContractAddress.error') }}</span>
<span v-else-if="emptyERC20Token">{{ $t('gamification.event.detail.verifyToken.message') }}</span>
<div v-if="erc20Token">
<v-card-text class="px-0 dark-grey-color font-weight-bold">
{{ $t('gamification.event.form.recipientAddress') }}
</v-card-text>
<v-text-field
ref="recipientAddress"
v-model="recipientAddress"
:placeholder="$t('gamification.event.form.recipientAddress.placeholder')"
class="pa-0"
type="text"
outlined
dense
@change="selectedRecipient" />
<v-card-text class="px-0 dark-grey-color font-weight-bold">
{{ $t('gamification.event.form.minAmount') }}
</v-card-text>
<v-text-field
ref="minAmount"
v-model="minAmount"
:placeholder="$t('gamification.event.form.minAmount.placeholder')"
class="pa-0"
type="text"
outlined
dense
@change="selectedAmount" />
</div>
</template>
</div>
</template>
Expand Down Expand Up @@ -203,21 +229,12 @@ export default {
return this.$evmConnectorService.getTokenDetailsByAddress({contractAddress: this.contractAddress, blockchainNetwork: this.selected?.providerUrl})
.then(token => {
this.erc20Token = token;
this.eventProperties = {
contractAddress: this.contractAddress,
blockchainNetwork: this.selected?.providerUrl,
tokenName: token.name,
tokenSymbol: token.symbol
};
})
.then(() => this.loading = false )
.catch(() => {
this.isValidERC20Address = false;
this.erc20Token = null;
this.loading = false;
})
.finally(() => {
this.submitEventProperties();
});
},
resetERC20Token() {
Expand All @@ -237,13 +254,61 @@ export default {
name: this.properties?.tokenName,
symbol: this.properties?.tokenSymbol
};
this.minAmount = this.properties?.minAmount;
this.recipientAddress = this.properties?.recipientAddress;
this.readOnly = true;
this.isValidAddress = true;
}
document.dispatchEvent(new CustomEvent('event-form-unfilled'));
this.loadingNetworks = false;
});
},
selectedAmount(minAmount) {
if (this.recipientAddress) {
this.eventProperties = {
contractAddress: this.contractAddress,
blockchainNetwork: this.selected?.providerUrl,
tokenName: this.erc20Token.name,
tokenSymbol: this.erc20Token.symbol,
tokenDecimals: this.erc20Token.decimals,
recipientAddress: this.recipientAddress,
minAmount: minAmount
};
} else {
this.eventProperties = {
contractAddress: this.contractAddress,
blockchainNetwork: this.selected?.providerUrl,
tokenName: this.erc20Token.name,
tokenSymbol: this.erc20Token.symbol,
tokenDecimals: this.erc20Token.decimals,
minAmount: minAmount
};
}
document.dispatchEvent(new CustomEvent('event-form-filled', {detail: this.eventProperties}));
},
selectedRecipient(recipientAddress) {
if (this.minAmount) {
this.eventProperties = {
contractAddress: this.contractAddress,
blockchainNetwork: this.selected?.providerUrl,
tokenName: this.erc20Token.name,
tokenSymbol: this.erc20Token.symbol,
tokenDecimals: this.erc20Token.decimals,
recipientAddress: recipientAddress,
minAmount: this.minAmount
};
} else {
this.eventProperties = {
contractAddress: this.contractAddress,
blockchainNetwork: this.selected?.providerUrl,
tokenName: this.erc20Token.name,
tokenSymbol: this.erc20Token.symbol,
tokenDecimals: this.erc20Token.decimals,
recipientAddress: recipientAddress
};
}
document.dispatchEvent(new CustomEvent('event-form-filled', {detail: this.eventProperties}));
}
},
}
};
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export function init() {
name: 'evm',
vueComponent: Vue.options.components['evm-connector-event'],
isEnabled: (params) => [
'holdToken',
'transferToken',
].includes(params?.trigger),
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export function init() {
rank: 60,
image: '/gamification-evm/images/EVM.png',
match: (actionLabel) => [
'holdToken',
'transferToken',
].includes(actionLabel),
getLink: realization => {
if (realization.objectType === 'evm' && realization.objectId !== '') {
Expand Down